diff --git a/CMakeLists.txt b/CMakeLists.txt index 381f0cd7b7..6acfcf2c8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,7 +114,11 @@ endif() #=============================================================================== add_library(pugixml vendor/pugixml/pugixml.cpp) -target_include_directories(pugixml PUBLIC vendor/pugixml/) +target_include_directories(pugixml + PUBLIC + $ + $ +) #=============================================================================== # xtensor header-only library @@ -134,7 +138,11 @@ target_link_libraries(xtensor INTERFACE xtl) #=============================================================================== add_library(gsl INTERFACE) -target_include_directories(gsl INTERFACE vendor/gsl/include) +target_include_directories(gsl + INTERFACE + $ + $ +) # Make sure contract violations throw exceptions target_compile_definitions(gsl INTERFACE GSL_THROW_ON_CONTRACT_VIOLATION) @@ -172,7 +180,11 @@ endif() #=============================================================================== add_library(faddeeva STATIC vendor/faddeeva/Faddeeva.cc) -target_include_directories(faddeeva PUBLIC vendor/faddeeva/) +target_include_directories(faddeeva + PUBLIC + $ + $ +) target_compile_options(faddeeva PRIVATE ${cxxflags}) #=============================================================================== @@ -283,7 +295,11 @@ set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) target_include_directories(libopenmc - PUBLIC include ${HDF5_INCLUDE_DIRS}) + PUBLIC + $ + $ + ${HDF5_INCLUDE_DIRS} +) # Set compile flags target_compile_options(libopenmc PRIVATE ${cxxflags}) @@ -336,19 +352,35 @@ set_target_properties( add_custom_command(TARGET libopenmc POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $ - ${CMAKE_CURRENT_SOURCE_DIR}/openmc/capi/$ + ${CMAKE_CURRENT_SOURCE_DIR}/openmc/lib/$ COMMENT "Copying libopenmc to Python module directory") #=============================================================================== # Install executable, scripts, manpage, license #=============================================================================== -install(TARGETS openmc libopenmc - RUNTIME DESTINATION bin - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib - ) -install(DIRECTORY src/relaxng DESTINATION share/openmc) -install(FILES man/man1/openmc.1 DESTINATION share/man/man1) -install(FILES LICENSE DESTINATION "share/doc/openmc" RENAME copyright) -install(DIRECTORY include/ DESTINATION include) +include(GNUInstallDirs) +set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/OpenMC) +install(TARGETS openmc libopenmc pugixml faddeeva gsl + EXPORT openmc-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) +install(EXPORT openmc-targets + FILE OpenMCTargets.cmake + NAMESPACE OpenMC:: + DESTINATION ${INSTALL_CONFIGDIR}) + +install(DIRECTORY src/relaxng DESTINATION ${CMAKE_INSTALL_DATADIR}/openmc) +install(FILES cmake/OpenMCConfig.cmake DESTINATION ${INSTALL_CONFIGDIR}) +install(FILES man/man1/openmc.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +install(FILES LICENSE DESTINATION "${CMAKE_INSTALL_DOCDIR}" RENAME copyright) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +# Copy headers for vendored dependencies (note that xtensor/xtl are handled +# separately since they are managed by CMake) +install(DIRECTORY vendor/pugixml DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING PATTERN "*.hpp") +install(DIRECTORY vendor/gsl DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY vendor/faddeeva DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..a061b7b6ea --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,48 @@ +# Data interface +openmc/data/ @paulromano + +# Python bindings to C/C++ API +openmc/lib/ @paulromano + +# Depletion +openmc/deplete/ @drewejohnson +tests/regression_tests/deplete/ @drewejohnson +tests/unit_tests/test_deplete_*.py @drewejohnson + +# MG-related functionality +openmc/mgxs_library.py @nelsonag +src/mgxs.cpp @nelsonag +src/mgxs_interface.cpp @nelsonag +src/physics_mg.cpp @nelsonag +src/scattdata.cpp @nelsonag +src/xsdata.cpp @nelsonag + +# CMFD +openmc/cmfd.py @shikhar413 +src/cmfd_solver.cpp @shikhar413 + +# DAGMC +src/dagmc.cpp @pshriwise +tests/regression_tests/dagmc/ @pshriwise +tests/unit_tests/dagmc/ @pshriwise + +# Photon transport +openmc/data/BREMX.DAT @amandalund +openmc/data/compton_profiles.h5 @amandalund +openmc/data/photon.py @amandalund +src/photon.cpp @amandalund +src/bremsstrahlung.cpp @amandalund +tests/regression_tests/photon_production/ @amandalund +tests/regression_tests/photon_source/ @amandalund + +# RCP and TRISOs +openmc/model/triso.py @amandalund +tests/regression_tests/triso/ @amandalund +tests/unit_tests/test_model_triso.py @amandalund + +# Geometry plotting +src/plot.cpp @pshriwise +openmc/lib/plot.py @pshriwise + +# Resonance covariance +openmc/data/resonance_covariance.py @icmeyer diff --git a/LICENSE b/LICENSE index cfa34033c2..328f3ee469 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2018 Massachusetts Institute of Technology and OpenMC contributors +Copyright (c) 2011-2019 Massachusetts Institute of Technology and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/cmake/OpenMCConfig.cmake b/cmake/OpenMCConfig.cmake new file mode 100644 index 0000000000..0bc86fa71c --- /dev/null +++ b/cmake/OpenMCConfig.cmake @@ -0,0 +1,8 @@ +get_filename_component(OpenMC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) + +find_package(xtl REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtl) +find_package(xtensor REQUIRED HINTS ${OpenMC_CMAKE_DIR}/../xtensor) + +if(NOT TARGET OpenMC::libopenmc) + include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake") +endif() diff --git a/docs/requirements-rtd.txt b/docs/requirements-rtd.txt index 15345247eb..7801c670f8 100644 --- a/docs/requirements-rtd.txt +++ b/docs/requirements-rtd.txt @@ -1,3 +1,4 @@ sphinx-numfig jupyter sphinxcontrib-katex +sphinxcontrib-svg2pdfconverter diff --git a/docs/source/_images/cosine-dist.png b/docs/source/_images/cosine-dist.png new file mode 100644 index 0000000000..f7c165dcfd Binary files /dev/null and b/docs/source/_images/cosine-dist.png differ diff --git a/docs/source/_templates/mycallable.rst b/docs/source/_templates/mycallable.rst new file mode 100644 index 0000000000..85fdd34c35 --- /dev/null +++ b/docs/source/_templates/mycallable.rst @@ -0,0 +1,9 @@ +{{ fullname }} +{{ underline }} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :special-members: __call__ + diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index 90bef1909c..0ce1c234d1 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -1,17 +1,17 @@ .. _capi: -===== -C API -===== +========= +C/C++ API +========= The libopenmc shared library that is built when installing OpenMC exports a number of C interoperable functions and global variables that can be used for -in-memory coupling. While it is possible to directly use the C API as documented -here for coupling, most advanced users will find it easier to work with the -Python bindings in the :py:mod:`openmc.capi` module. +in-memory coupling. While it is possible to directly use the C/C++ API as +documented here for coupling, most advanced users will find it easier to work +with the Python bindings in the :py:mod:`openmc.lib` module. -.. warning:: The C API is still experimental and may undergo substantial changes - in future releases. +.. warning:: The C/C++ API is still experimental and may undergo substantial + changes in future releases. ---------------- Type Definitions diff --git a/docs/source/conf.py b/docs/source/conf.py index a24f9b398a..68caff888e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -27,7 +27,7 @@ MOCK_MODULES = [ 'scipy.interpolate', 'scipy.integrate', 'scipy.optimize', 'scipy.special', 'scipy.stats', 'scipy.spatial', 'h5py', 'pandas', 'uncertainties', 'matplotlib', 'matplotlib.pyplot', 'openmoc', - 'openmc.data.reconstruct' + 'openmc.data.reconstruct', 'openmc.checkvalue' ] sys.modules.update((mod_name, MagicMock()) for mod_name in MOCK_MODULES) @@ -56,7 +56,7 @@ extensions = ['sphinx.ext.autodoc', 'sphinx_numfig', 'notebook_sphinxext'] if not on_rtd: - extensions.append('sphinx.ext.imgconverter') + extensions.append('sphinxcontrib.rsvgconverter') # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -79,9 +79,9 @@ copyright = '2011-2019, Massachusetts Institute of Technology and OpenMC contrib # built documents. # # The short X.Y version. -version = "0.11" +version = "0.12" # The full version, including alpha/beta/rc tags. -release = "0.11.0-dev" +release = "0.12.0-dev" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/source/devguide/docbuild.rst b/docs/source/devguide/docbuild.rst index 5cbbb65bb8..38ef628df5 100644 --- a/docs/source/devguide/docbuild.rst +++ b/docs/source/devguide/docbuild.rst @@ -5,27 +5,28 @@ Building Sphinx Documentation ============================= In order to build the documentation in the ``docs`` directory, you will need to -have the `Sphinx `_ third-party Python +have the `Sphinx `_ third-party Python package. The easiest way to install Sphinx is via pip: .. code-block:: sh - sudo pip install sphinx + pip install sphinx -Additionally, you will also need a Sphinx extension for numbering figures. The -`Numfig `_ package can be installed +Additionally, you will need several Sphinx extensions that can be installed directly with pip: .. code-block:: sh - sudo pip install sphinx-numfig + pip install sphinx-numfig + pip install sphinxcontrib-katex + pip install sphinxcontrib-svg2pdfconverter ----------------------------------- Building Documentation as a Webpage ----------------------------------- To build the documentation as a webpage (what appears at -http://openmc.readthedocs.io), simply go to the ``docs`` directory and run: +https://docs.openmc.org), simply go to the ``docs`` directory and run: .. code-block:: sh diff --git a/docs/source/devguide/docker.rst b/docs/source/devguide/docker.rst index 12c095c455..0b21911686 100644 --- a/docs/source/devguide/docker.rst +++ b/docs/source/devguide/docker.rst @@ -19,7 +19,7 @@ build a Docker image with OpenMC installed. The image includes OpenMC with MPICH and parallel HDF5 in the ``/opt/openmc`` directory, and `Miniconda3 `_ with all of the Python pre-requisites (NumPy, SciPy, Pandas, etc.) installed. The -`NJOY2016 `_ codebase is installed in +`NJOY2016 `_ codebase is installed in ``/opt/NJOY2016`` to support full functionality and testing of the ``openmc.data`` Python module. The publicly available nuclear data libraries necessary to run OpenMC's test suite -- including NNDC and WMP cross sections @@ -54,4 +54,3 @@ Docker container where you have access to use OpenMC. .. _Docker container: https://www.docker.com/resources/what-container .. _options: https://docs.docker.com/engine/reference/commandline/run/ .. _mounting volumes: https://docs.docker.com/storage/volumes/ - diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index cbf665cc8e..562b75e897 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -207,8 +207,8 @@ Documentation ------------- Classes, structs, and functions are to be annotated for the `Doxygen -`_ documentation generation tool. Use the -``\`` form of Doxygen commands, e.g., ``\brief`` instead of ``@brief``. +`_ documentation generation tool. Use the ``\`` form of +Doxygen commands, e.g., ``\brief`` instead of ``@brief``. ------ Python @@ -231,7 +231,7 @@ represent a filesystem path should work with both strings and Path_ objects. .. _C++ Core Guidelines: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines .. _PEP8: https://www.python.org/dev/peps/pep-0008/ .. _numpydoc: https://numpydoc.readthedocs.io/en/latest/format.html -.. _numpy: http://www.numpy.org/ +.. _numpy: https://numpy.org/ .. _scipy: https://www.scipy.org/ .. _matplotlib: https://matplotlib.org/ .. _pandas: https://pandas.pydata.org/ diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 31dc088428..037b8b68af 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -124,7 +124,7 @@ can interfere with virtual environments. .. _GitHub: https://github.com/ .. _git flow: http://nvie.com/git-model .. _valgrind: http://valgrind.org/ -.. _style guide: http://openmc.readthedocs.io/en/latest/devguide/styleguide.html +.. _style guide: https://docs.openmc.org/en/latest/devguide/styleguide.html .. _pull request: https://help.github.com/articles/using-pull-requests .. _openmc-dev/openmc: https://github.com/openmc-dev/openmc .. _paid plan: https://github.com/plans diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index 1633f9950b..529f9427ba 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -24,6 +24,7 @@ General Usage nuclear-data nuclear-data-resonance-covariance cad-geom + pincell-depletion -------- Geometry diff --git a/docs/source/examples/pincell-depletion.rst b/docs/source/examples/pincell-depletion.rst new file mode 100644 index 0000000000..348f3c6b95 --- /dev/null +++ b/docs/source/examples/pincell-depletion.rst @@ -0,0 +1,14 @@ +.. _notebook_depletion: + +================= +Pincell Depletion +================= + + +.. only:: html + + .. notebook:: ../../../examples/jupyter/pincell_depletion.ipynb + +.. only:: latex + + IPython notebooks must be viewed in the online HTML documentation. diff --git a/docs/source/index.rst b/docs/source/index.rst index 2ce5072d01..198a9429ee 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -2,12 +2,14 @@ The OpenMC Monte Carlo Code =========================== -OpenMC is a Monte Carlo particle transport simulation code focused on neutron -criticality calculations. It is capable of simulating 3D models based on -constructive solid geometry with second-order surfaces. OpenMC supports either -continuous-energy or multi-group transport. The continuous-energy particle +OpenMC is a community-developed Monte Carlo neutron and photon transport +simulation code. It is capable of performing fixed source, k-eigenvalue, and +subcritical multiplication calculations on models built using either a +constructive solid geometry or CAD representation. OpenMC supports both +continuous-energy and multigroup transport. The continuous-energy particle interaction data is based on a native HDF5 format that can be generated from ACE -files used by the MCNP and Serpent Monte Carlo codes. +files produced by NJOY. Parallelism is enabled via a hybrid MPI and OpenMP +programming model. OpenMC was originally developed by members of the `Computational Reactor Physics Group `_ at the `Massachusetts Institute of Technology diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 9c68a9730b..fb55f9e513 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -333,7 +333,7 @@ or sub-elements: velocity sampling) or "dbrc" (Doppler broadening rejection correction). Descriptions of each of these methods are documented here_. - .. _here: http://dx.doi.org/10.1016/j.anucene.2017.12.044 + .. _here: https://doi.org/10.1016/j.anucene.2017.12.044 *Default*: "rvs" diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 9c5b8f5260..9156e848f3 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -109,6 +109,11 @@ The current version of the statepoint file format is 17.0. **/tallies/tally /** +:Attributes: + - **internal** (*int*) -- Flag indicating the presence of tally + data (0) or absence of tally data (1). All user defined + tallies will have a value of 0 unless otherwise instructed. + :Datasets: - **n_realizations** (*int*) -- Number of realizations. - **n_filters** (*int*) -- Number of filters used. - **filters** (*int[]*) -- User-defined unique IDs of the filters on diff --git a/docs/source/io_formats/volume.rst b/docs/source/io_formats/volume.rst index 456a06eef5..c79255a1c9 100644 --- a/docs/source/io_formats/volume.rst +++ b/docs/source/io_formats/volume.rst @@ -23,6 +23,8 @@ The current version of the volume file format is 1.0. bounding box - **upper_right** (*double[3]*) -- Upper-right coordinates of bounding box + - **threshold** (*double*) -- Threshold used for volume uncertainty + - **trigger_type** (*char[]*) -- Trigger type used for volume uncertainty **/domain_/** diff --git a/docs/source/methods/cmfd.rst b/docs/source/methods/cmfd.rst index b13004e100..344a4cc1a6 100644 --- a/docs/source/methods/cmfd.rst +++ b/docs/source/methods/cmfd.rst @@ -538,7 +538,7 @@ Examples of CMFD simulations using OpenMC can be found in [HermanThesis]_. .. rubric:: References .. [BEAVRS] Nick Horelik, Bryan Herman. *Benchmark for Evaluation And Verification of Reactor - Simulations*. Massachusetts Institute of Technology, http://crpg.mit.edu/pub/beavrs + Simulations*. Massachusetts Institute of Technology, https://crpg.mit.edu/research/beavrs , 2013. .. [Gill] Daniel F. Gill. *Newton-Krylov methods for the solution of the k-eigenvalue problem in diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index 4940275d3a..c360805bea 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -8,13 +8,14 @@ Cross Section Representations Continuous-Energy Data ---------------------- -The data governing the interaction of neutrons with -various nuclei for continous-energy problems are represented using the ACE -format which is used by MCNP_ and Serpent_. ACE-format data can be generated -with the NJOY_ nuclear data processing system which converts raw -`ENDF/B data`_ into linearly-interpolable data as required by most Monte Carlo -codes. The use of a standard cross section format allows for a direct comparison -of OpenMC with other codes since the same cross section libraries can be used. +In OpenMC, the data governing the interaction of neutrons with various nuclei +for continous-energy problems are represented using an HDF5 format that can be +produced by converting files in the ACE format, which is used by MCNP_ and +Serpent_. ACE-format data can be generated with the NJOY_ nuclear data +processing system, which converts raw `ENDF/B data`_ into linearly-interpolable +data as required by most Monte Carlo codes. Since ACE-format data can be +converted into OpenMC's HDF5 format, it is possible to perform direct comparison +of OpenMC with other codes using the same underlying nuclear data library. The ACE format contains continuous-energy cross sections for the following types of reactions: elastic scattering, fission (or first-chance fission, @@ -31,7 +32,7 @@ data can be used. Energy Grid Methods ------------------- -The method by which continuous energy cross sections for each nuclide in a +The method by which continuous-energy cross sections for each nuclide in a problem are stored as a function of energy can have a substantial effect on the performance of a Monte Carlo simulation. Since the ACE format is based on linearly-interpolable cross sections, each nuclide has cross sections tabulated @@ -72,9 +73,9 @@ Windowed Multipole Representation --------------------------------- In addition to the usual pointwise representation of cross sections, OpenMC -offers support for an experimental data format called windowed multipole (WMP). -This data format requires less memory than pointwise cross sections, and it -allows on-the-fly Doppler broadening to arbitrary temperature. +offers support for a data format called windowed multipole (WMP). This data +format requires less memory than pointwise cross sections, and it allows +on-the-fly Doppler broadening to arbitrary temperature. The multipole method was introduced by Hwang_ and the faster windowed multipole method by Josey_. In the multipole format, cross section resonances are @@ -258,7 +259,7 @@ where a material has a very large cross sections relative to the other material used to minimize this error. Finally, the above options for representing the physics do not have to be -consistent across the problem. The number of groups and the structure, however, +consistent across the problem. The number of groups and the structure, however, does have to be consistent across the data sets. That is to say that each microscopic or macroscopic data set does not have to apply the same scattering expansion, treatment of multiplicity or angular representation of the cross @@ -269,11 +270,11 @@ or even isotropic scattering. .. _logarithmic mapping technique: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf .. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381 -.. _Josey: http://dx.doi.org/10.1016/j.jcp.2015.08.013 +.. _Josey: https://doi.org/10.1016/j.jcp.2015.08.013 .. _WMP Library: https://github.com/mit-crpg/WMP_Library .. _MCNP: http://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _NJOY: http://t2.lanl.gov/codes.shtml .. _ENDF/B data: http://www.nndc.bnl.gov/endf -.. _Leppanen: http://dx.doi.org/10.1016/j.anucene.2009.03.019 +.. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019 .. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package diff --git a/docs/source/methods/energy_deposition.rst b/docs/source/methods/energy_deposition.rst new file mode 100644 index 0000000000..86dfb9bf9d --- /dev/null +++ b/docs/source/methods/energy_deposition.rst @@ -0,0 +1,149 @@ +.. _methods_heating: + +============================= +Heating and Energy Deposition +============================= + +As particles traverse a problem, some portion of their energy is deposited at +collision sites. This energy is deposited when charged particles, including +electrons and recoil nuclei, undergo electromagnetic interactions with +surrounding electons and ions. The information describing how much energy +is deposited for a specific reaction is referred to as +"heating numbers" and can be computed using a program like NJOY with the +``heatr`` module. + +These heating rate is the product of reaction-specific coefficients and +a reaction cross section + +.. math:: + + H(E) = \phi(E)\sum_i\rho_i\sum_rk_{i, r}(E), + +and has units energy per time, typically eV / s. +Here, :math:`k_{i, r}` are the KERMA (Kinetic Energy Release in Materials) +[Mack97]_ coefficients for reaction :math:`r` of isotope :math:`i`. +The KERMA coefficients have units energy :math:`\times` cross-section, e.g. +eV-barn, and can be used much like a reaction cross section for the purpose +of tallying energy deposition. + +KERMA coefficients can be computed using the energy-balance method with +a nuclear data processing code like NJOY, which performs the following +iteration over all reactions :math:`r` for all isotopes :math:`i` +requested + +.. math:: + + k_{i, r}(E) = \left(E + Q_{i, r} - \bar{E}_{i, r, n} + - \bar{E}_{i, r, \gamma}\right)\sigma_{i, r}(E), + +removing the energy of neutral particles (neutrons and photons) that are +transported away from the reaction site :math:`\bar{E}`, and the reaction +:math:`Q` value. + +------- +Fission +------- + +During a fission event, there are potentially many secondary particles, and all +must be considered. The total energy released in a fission event is typically +broken up into the following categories: + +- :math:`E_{fr}` - kinetic energy of fission fragments +- :math:`E_{n,p}` - energy of prompt fission neutrons +- :math:`E_{n,d}` - energy of delayed fission neutrons +- :math:`E_{\gamma,p}` - energy of prompt fission photons +- :math:`E_{\gamma,d}` - energy of delayed fission photons +- :math:`E_{\beta}` - energy of released :math:`\beta` particles +- :math:`E_{\nu}` - energy of neutrinos + +These components are defined in MF=1,MT=458 data in a standard ENDF/B-6 formatted +file. All these quantities may depend upon incident neutron energy, +but this dependence is not shown to make the following demonstrations cleaner. +As neutrinos scarcely interact with matter, the recoverable energy from +fission is defined as + +.. math:: + + E_r\equiv E_{fr} + E_{n,p} + E_{n, d} + E_{\gamma, p} + + E_{\gamma, d} + E_{\beta} + +Furthermore, the energy of the secondary neutrons and photons is given as +:math:`E_{n, p}` and :math:`E_{\gamma, p}`, respectively. + +NJOY computes the fission KERMA coefficient using this energy-balance method to be + +.. math:: + + k_{i, f}(E) = \left[E + Q(E) - \bar{E}(E)\right]\sigma_{i, f}(E) + = \left[E_{fr} + E_{\gamma, p}\right]\sigma_{i, j}(E) + +.. note:: + + The energy from delayed neutrons and photons and beta particles is intentionally + left out from the NJOY calculations. + +--------------------- +OpenMC Implementation +--------------------- + +For fissile isotopes, OpenMC makes modifications to the heating reaction to +include all relevant components of fission energy release. These modifications +are made to the total heating reaction, MT=301. Breaking the total heating +KERMA into a fission and non-fission section, one can write + +.. math:: + + k_i(E) = k_{i, nf}(E) + \left[E_{fr}(E) + E_{\gamma, p}\right]\sigma_{i, f}(E) + +OpenMC seeks to modify the total heating data to include energy from +:math:`\beta` particles and, conditionally, delayed photons. This conditional +inclusion depends on the simulation mode: neutron transport, or coupled +neutron-photon transport. The heating due to fission is removed using MT=318 +data, and then re-built using the desired components of fission energy release +from MF=1,MT=458 data. + +Neutron Transport +----------------- + +For this case, OpenMC instructs ``heatr`` to produce heating coefficients +assuming that energy from photons, :math:`E_{\gamma, p}` and +:math:`E_{\gamma, d}`, is deposited at the fission site. +Let :math:`N901` represent the total heating number returned from this ``heatr`` +run with :math:`N918` reflecting fission heating computed from NJOY. +:math:`M901` represent the following modification + +.. math:: + + M901_{i}(E)\equiv N901_{i}(E) - N918_{i}(E) + + \left[E_{i, fr} + E_{i, \beta} + E_{i, \gamma, p} + + E_{i, \gamma, d}\right]\sigma_{i, f}(E). + +This modified heating data is stored as the MT=901 reaction and will be scored +if ``heating-local`` is included in :attr:`openmc.Tally.scores`. + +Coupled neutron-photon transport +-------------------------------- + +Here, OpenMC instructs ``heatr`` to assume that energy from photons is not +deposited locally. However, the definitions provided in the NJOY manual +indicate that, regardless of this mode, the prompt photon energy is still +included in :math:`k_{i, f}`, and therefore must be manually removed. +Let :math:`N301` represent the total heating number returned from this +``heatr`` run and :math:`M301` be + +.. math:: + + M301_{i}(E)\equiv N301_{i}(E) - N318_{i}(E) + + \left[E_{i, fr}(E) + E_{i, \beta}(E)\right]\sigma_{i, f}(E). + +This modified heating data is stored as the MT=301 reaction and will be scored +if ``heating`` is included in :attr:`openmc.Tally.scores`. + +---------- +References +---------- + +.. [Mack97] Abdou, M.A., Maynard, C.W., and Wright, R.Q. MACK: computer + program to calculate neutron energy release parameters (fluence-to-kerma + factors) and multigroup neutron reaction cross sections from nuclear data + in ENDF Format. Oak Ridge National Laboratory report ORNL-TM-3994. diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index 4e90dd9922..38d9eb172c 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -617,6 +617,10 @@ condition has been applied, the particle is killed and any surface current tallies are scored to as needed. If a reflective boundary condition has been applied to the surface, surface current tallies are scored to and then the particle's direction is changed according to the procedure in :ref:`reflection`. +Note that the white boundary condition can be considered as the special case of +reflective boundary condition, where the same processing method will be applied to +deal with the surface current tallies scoring, except for determining the +changes of particle's direction according to the procedures in :ref:`white`. Next, we need to determine what cell is beyond the surface in the direction of travel of the particle so that we can evaluate cross sections based on its @@ -892,8 +896,72 @@ Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0`. Thus, the gradient to the surface is \\ 2Cz + Ey + Fx + J \end{array} \right ). -.. _constructive solid geometry: http://en.wikipedia.org/wiki/Constructive_solid_geometry -.. _surfaces: http://en.wikipedia.org/wiki/Surface +.. _white: + +------------------------- +White Boundary Conditions +------------------------- + +The `white boundary condition `_ +is usually applied in deterministic codes, where the particle will hit the +surface and travel back with isotropic angular distribution. The change in +particle's direction is sampled from a cosine distribution instead of uniform. +Figure :num:`fig-cosine-dist` shows an example of cosine-distribution reflection +on the arbitrary surface relative to the surface normal. + +.. _fig-cosine-dist: + +.. figure:: ../_images/cosine-dist.png + :align: center + :figclass: align-center + + Cosine-distribution reflection on an arbitrary surface. + +The probability density function (pdf) for the reflected direction can be +expressed as follows, + +.. math:: + :label: white-reflection-pdf + + f(\mu, \phi) d\mu d\phi = \frac{\mu}{\pi} d\mu d\phi = 2\mu d\mu \frac{d\phi}{2\pi} + +where :math:`\mu = \cos \theta` is the cosine of the polar angle between +reflected direction and the normal to the surface; and :math:`\theta` is the +azimuthal angle in :math:`[0,2\pi]`. We can separate the multivariate +probability density into two separate univariate density functions, one for +the cosine of the polar angle, + +.. math:: + :label: white-reflection-cosine + + f(\mu) = 2\mu + +and one for the azimuthal angle, + +.. math:: + :label: white-reflection-uniform + + f(\phi) = \frac{1}{2\pi}. + +Each of these density functions can be sampled by analytical inversion of the +cumulative distribution distribution, resulting in the following sampling +scheme: + +.. math:: + :label: white-reflection-sqrt-prn + + \mu = \sqrt{\xi_1} \\ + \phi = 2\pi\xi_2 + +where :math:`\xi_1` and :math:`\xi_2` are uniform random numbers on +:math:`[0,1)`. With the sampled values of :math:`\mu` and :math:`\phi`, the +final reflected direction vector can be computed via rotation of the surface +normal using the equations from :ref:`transform-coordinates`. The white boundary +condition can be applied to any kind of surface, as long as the normal to the +surface is known as in :ref:`reflection`. + +.. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry +.. _surfaces: https://en.wikipedia.org/wiki/Surface .. _MCNP: http://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _Monte Carlo Performance benchmark: https://github.com/mit-crpg/benchmarks/tree/master/mc-performance/openmc diff --git a/docs/source/methods/index.rst b/docs/source/methods/index.rst index d8e6ee8190..5eff6c50e6 100644 --- a/docs/source/methods/index.rst +++ b/docs/source/methods/index.rst @@ -18,3 +18,4 @@ Theory and Methodology eigenvalue parallelization cmfd + energy_deposition diff --git a/docs/source/methods/introduction.rst b/docs/source/methods/introduction.rst index dab1c544ad..adb52c02e1 100644 --- a/docs/source/methods/introduction.rst +++ b/docs/source/methods/introduction.rst @@ -139,9 +139,9 @@ be performed before the run is finished. This include the following: - If requested, a source file is written to disk. - - All allocatable arrays are deallocated. + - Dynamically-allocated memory should be freed. -.. _probability distributions: http://en.wikipedia.org/wiki/Probability_distribution -.. _Monte Carlo: http://en.wikipedia.org/wiki/Monte_Carlo_method -.. _central limit theorem: http://en.wikipedia.org/wiki/Central_limit_theorem -.. _pseudorandom number: http://en.wikipedia.org/wiki/Pseudorandom_number_generator +.. _probability distributions: https://en.wikipedia.org/wiki/Probability_distribution +.. _Monte Carlo: https://en.wikipedia.org/wiki/Monte_Carlo_method +.. _central limit theorem: https://en.wikipedia.org/wiki/Central_limit_theorem +.. _pseudorandom number: https://en.wikipedia.org/wiki/Pseudorandom_number_generator diff --git a/docs/source/methods/neutron_physics.rst b/docs/source/methods/neutron_physics.rst index 60d3c1468a..384e79bdd6 100644 --- a/docs/source/methods/neutron_physics.rst +++ b/docs/source/methods/neutron_physics.rst @@ -1298,11 +1298,10 @@ section over the range of velocities considered: where it should be noted that the maximum is taken over the range :math:`[v_n - 4/\beta, 4_n + 4\beta]`. This method is known as Doppler broadening rejection correction (DBRC) and was first introduced by `Becker et al.`_. OpenMC has an -implementation of DBRC as well as an accelerated sampling method that are -described fully in `Walsh et al.`_ +implementation of DBRC as well as an accelerated sampling method that samples the `relative velocity`_ directly. -.. _Becker et al.: http://dx.doi.org/10.1016/j.anucene.2008.12.001 -.. _Walsh et al.: http://dx.doi.org/10.1016/j.anucene.2014.01.017 +.. _Becker et al.: https://doi.org/10.1016/j.anucene.2008.12.001 +.. _relative velocity: https://doi.org/10.1016/j.anucene.2017.12.044 .. _sab_tables: @@ -1645,23 +1644,23 @@ another. .. |sab| replace:: S(:math:`\alpha,\beta,T`) -.. _SIGMA1 method: http://dx.doi.org/10.13182/NSE76-1 +.. _SIGMA1 method: https://doi.org/10.13182/NSE76-1 .. _scaled interpolation: http://www.ans.org/pubs/journals/nse/a_26575 -.. _probability table method: http://dx.doi.org/10.13182/NSE72-3 +.. _probability table method: https://doi.org/10.13182/NSE72-3 -.. _Watt fission spectrum: http://dx.doi.org/10.1103/PhysRev.87.1037 +.. _Watt fission spectrum: https://doi.org/10.1103/PhysRev.87.1037 .. _Foderaro: http://hdl.handle.net/1721.1/1716 -.. _OECD: http://www.oecd-nea.org/dbprog/MMRW-BOOKS.html +.. _OECD: http://www.oecd-nea.org/tools/abstract/detail/NEA-1792 -.. _NJOY: https://njoy.github.io/NJOY2016/ +.. _NJOY: https://www.njoy21.io/NJOY2016/ .. _PREPRO: http://www-nds.iaea.org/ndspub/endf/prepro/ -.. _ENDF-6 Format: http://www-nds.iaea.org/ndspub/documents/endf/endf102/endf102.pdf +.. _ENDF-6 Format: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf .. _Monte Carlo Sampler: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-9721.pdf @@ -1669,7 +1668,7 @@ another. .. _MC21: http://www.osti.gov/bridge/servlets/purl/903083-HT5p1o/903083.pdf -.. _Romano: http://dx.doi.org/10.1016/j.cpc.2014.11.001 +.. _Romano: https://doi.org/10.1016/j.cpc.2014.11.001 .. _Sutton and Brown: http://www.osti.gov/bridge/product.biblio.jsp?osti_id=307911 diff --git a/docs/source/methods/parallelization.rst b/docs/source/methods/parallelization.rst index 63c4a9d212..5bf090a2b5 100644 --- a/docs/source/methods/parallelization.rst +++ b/docs/source/methods/parallelization.rst @@ -607,33 +607,33 @@ is actually independent of the number of nodes: Radiation Penetration Calculations on a Parallel Computer," *Trans. Am. Nucl. Soc.*, **17**, 260 (1973). -.. _first paper: http://www.jstor.org/stable/2280232 +.. _first paper: https://doi.org/10.2307/2280232 .. _work of Forrest Brown: http://hdl.handle.net/2027.42/24996 -.. _Brissenden and Garlick: http://dx.doi.org/10.1016/0306-4549(86)90095-2 +.. _Brissenden and Garlick: https://doi.org/10.1016/0306-4549(86)90095-2 .. _MPICH2: http://www.mcs.anl.gov/mpi/mpich -.. _binomial tree: http://www.cs.auckland.ac.nz/~jmor159/PLDS210/trees.html +.. _binomial tree: https://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf -.. _Geary: http://www.jstor.org/stable/10.2307/2342070 +.. _Geary: https://doi.org/10.2307/2342070 .. _Barnett: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.7772 -.. _single-instruction multiple-data: http://en.wikipedia.org/wiki/SIMD +.. _single-instruction multiple-data: https://en.wikipedia.org/wiki/SIMD -.. _vector computers: http://en.wikipedia.org/wiki/Vector_processor +.. _vector computers: https://en.wikipedia.org/wiki/Vector_processor -.. _single program multiple data: http://en.wikipedia.org/wiki/SPMD +.. _single program multiple data: https://en.wikipedia.org/wiki/SPMD -.. _message-passing interface: http://en.wikipedia.org/wiki/Message_Passing_Interface +.. _message-passing interface: https://en.wikipedia.org/wiki/Message_Passing_Interface .. _PVM: http://www.csm.ornl.gov/pvm/pvm_home.html .. _MPI: http://www.mcs.anl.gov/research/projects/mpi/ -.. _embarrassingly parallel: http://en.wikipedia.org/wiki/Embarrassingly_parallel +.. _embarrassingly parallel: https://en.wikipedia.org/wiki/Embarrassingly_parallel .. _sends: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Send.html @@ -643,8 +643,8 @@ is actually independent of the number of nodes: .. _allgather: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Allgather.html -.. _Cauchy distribution: http://en.wikipedia.org/wiki/Cauchy_distribution +.. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution -.. _latency: http://en.wikipedia.org/wiki/Latency_(engineering)#Packet-switched_networks +.. _latency: https://en.wikipedia.org/wiki/Latency_(engineering)#Packet-switched_networks -.. _bandwidth: http://en.wikipedia.org/wiki/Bandwidth_(computing) +.. _bandwidth: https://en.wikipedia.org/wiki/Bandwidth_(computing) diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index 4494fc6431..f9bdcfa1cc 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -579,7 +579,7 @@ sampled using the leading order term of the Sauter–Gluckstern–Hull distribution, .. math:: - :label: sauter–gluckstern–hull + :label: sauter-gluckstern-hull p(\mu_{\pm}) = C(1 - \beta_{\pm}\mu_{\pm})^{-2}, @@ -588,7 +588,7 @@ ratio of the velocity of the charged particle to the speed of light given in :eq:`beta-2`. The inverse transform method is used to sample :math:`\mu_{-}` and -:math:`\mu_{+}` from :eq:`sauter–gluckstern–hull`, using the sampling formula +:math:`\mu_{+}` from :eq:`sauter-gluckstern-hull`, using the sampling formula .. math:: :label: sample-mu diff --git a/docs/source/methods/random_numbers.rst b/docs/source/methods/random_numbers.rst index ce118556e4..0cefc9156b 100644 --- a/docs/source/methods/random_numbers.rst +++ b/docs/source/methods/random_numbers.rst @@ -67,6 +67,6 @@ the idea is to determine the new multiplicative and additive constants in .. rubric:: References -.. _L'Ecuyer: http://dx.doi.org/10.1090/S0025-5718-99-00996-5 +.. _L'Ecuyer: https://doi.org/10.1090/S0025-5718-99-00996-5 .. _Brown: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/anl-rn-arb-stride.pdf -.. _linear congruential generator: http://en.wikipedia.org/wiki/Linear_congruential_generator +.. _linear congruential generator: https://en.wikipedia.org/wiki/Linear_congruential_generator diff --git a/docs/source/methods/tallies.rst b/docs/source/methods/tallies.rst index 9e7bd93bb0..d67584622c 100644 --- a/docs/source/methods/tallies.rst +++ b/docs/source/methods/tallies.rst @@ -484,31 +484,31 @@ improve the estimate of the percentile. .. rubric:: References -.. _following approximation: http://dx.doi.org/10.1080/03610918708812641 +.. _following approximation: https://doi.org/10.1080/03610918708812641 -.. _Bessel's correction: http://en.wikipedia.org/wiki/Bessel's_correction +.. _Bessel's correction: https://en.wikipedia.org/wiki/Bessel's_correction -.. _random variable: http://en.wikipedia.org/wiki/Random_variable +.. _random variable: https://en.wikipedia.org/wiki/Random_variable -.. _stochastic process: http://en.wikipedia.org/wiki/Stochastic_process +.. _stochastic process: https://en.wikipedia.org/wiki/Stochastic_process -.. _independent, identically-distributed random variables: http://en.wikipedia.org/wiki/Independent_and_identically_distributed_random_variables +.. _independent, identically-distributed random variables: https://en.wikipedia.org/wiki/Independent_and_identically_distributed_random_variables -.. _law of large numbers: http://en.wikipedia.org/wiki/Law_of_large_numbers +.. _law of large numbers: https://en.wikipedia.org/wiki/Law_of_large_numbers -.. _expected value: http://en.wikipedia.org/wiki/Expected_value +.. _expected value: https://en.wikipedia.org/wiki/Expected_value -.. _converges in probability: http://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_probability +.. _converges in probability: https://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_probability -.. _normal distribution: http://en.wikipedia.org/wiki/Normal_distribution +.. _normal distribution: https://en.wikipedia.org/wiki/Normal_distribution -.. _converges in distribution: http://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_distribution +.. _converges in distribution: https://en.wikipedia.org/wiki/Convergence_of_random_variables#Convergence_in_distribution -.. _confidence intervals: http://en.wikipedia.org/wiki/Confidence_interval +.. _confidence intervals: https://en.wikipedia.org/wiki/Confidence_interval -.. _Student's t-distribution: http://en.wikipedia.org/wiki/Student%27s_t-distribution +.. _Student's t-distribution: https://en.wikipedia.org/wiki/Student%27s_t-distribution -.. _Cauchy distribution: http://en.wikipedia.org/wiki/Cauchy_distribution +.. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution .. _unpublished rational approximation: https://web.archive.org/web/20150926021742/http://home.online.no/~pjacklam/notes/invnorm/ diff --git a/docs/source/publications.rst b/docs/source/publications.rst index f33cf79587..24bcc66a18 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -58,7 +58,8 @@ Coupling and Multi-physics -------------------------- - Miriam A. Kreher, Benoit Forget, and Kord Smith, "Single-Batch Monte Carlo - Multiphysics Coupling," *Proc. M&C*, Portland, Oregon, Aug. 25-29 (2019). + Multiphysics Coupling," *Proc. M&C*, 1789-1797, Portland, Oregon, Aug. 25-29 + (2019). - Ze-Long Zhao, Yongwei Yang, and Shuang Hong, "`Application of FLUKA and OpenMC in coupled physics calculation of target and subcritical reactor for ADS @@ -118,7 +119,7 @@ Geometry and Visualization - Sterling Harper, Paul Romano, Benoit Forget, and Kord Smith, "Efficient dynamic threadsafe neighbor lists for Monte Carlo ray tracing," *Proc. M&C*, - Portland, Oregon, Aug. 25-29 (2019). + 918-926, Portland, Oregon, Aug. 25-29 (2019). - Jin-Yang Li, Long Gu, Hu-Shan Xu, Nadezha Korepanova, Rui Yu, Yan-Lei Zhu, and Chang-Ping Qin, "`CAD modeling study on FLUKA and OpenMC for accelerator @@ -141,6 +142,10 @@ Geometry and Visualization Miscellaneous ------------- +- Shikhar Kumar, Benoit Forget, and Kord Smith, "Analysis of fission source + convergence for a 3-D SMR core using functional expansion tallies," *Proc. + M&C*, 937-947, Portland, Oregon, Aug. 25-29 (2019). + - Faisal Qayyum, Muhammad R. Ali, Awais Zahur, and R. Khan, "`Improvements in methodology to determine feedback reactivity coefficients `_," *Nucl. Sci. Tech.*, **30**: 63 @@ -361,7 +366,7 @@ Nuclear Data - Jonathan A. Walsh, Benoit Forget, Kord S. Smith, and Forrest B. Brown, "`Uncertainty in Fast Reactor-Relevant Critical Benchmark Simulations Due to Unresolved Resonance Structure - `_," + `_," *Proc. Int. Conf. Mathematics & Computational Methods Applied to Nuclear Science and Engineering*, Jeju, Korea, Apr. 16-20, 2017. @@ -404,7 +409,7 @@ Parallelism - Paul K. Romano and Andrew R. Siegel, "`Limits on the efficiency of event-based algorithms for Monte Carlo neutron transport - `_," + `_," *Proc. Int. Conf. Mathematics & Computational Methods Applied to Nuclear Science and Engineering*, Jeju, Korea, Apr. 16-20, 2017. @@ -496,7 +501,7 @@ Depletion - Jose L. Salcedo-Perez, Benoit Forget, Kord Smith, and Paul Romano, "Hybrid tallies to improve performance in depletion Monte Carlo simulations," *Proc. - M&C*, Aug. 25-29 (2019). + M&C*, 927-936, Portland, Oregon, Aug. 25-29 (2019). - Zhao-Qing Liu, Ze-Long Zhao, Yong-Wei Yang, Yu-Cui Gao, Hai-Yan Meng, and Qing-Yu Gao, "`Development and validation of depletion code system IMPC-Burnup @@ -526,6 +531,10 @@ Depletion Sensitivity Analysis -------------------- +- Abdulla Alhajri and Benoit Forget, "Eigenvalue Sensitivity in Monte Carlo + Simulations to Nuclear Data Parameters using the Multipole Formalism," *Proc. + M&C*, 1895-1906, Portland, Oregon, Aug. 25-29 (2019). + - Xingjie Peng, Jingang Liang, Benoit Forget, and Kord Smith, "`Calculation of adjoint-weighted reactor kinetics parameters in OpenMC `_", *Ann. Nucl. Energy*, diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index cacc5472e1..44094ffd41 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -1,8 +1,8 @@ --------------------------------------------------- -:mod:`openmc.capi` -- Python bindings to the C API --------------------------------------------------- +------------------------------------------------------ +:mod:`openmc.lib` -- Python bindings to the C/C++ API +------------------------------------------------------ -.. automodule:: openmc.capi +.. automodule:: openmc.lib Functions --------- diff --git a/docs/source/pythonapi/data.rst b/docs/source/pythonapi/data.rst index 92cb785481..be29d7c0eb 100644 --- a/docs/source/pythonapi/data.rst +++ b/docs/source/pythonapi/data.rst @@ -2,6 +2,8 @@ :mod:`openmc.data` -- Nuclear Data Interface -------------------------------------------- +.. module:: openmc.data + Core Classes ------------ @@ -13,15 +15,15 @@ and product yields. :nosignatures: :template: myclass.rst - openmc.data.IncidentNeutron - openmc.data.Reaction - openmc.data.Product - openmc.data.FissionEnergyRelease - openmc.data.DataLibrary - openmc.data.Decay - openmc.data.FissionProductYields - openmc.data.WindowedMultipole - openmc.data.ProbabilityTables + IncidentNeutron + Reaction + Product + FissionEnergyRelease + DataLibrary + Decay + FissionProductYields + WindowedMultipole + ProbabilityTables The following classes are used for storing atomic data (incident photon cross sections, atomic relaxation): @@ -31,9 +33,9 @@ sections, atomic relaxation): :nosignatures: :template: myclass.rst - openmc.data.IncidentPhoton - openmc.data.PhotonReaction - openmc.data.AtomicRelaxation + IncidentPhoton + PhotonReaction + AtomicRelaxation The following classes are used for storing thermal neutron scattering data: @@ -43,10 +45,10 @@ The following classes are used for storing thermal neutron scattering data: :nosignatures: :template: myclass.rst - openmc.data.ThermalScattering - openmc.data.ThermalScatteringReaction - openmc.data.CoherentElastic - openmc.data.IncoherentElastic + ThermalScattering + ThermalScatteringReaction + CoherentElastic + IncoherentElastic Core Functions @@ -57,12 +59,12 @@ Core Functions :nosignatures: :template: myfunction.rst - openmc.data.atomic_mass - openmc.data.gnd_name - openmc.data.linearize - openmc.data.thin - openmc.data.water_density - openmc.data.zam + atomic_mass + gnd_name + linearize + thin + water_density + zam One-dimensional Functions ------------------------- @@ -72,13 +74,13 @@ One-dimensional Functions :nosignatures: :template: myclass.rst - openmc.data.Function1D - openmc.data.Tabulated1D - openmc.data.Polynomial - openmc.data.Combination - openmc.data.Sum - openmc.data.Regions1D - openmc.data.ResonancesWithBackground + Function1D + Tabulated1D + Polynomial + Combination + Sum + Regions1D + ResonancesWithBackground Angle-Energy Distributions -------------------------- @@ -88,27 +90,27 @@ Angle-Energy Distributions :nosignatures: :template: myclass.rst - openmc.data.AngleEnergy - openmc.data.KalbachMann - openmc.data.CorrelatedAngleEnergy - openmc.data.UncorrelatedAngleEnergy - openmc.data.NBodyPhaseSpace - openmc.data.LaboratoryAngleEnergy - openmc.data.AngleDistribution - openmc.data.EnergyDistribution - openmc.data.ArbitraryTabulated - openmc.data.GeneralEvaporation - openmc.data.MaxwellEnergy - openmc.data.Evaporation - openmc.data.WattEnergy - openmc.data.MadlandNix - openmc.data.DiscretePhoton - openmc.data.LevelInelastic - openmc.data.ContinuousTabular - openmc.data.CoherentElasticAE - openmc.data.IncoherentElasticAE - openmc.data.IncoherentElasticAEDiscrete - openmc.data.IncoherentInelasticAEDiscrete + AngleEnergy + KalbachMann + CorrelatedAngleEnergy + UncorrelatedAngleEnergy + NBodyPhaseSpace + LaboratoryAngleEnergy + AngleDistribution + EnergyDistribution + ArbitraryTabulated + GeneralEvaporation + MaxwellEnergy + Evaporation + WattEnergy + MadlandNix + DiscretePhoton + LevelInelastic + ContinuousTabular + CoherentElasticAE + IncoherentElasticAE + IncoherentElasticAEDiscrete + IncoherentInelasticAEDiscrete Resonance Data -------------- @@ -118,20 +120,20 @@ Resonance Data :nosignatures: :template: myclass.rst - openmc.data.Resonances - openmc.data.ResonanceRange - openmc.data.SingleLevelBreitWigner - openmc.data.MultiLevelBreitWigner - openmc.data.ReichMoore - openmc.data.RMatrixLimited - openmc.data.ResonanceCovariances - openmc.data.ResonanceCovarianceRange - openmc.data.SingleLevelBreitWignerCovariance - openmc.data.MultiLevelBreitWignerCovariance - openmc.data.ReichMooreCovariance - openmc.data.ParticlePair - openmc.data.SpinGroup - openmc.data.Unresolved + Resonances + ResonanceRange + SingleLevelBreitWigner + MultiLevelBreitWigner + ReichMoore + RMatrixLimited + ResonanceCovariances + ResonanceCovarianceRange + SingleLevelBreitWignerCovariance + MultiLevelBreitWignerCovariance + ReichMooreCovariance + ParticlePair + SpinGroup + Unresolved ACE Format ---------- @@ -144,8 +146,8 @@ Classes :nosignatures: :template: myclass.rst - openmc.data.ace.Library - openmc.data.ace.Table + ace.Library + ace.Table Functions +++++++++ @@ -155,7 +157,7 @@ Functions :nosignatures: :template: myfunction.rst - openmc.data.ace.ascii_to_binary + ace.ascii_to_binary ENDF Format ----------- @@ -168,7 +170,7 @@ Classes :nosignatures: :template: myclass.rst - openmc.data.endf.Evaluation + endf.Evaluation Functions +++++++++ @@ -178,13 +180,13 @@ Functions :nosignatures: :template: myfunction.rst - openmc.data.endf.float_endf - openmc.data.endf.get_cont_record - openmc.data.endf.get_evaluations - openmc.data.endf.get_head_record - openmc.data.endf.get_tab1_record - openmc.data.endf.get_tab2_record - openmc.data.endf.get_text_record + endf.float_endf + endf.get_cont_record + endf.get_evaluations + endf.get_head_record + endf.get_tab1_record + endf.get_tab2_record + endf.get_text_record NJOY Interface -------------- @@ -194,7 +196,7 @@ NJOY Interface :nosignatures: :template: myfunction.rst - openmc.data.njoy.run - openmc.data.njoy.make_pendf - openmc.data.njoy.make_ace - openmc.data.njoy.make_ace_thermal + njoy.run + njoy.make_pendf + njoy.make_ace + njoy.make_ace_thermal diff --git a/docs/source/pythonapi/deplete.rst b/docs/source/pythonapi/deplete.rst index 5b26eb38aa..b9970ffb11 100644 --- a/docs/source/pythonapi/deplete.rst +++ b/docs/source/pythonapi/deplete.rst @@ -1,12 +1,27 @@ .. _pythonapi_deplete: +.. module:: openmc.deplete + ---------------------------------- :mod:`openmc.deplete` -- Depletion ---------------------------------- -.. module:: openmc.deplete +Primary API +----------- -Several classes are provided that implement different time-integration +The two primary requirements to perform depletion with :mod:`openmc.deplete` +are: + + 1) A transport operator + 2) A time-integration scheme + +The former is responsible for executing a transport code, like OpenMC, +and retaining important information required for depletion. The most common examples +are reaction rates and power normalization data. The latter is responsible for +projecting reaction rates and compositions forward in calendar time across +some step size :math:`\Delta t`, and obtaining new compositions given a power +or power density. The :class:`Operator` is provided to handle communicating with +OpenMC. Several classes are provided that implement different time-integration algorithms for depletion calculations, which are described in detail in Colin Josey's thesis, `Development and analysis of high order neutron transport-depletion coupling algorithms `_. @@ -31,14 +46,46 @@ specific to OpenMC is available using the following class: .. autosummary:: :toctree: generated :nosignatures: - :template: myclass.rst + :template: mycallable.rst Operator -When running in parallel using `mpi4py `_, the MPI -intercommunicator used can be changed by modifying the following module -variable. If it is not explicitly modified, it defaults to -``mpi4py.MPI.COMM_WORLD``. +The :class:`Operator` must also have some knowledge of how nuclides transmute +and decay. This is handled by the :class:`Chain`. + +Minimal Example +--------------- + +A minimal example for performing depletion would be: + +.. code:: + + >>> import openmc + >>> import openmc.deplete + >>> geometry = openmc.Geometry.from_xml() + >>> settings = openmc.Settings.from_xml() + + # Representation of a depletion chain + >>> chain_file = "chain_casl.xml" + >>> operator = openmc.deplete.Operator( + ... geometry, settings, chain_file) + + # Set up 5 time steps of one day each + >>> dt = [24 * 60 * 60] * 5 + >>> power = 1e6 # constant power of 1 MW + + # Deplete using mid-point predictor-corrector + >>> cecm = openmc.deplete.CECMIntegrator( + ... operator, dt, power) + >>> cecm.integrate() + +Internal Classes and Functions +------------------------------ + +When running in parallel using `mpi4py +`_, the MPI intercommunicator used can +be changed by modifying the following module variable. If it is not explicitly +modified, it defaults to ``mpi4py.MPI.COMM_WORLD``. .. data:: comm @@ -46,9 +93,6 @@ variable. If it is not explicitly modified, it defaults to :type: mpi4py.MPI.Comm -Internal Classes and Functions ------------------------------- - During a depletion calculation, the depletion chain, reaction rates, and number densities are managed through a series of internal classes that are not normally visible to a user. However, should you find yourself wondering about these @@ -82,6 +126,26 @@ data, such as number densities and reaction rates for each material. Results ResultsList +The following class and functions are used to solve the depletion equations, +with :func:`cram.CRAM48` being the default. + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myintegrator.rst + + cram.IPFCramSolver + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + cram.CRAM16 + cram.CRAM48 + cram.deplete + cram.timed_deplete + The following classes are used to help the :class:`openmc.deplete.Operator` compute quantities like effective fission yields, reaction rates, and total system energy. @@ -95,40 +159,47 @@ total system energy. helpers.ChainFissionHelper helpers.ConstantFissionYieldHelper helpers.DirectReactionRateHelper + helpers.EnergyScoreHelper helpers.FissionYieldCutoffHelper -The following classes are abstract classes that can be used to extend the -:mod:`openmc.deplete` capabilities: + +Abstract Base Classes +--------------------- + +A good starting point for extending capabilities in :mod:`openmc.deplete` is +to examine the following abstract base classes. Custom classes can +inherit from :class:`abc.TransportOperator` to implement alternative +schemes for collecting reaction rates and other data from a transport code +prior to depleting materials + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: mycallable.rst + + abc.TransportOperator + +The following classes are abstract classes used to pass information from +OpenMC simulations back on to the :class:`abc.TransportOperator` .. autosummary:: :toctree: generated :nosignatures: :template: myclass.rst - EnergyHelper - FissionYieldHelper - ReactionRateHelper - TalliedFissionYieldHelper - TransportOperator + abc.EnergyHelper + abc.FissionYieldHelper + abc.ReactionRateHelper + abc.TalliedFissionYieldHelper -Custom integrators can be developed by subclassing from the following abstract -base classes: +Custom integrators or depletion solvers can be developed by subclassing from +the following abstract base classes: .. autosummary:: :toctree: generated :nosignatures: :template: myintegrator.rst - Integrator - SIIntegrator - -Each of the integrator classes also relies on a number of "helper" functions -as follows: - -.. autosummary:: - :toctree: generated - :nosignatures: - :template: myfunction.rst - - cram.CRAM16 - cram.CRAM48 + abc.Integrator + abc.SIIntegrator + abc.DepSystemSolver diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 87d0d2a245..7f67f95cef 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -27,7 +27,7 @@ there are many substantial benefits to using the Python API, including: For those new to Python, there are many good tutorials available online. We recommend going through the modules from `Codecademy -`_ and/or the `Scipy lectures +`_ and/or the `Scipy lectures `_. The full API documentation serves to provide more information on a given module diff --git a/docs/source/releasenotes/0.11.0.rst b/docs/source/releasenotes/0.11.0.rst index 0bf44be3d4..221d576580 100644 --- a/docs/source/releasenotes/0.11.0.rst +++ b/docs/source/releasenotes/0.11.0.rst @@ -2,11 +2,6 @@ What's New in 0.11.0 ==================== -.. note:: - These release notes are for a future release of OpenMC and are still subject - to change. The new features and bug fixes documented here reflect the - current status of the ``develop`` branch of OpenMC. - .. currentmodule:: openmc ------- @@ -17,7 +12,7 @@ This release of OpenMC adds several major new features: :ref:`depletion `, photon transport, and support for CAD geometries through DAGMC. In addition, the core codebase has been rewritten in C++14 (it was previously written in Fortran 2008). This makes compiling the code -cosiderably simpler as no Fortran compiler is needed. +considerably simpler as no Fortran compiler is needed. Functional expansion tallies are now supported through several new tally filters that can be arbitrarily combined: @@ -40,13 +35,16 @@ random, non-overlapping configuration of spheres within the region. New Features ------------ +- White boundary conditions can be applied to surfaces +- Support for rectilinear meshes through :class:`openmc.RectilinearMesh`. - The :class:`Geometry`, :class:`Materials`, and :class:`Settings` classes now have a ``from_xml`` method that will build an instance from an existing XML file. - Predefined energy group structures can be found in :data:`openmc.mgxs.GROUP_STRUCTURES`. - New tally scores: ``H1-production``, ``H2-production``, ``H3-production``, - ``He3-production``, ``He4-production``, ``heating``, and ``damage-energy`` + ``He3-production``, ``He4-production``, ``heating``, ``heating-local``, and + ``damage-energy``. - Switched to cell-based neighor lists (`PR 1140 `_) - Two new probability distributions that can be used for source distributions: @@ -54,11 +52,40 @@ New Features - The :mod:`openmc.data` module now supports reading and sampling from ENDF File 32 resonance covariance data (`PR 1024 `_). +- Several new convenience functions/methods have been added: + + - The :func:`openmc.model.cylinder_from_points` function creates a cylinder + given two points passing through its center and a radius. + - The :meth:`openmc.Plane.from_points` function creates a plane given three + points that pass through it. + - The :func:`openmc.model.pin` function creates a pin cell universe given a + sequence of concentric cylinders and materials. + +------------------ +Python API Changes +------------------ + +- All surface classes now have coefficient arguments given as lowercase names. +- The order of arguments in surface classes has been changed so that + coefficients are the first arguments (rather than the optional surface ID). + This means you can now write:: + + x = openmc.XPlane(5.0, 'reflective') + zc = openmc.ZCylinder(0., 0., 10.) + +- The ``Mesh`` class has been renamed :class:`openmc.RegularMesh`. +- The ``get_rectangular_prism`` function has been renamed + :func:`openmc.model.rectangular_prism`. +- The ``get_hexagonal_prism`` function has been renamed + :func:`openmc.model.hexagonal_prism`. +- Python bindings to the C/C++ API have been move from ``openmc.capi`` to + :mod:`openmc.lib`. --------- Bug Fixes --------- +- `Rotate azimuthal distributions correctly for source sampling `_ - `Fix reading ASCII ACE tables in Python 3 `_ - `Fix bug for distributed temperatures `_ - `Fix bug for distance to boundary in complex cells `_ @@ -77,6 +104,7 @@ This release contains new contributions from the following people: - `Brody Bassett `_ - `Will Boyd `_ - `Andrew Davis `_ +- `Iurii Drobyshev `_ - `Guillaume Giudicelli `_ - `Brittany Grayson `_ - `Zhuoran Han `_ @@ -94,9 +122,12 @@ This release contains new contributions from the following people: - `Isaac Meyer `_ - `April Novak `_ - `Adam Nelson `_ +- `Gavin Ridley `_ - `Jose Salcedo Perez `_ - `Paul Romano `_ - `Sam Shaner `_ - `Jonathan Shimwell `_ - `Patrick Shriwise `_ - `John Tramm `_ +- `Jiankai Yu `_ +- `Xiaokang Zhang `_ diff --git a/docs/source/releasenotes/0.4.1.rst b/docs/source/releasenotes/0.4.1.rst index 8ec96897ef..4bc3bc6585 100644 --- a/docs/source/releasenotes/0.4.1.rst +++ b/docs/source/releasenotes/0.4.1.rst @@ -45,9 +45,9 @@ Bug Fixes - `95cfac`_: Fixed error in cell neighbor searches. - `83a803`_: Fixed bug related to probability tables. -.. _b206a8: https://github.com/mit-crpg/openmc/commit/b206a8 -.. _800742: https://github.com/mit-crpg/openmc/commit/800742 -.. _a07c08: https://github.com/mit-crpg/openmc/commit/a07c08 -.. _a75283: https://github.com/mit-crpg/openmc/commit/a75283 -.. _95cfac: https://github.com/mit-crpg/openmc/commit/95cfac -.. _83a803: https://github.com/mit-crpg/openmc/commit/83a803 +.. _b206a8: https://github.com/openmc-dev/openmc/commit/b206a8 +.. _800742: https://github.com/openmc-dev/openmc/commit/800742 +.. _a07c08: https://github.com/openmc-dev/openmc/commit/a07c08 +.. _a75283: https://github.com/openmc-dev/openmc/commit/a75283 +.. _95cfac: https://github.com/openmc-dev/openmc/commit/95cfac +.. _83a803: https://github.com/openmc-dev/openmc/commit/83a803 diff --git a/docs/source/releasenotes/0.4.2.rst b/docs/source/releasenotes/0.4.2.rst index c0d399301e..0e28eb6e66 100644 --- a/docs/source/releasenotes/0.4.2.rst +++ b/docs/source/releasenotes/0.4.2.rst @@ -42,13 +42,13 @@ Bug Fixes - d050c7_: Added Bessel's correction to make estimate of variance unbiased. - 2a5b9c_: Fixed regression in plotting. -.. _a27f8f: https://github.com/mit-crpg/openmc/commit/a27f8f -.. _afe121: https://github.com/mit-crpg/openmc/commit/afe121 -.. _e0968e: https://github.com/mit-crpg/openmc/commit/e0968e -.. _298db8: https://github.com/mit-crpg/openmc/commit/298db8 -.. _2f3bbe: https://github.com/mit-crpg/openmc/commit/2f3bbe -.. _671f30: https://github.com/mit-crpg/openmc/commit/671f30 -.. _b2c40e: https://github.com/mit-crpg/openmc/commit/b2c40e -.. _5524fd: https://github.com/mit-crpg/openmc/commit/5524fd -.. _d050c7: https://github.com/mit-crpg/openmc/commit/d050c7 -.. _2a5b9c: https://github.com/mit-crpg/openmc/commit/2a5b9c +.. _a27f8f: https://github.com/openmc-dev/openmc/commit/a27f8f +.. _afe121: https://github.com/openmc-dev/openmc/commit/afe121 +.. _e0968e: https://github.com/openmc-dev/openmc/commit/e0968e +.. _298db8: https://github.com/openmc-dev/openmc/commit/298db8 +.. _2f3bbe: https://github.com/openmc-dev/openmc/commit/2f3bbe +.. _671f30: https://github.com/openmc-dev/openmc/commit/671f30 +.. _b2c40e: https://github.com/openmc-dev/openmc/commit/b2c40e +.. _5524fd: https://github.com/openmc-dev/openmc/commit/5524fd +.. _d050c7: https://github.com/openmc-dev/openmc/commit/d050c7 +.. _2a5b9c: https://github.com/openmc-dev/openmc/commit/2a5b9c diff --git a/docs/source/releasenotes/0.4.3.rst b/docs/source/releasenotes/0.4.3.rst index 7ef12198d7..4023cb5255 100644 --- a/docs/source/releasenotes/0.4.3.rst +++ b/docs/source/releasenotes/0.4.3.rst @@ -40,12 +40,12 @@ Bug Fixes - 3212f5_: Fixed issue with blank line at beginning of XML files. .. _nelsonag: https://github.com/nelsonag -.. _33f29a: https://github.com/mit-crpg/openmc/commit/33f29a -.. _1c472d: https://github.com/mit-crpg/openmc/commit/1c472d -.. _3c6e80: https://github.com/mit-crpg/openmc/commit/3c6e80 -.. _3bd35b: https://github.com/mit-crpg/openmc/commit/3bd35b -.. _0069d5: https://github.com/mit-crpg/openmc/commit/0069d5 -.. _7af2cf: https://github.com/mit-crpg/openmc/commit/7af2cf -.. _460ef1: https://github.com/mit-crpg/openmc/commit/460ef1 -.. _85a60e: https://github.com/mit-crpg/openmc/commit/85a60e -.. _3212f5: https://github.com/mit-crpg/openmc/commit/3212f5 +.. _33f29a: https://github.com/openmc-dev/openmc/commit/33f29a +.. _1c472d: https://github.com/openmc-dev/openmc/commit/1c472d +.. _3c6e80: https://github.com/openmc-dev/openmc/commit/3c6e80 +.. _3bd35b: https://github.com/openmc-dev/openmc/commit/3bd35b +.. _0069d5: https://github.com/openmc-dev/openmc/commit/0069d5 +.. _7af2cf: https://github.com/openmc-dev/openmc/commit/7af2cf +.. _460ef1: https://github.com/openmc-dev/openmc/commit/460ef1 +.. _85a60e: https://github.com/openmc-dev/openmc/commit/85a60e +.. _3212f5: https://github.com/openmc-dev/openmc/commit/3212f5 diff --git a/docs/source/releasenotes/0.4.4.rst b/docs/source/releasenotes/0.4.4.rst index d700e610d2..c5201b30f3 100644 --- a/docs/source/releasenotes/0.4.4.rst +++ b/docs/source/releasenotes/0.4.4.rst @@ -36,8 +36,8 @@ Bug Fixes - 7fd617_: Fixed bug with restart runs in parallel. - dc4a8f_: Fixed bug with fixed source restart runs. -.. _4654ee: https://github.com/mit-crpg/openmc/commit/4654ee -.. _7ee461: https://github.com/mit-crpg/openmc/commit/7ee461 -.. _792eb3: https://github.com/mit-crpg/openmc/commit/792eb3 -.. _7fd617: https://github.com/mit-crpg/openmc/commit/7fd617 -.. _dc4a8f: https://github.com/mit-crpg/openmc/commit/dc4a8f +.. _4654ee: https://github.com/openmc-dev/openmc/commit/4654ee +.. _7ee461: https://github.com/openmc-dev/openmc/commit/7ee461 +.. _792eb3: https://github.com/openmc-dev/openmc/commit/792eb3 +.. _7fd617: https://github.com/openmc-dev/openmc/commit/7fd617 +.. _dc4a8f: https://github.com/openmc-dev/openmc/commit/dc4a8f diff --git a/docs/source/releasenotes/0.5.0.rst b/docs/source/releasenotes/0.5.0.rst index 443216e335..aa906a323b 100644 --- a/docs/source/releasenotes/0.5.0.rst +++ b/docs/source/releasenotes/0.5.0.rst @@ -39,11 +39,11 @@ Bug Fixes - 6f8d9d_: Set default tally labels. - 6a3a5e_: Fix problem with corner-crossing in lattices. -.. _737b90: https://github.com/mit-crpg/openmc/commit/737b90 -.. _a819b4: https://github.com/mit-crpg/openmc/commit/a819b4 -.. _b11696: https://github.com/mit-crpg/openmc/commit/b11696 -.. _2bd46a: https://github.com/mit-crpg/openmc/commit/2bd46a -.. _7a1f08: https://github.com/mit-crpg/openmc/commit/7a1f08 -.. _c0e3ec: https://github.com/mit-crpg/openmc/commit/c0e3ec -.. _6f8d9d: https://github.com/mit-crpg/openmc/commit/6f8d9d -.. _6a3a5e: https://github.com/mit-crpg/openmc/commit/6a3a5e +.. _737b90: https://github.com/openmc-dev/openmc/commit/737b90 +.. _a819b4: https://github.com/openmc-dev/openmc/commit/a819b4 +.. _b11696: https://github.com/openmc-dev/openmc/commit/b11696 +.. _2bd46a: https://github.com/openmc-dev/openmc/commit/2bd46a +.. _7a1f08: https://github.com/openmc-dev/openmc/commit/7a1f08 +.. _c0e3ec: https://github.com/openmc-dev/openmc/commit/c0e3ec +.. _6f8d9d: https://github.com/openmc-dev/openmc/commit/6f8d9d +.. _6a3a5e: https://github.com/openmc-dev/openmc/commit/6a3a5e diff --git a/docs/source/releasenotes/0.5.1.rst b/docs/source/releasenotes/0.5.1.rst index 6fa584fcf0..26a6927c79 100644 --- a/docs/source/releasenotes/0.5.1.rst +++ b/docs/source/releasenotes/0.5.1.rst @@ -36,8 +36,8 @@ Bug Fixes - 63bfd2_: Fix tracklength tallies with cell filter and universes. - 88daf7_: Fix analog tallies with survival biasing. -.. _94103e: https://github.com/mit-crpg/openmc/commit/94103e -.. _e77059: https://github.com/mit-crpg/openmc/commit/e77059 -.. _b0fe88: https://github.com/mit-crpg/openmc/commit/b0fe88 -.. _63bfd2: https://github.com/mit-crpg/openmc/commit/63bfd2 -.. _88daf7: https://github.com/mit-crpg/openmc/commit/88daf7 +.. _94103e: https://github.com/openmc-dev/openmc/commit/94103e +.. _e77059: https://github.com/openmc-dev/openmc/commit/e77059 +.. _b0fe88: https://github.com/openmc-dev/openmc/commit/b0fe88 +.. _63bfd2: https://github.com/openmc-dev/openmc/commit/63bfd2 +.. _88daf7: https://github.com/openmc-dev/openmc/commit/88daf7 diff --git a/docs/source/releasenotes/0.5.2.rst b/docs/source/releasenotes/0.5.2.rst index ba6374ea7c..1a57028953 100644 --- a/docs/source/releasenotes/0.5.2.rst +++ b/docs/source/releasenotes/0.5.2.rst @@ -41,15 +41,15 @@ Bug Fixes - ab0793_: Corrected PETSC_NULL references to their correct types. - 182ebd_: Use analog estimator with energyout filter. -.. _7632f3: https://github.com/mit-crpg/openmc/commit/7632f3 -.. _f85ac4: https://github.com/mit-crpg/openmc/commit/f85ac4 -.. _49c36b: https://github.com/mit-crpg/openmc/commit/49c36b -.. _5ccc78: https://github.com/mit-crpg/openmc/commit/5ccc78 -.. _b1f52f: https://github.com/mit-crpg/openmc/commit/b1f52f -.. _eae7e5: https://github.com/mit-crpg/openmc/commit/eae7e5 -.. _10c1cc: https://github.com/mit-crpg/openmc/commit/10c1cc -.. _afdb50: https://github.com/mit-crpg/openmc/commit/afdb50 -.. _a3c593: https://github.com/mit-crpg/openmc/commit/a3c593 -.. _3a66e3: https://github.com/mit-crpg/openmc/commit/3a66e3 -.. _ab0793: https://github.com/mit-crpg/openmc/commit/ab0793 -.. _182ebd: https://github.com/mit-crpg/openmc/commit/182ebd +.. _7632f3: https://github.com/openmc-dev/openmc/commit/7632f3 +.. _f85ac4: https://github.com/openmc-dev/openmc/commit/f85ac4 +.. _49c36b: https://github.com/openmc-dev/openmc/commit/49c36b +.. _5ccc78: https://github.com/openmc-dev/openmc/commit/5ccc78 +.. _b1f52f: https://github.com/openmc-dev/openmc/commit/b1f52f +.. _eae7e5: https://github.com/openmc-dev/openmc/commit/eae7e5 +.. _10c1cc: https://github.com/openmc-dev/openmc/commit/10c1cc +.. _afdb50: https://github.com/openmc-dev/openmc/commit/afdb50 +.. _a3c593: https://github.com/openmc-dev/openmc/commit/a3c593 +.. _3a66e3: https://github.com/openmc-dev/openmc/commit/3a66e3 +.. _ab0793: https://github.com/openmc-dev/openmc/commit/ab0793 +.. _182ebd: https://github.com/openmc-dev/openmc/commit/182ebd diff --git a/docs/source/releasenotes/0.5.3.rst b/docs/source/releasenotes/0.5.3.rst index a7305da716..7b2878d911 100644 --- a/docs/source/releasenotes/0.5.3.rst +++ b/docs/source/releasenotes/0.5.3.rst @@ -40,8 +40,8 @@ Bug Fixes - c18a6e_: Check for valid secondary mode on S(a,b) tables. - 82c456_: Fix bug where last process could have zero particles. -.. _2b1e8a: https://github.com/mit-crpg/openmc/commit/2b1e8a -.. _5853d2: https://github.com/mit-crpg/openmc/commit/5853d2 -.. _e178c7: https://github.com/mit-crpg/openmc/commit/e178c7 -.. _c18a6e: https://github.com/mit-crpg/openmc/commit/c18a6e -.. _82c456: https://github.com/mit-crpg/openmc/commit/82c456 +.. _2b1e8a: https://github.com/openmc-dev/openmc/commit/2b1e8a +.. _5853d2: https://github.com/openmc-dev/openmc/commit/5853d2 +.. _e178c7: https://github.com/openmc-dev/openmc/commit/e178c7 +.. _c18a6e: https://github.com/openmc-dev/openmc/commit/c18a6e +.. _82c456: https://github.com/openmc-dev/openmc/commit/82c456 diff --git a/docs/source/releasenotes/0.5.4.rst b/docs/source/releasenotes/0.5.4.rst index 36d9ec4d57..ed40d29df5 100644 --- a/docs/source/releasenotes/0.5.4.rst +++ b/docs/source/releasenotes/0.5.4.rst @@ -39,13 +39,13 @@ Bug Fixes - cf567c_: ENDF/B-VI data checked for compatibility - 6b9461_: Fix p_valid sampling inside of sample_energy -.. _32c03c: https://github.com/mit-crpg/openmc/commit/32c03c -.. _c71ef5: https://github.com/mit-crpg/openmc/commit/c71ef5 -.. _8884fb: https://github.com/mit-crpg/openmc/commit/8884fb -.. _b38af0: https://github.com/mit-crpg/openmc/commit/b38af0 -.. _d28750: https://github.com/mit-crpg/openmc/commit/d28750 -.. _cf567c: https://github.com/mit-crpg/openmc/commit/cf567c -.. _6b9461: https://github.com/mit-crpg/openmc/commit/6b9461 +.. _32c03c: https://github.com/openmc-dev/openmc/commit/32c03c +.. _c71ef5: https://github.com/openmc-dev/openmc/commit/c71ef5 +.. _8884fb: https://github.com/openmc-dev/openmc/commit/8884fb +.. _b38af0: https://github.com/openmc-dev/openmc/commit/b38af0 +.. _d28750: https://github.com/openmc-dev/openmc/commit/d28750 +.. _cf567c: https://github.com/openmc-dev/openmc/commit/cf567c +.. _6b9461: https://github.com/openmc-dev/openmc/commit/6b9461 ------------ Contributors diff --git a/docs/source/releasenotes/0.6.0.rst b/docs/source/releasenotes/0.6.0.rst index 63e234583c..8d0d2957be 100644 --- a/docs/source/releasenotes/0.6.0.rst +++ b/docs/source/releasenotes/0.6.0.rst @@ -35,13 +35,13 @@ Bug Fixes - d7a7d0_: Fix bug with specifying xs attribute - 85b3cb_: Fix out-of-bounds error with OpenMP threading -.. _41f7ca: https://github.com/mit-crpg/openmc/commit/41f7ca -.. _038736: https://github.com/mit-crpg/openmc/commit/038736 -.. _46f9e8: https://github.com/mit-crpg/openmc/commit/46f9e8 -.. _d1ca35: https://github.com/mit-crpg/openmc/commit/d1ca35 -.. _0291c0: https://github.com/mit-crpg/openmc/commit/0291c0 -.. _d7a7d0: https://github.com/mit-crpg/openmc/commit/d7a7d0 -.. _85b3cb: https://github.com/mit-crpg/openmc/commit/85b3cb +.. _41f7ca: https://github.com/openmc-dev/openmc/commit/41f7ca +.. _038736: https://github.com/openmc-dev/openmc/commit/038736 +.. _46f9e8: https://github.com/openmc-dev/openmc/commit/46f9e8 +.. _d1ca35: https://github.com/openmc-dev/openmc/commit/d1ca35 +.. _0291c0: https://github.com/openmc-dev/openmc/commit/0291c0 +.. _d7a7d0: https://github.com/openmc-dev/openmc/commit/d7a7d0 +.. _85b3cb: https://github.com/openmc-dev/openmc/commit/85b3cb ------------ Contributors diff --git a/docs/source/releasenotes/0.6.1.rst b/docs/source/releasenotes/0.6.1.rst index a8c59aa519..c0f11ef685 100644 --- a/docs/source/releasenotes/0.6.1.rst +++ b/docs/source/releasenotes/0.6.1.rst @@ -38,16 +38,16 @@ Bug Fixes - 2a95ef_: Prevent segmentation fault on "current" score without mesh filter - 93e482_: Check for negative values in probability tables -.. _03e890: https://github.com/mit-crpg/openmc/commit/03e890 -.. _4439de: https://github.com/mit-crpg/openmc/commit/4439de -.. _5808ed: https://github.com/mit-crpg/openmc/commit/5808ed -.. _2e60c0: https://github.com/mit-crpg/openmc/commit/2e60c0 -.. _3e0870: https://github.com/mit-crpg/openmc/commit/3e0870 -.. _dc4776: https://github.com/mit-crpg/openmc/commit/dc4776 -.. _01178b: https://github.com/mit-crpg/openmc/commit/01178b -.. _62ec43: https://github.com/mit-crpg/openmc/commit/62ec43 -.. _2a95ef: https://github.com/mit-crpg/openmc/commit/2a95ef -.. _93e482: https://github.com/mit-crpg/openmc/commit/93e482 +.. _03e890: https://github.com/openmc-dev/openmc/commit/03e890 +.. _4439de: https://github.com/openmc-dev/openmc/commit/4439de +.. _5808ed: https://github.com/openmc-dev/openmc/commit/5808ed +.. _2e60c0: https://github.com/openmc-dev/openmc/commit/2e60c0 +.. _3e0870: https://github.com/openmc-dev/openmc/commit/3e0870 +.. _dc4776: https://github.com/openmc-dev/openmc/commit/dc4776 +.. _01178b: https://github.com/openmc-dev/openmc/commit/01178b +.. _62ec43: https://github.com/openmc-dev/openmc/commit/62ec43 +.. _2a95ef: https://github.com/openmc-dev/openmc/commit/2a95ef +.. _93e482: https://github.com/openmc-dev/openmc/commit/93e482 ------------ Contributors diff --git a/docs/source/releasenotes/0.6.2.rst b/docs/source/releasenotes/0.6.2.rst index 6fe6c28dac..9a5400cac3 100644 --- a/docs/source/releasenotes/0.6.2.rst +++ b/docs/source/releasenotes/0.6.2.rst @@ -33,10 +33,10 @@ Bug Fixes - e6abb9_: Fix segfault when tallying in a void material - 291b45_: Handle metastable nuclides in NNDC data and multiplicities in MT=5 data -.. _26fb93: https://github.com/mit-crpg/openmc/commit/26fb93 -.. _2f07c0: https://github.com/mit-crpg/openmc/commit/2f07c0 -.. _e6abb9: https://github.com/mit-crpg/openmc/commit/e6abb9 -.. _291b45: https://github.com/mit-crpg/openmc/commit/291b45 +.. _26fb93: https://github.com/openmc-dev/openmc/commit/26fb93 +.. _2f07c0: https://github.com/openmc-dev/openmc/commit/2f07c0 +.. _e6abb9: https://github.com/openmc-dev/openmc/commit/e6abb9 +.. _291b45: https://github.com/openmc-dev/openmc/commit/291b45 ------------ Contributors diff --git a/docs/source/releasenotes/0.7.0.rst b/docs/source/releasenotes/0.7.0.rst index 8f86631ea5..2a3063a92b 100644 --- a/docs/source/releasenotes/0.7.0.rst +++ b/docs/source/releasenotes/0.7.0.rst @@ -40,11 +40,11 @@ Bug Fixes - 6121d9_: Fix bugs related to particle track files - 2f0e89_: Fixes for nuclide specification in tallies -.. _b5f712: https://github.com/mit-crpg/openmc/commit/b5f712 -.. _e6675b: https://github.com/mit-crpg/openmc/commit/e6675b -.. _04e2c1: https://github.com/mit-crpg/openmc/commit/04e2c1 -.. _6121d9: https://github.com/mit-crpg/openmc/commit/6121d9 -.. _2f0e89: https://github.com/mit-crpg/openmc/commit/2f0e89 +.. _b5f712: https://github.com/openmc-dev/openmc/commit/b5f712 +.. _e6675b: https://github.com/openmc-dev/openmc/commit/e6675b +.. _04e2c1: https://github.com/openmc-dev/openmc/commit/04e2c1 +.. _6121d9: https://github.com/openmc-dev/openmc/commit/6121d9 +.. _2f0e89: https://github.com/openmc-dev/openmc/commit/2f0e89 ------------ Contributors diff --git a/docs/source/releasenotes/0.7.1.rst b/docs/source/releasenotes/0.7.1.rst index 5ee2c9f69f..01af742390 100644 --- a/docs/source/releasenotes/0.7.1.rst +++ b/docs/source/releasenotes/0.7.1.rst @@ -62,15 +62,15 @@ Bug Fixes - 441fd4_: Fix bug in kappa-fission score - 7e5974_: Allow fixed source simulations from Python API -.. _299322: https://github.com/mit-crpg/openmc/commit/299322 -.. _d74840: https://github.com/mit-crpg/openmc/commit/d74840 -.. _c29a81: https://github.com/mit-crpg/openmc/commit/c29a81 -.. _3edc23: https://github.com/mit-crpg/openmc/commit/3edc23 -.. _629e3b: https://github.com/mit-crpg/openmc/commit/629e3b -.. _5dbe8b: https://github.com/mit-crpg/openmc/commit/5dbe8b -.. _ff66f4: https://github.com/mit-crpg/openmc/commit/ff66f4 -.. _441fd4: https://github.com/mit-crpg/openmc/commit/441fd4 -.. _7e5974: https://github.com/mit-crpg/openmc/commit/7e5974 +.. _299322: https://github.com/openmc-dev/openmc/commit/299322 +.. _d74840: https://github.com/openmc-dev/openmc/commit/d74840 +.. _c29a81: https://github.com/openmc-dev/openmc/commit/c29a81 +.. _3edc23: https://github.com/openmc-dev/openmc/commit/3edc23 +.. _629e3b: https://github.com/openmc-dev/openmc/commit/629e3b +.. _5dbe8b: https://github.com/openmc-dev/openmc/commit/5dbe8b +.. _ff66f4: https://github.com/openmc-dev/openmc/commit/ff66f4 +.. _441fd4: https://github.com/openmc-dev/openmc/commit/441fd4 +.. _7e5974: https://github.com/openmc-dev/openmc/commit/7e5974 ------------ Contributors diff --git a/docs/source/releasenotes/0.8.0.rst b/docs/source/releasenotes/0.8.0.rst index bd8a89a64a..d4f5a878be 100644 --- a/docs/source/releasenotes/0.8.0.rst +++ b/docs/source/releasenotes/0.8.0.rst @@ -65,17 +65,17 @@ Bug Fixes - 8467ae_: Better threshold for allowable lost particles - 493c6f_: Fix type of return argument for h5pget_driver_f -.. _70daa7: https://github.com/mit-crpg/openmc/commit/70daa7 -.. _40b05f: https://github.com/mit-crpg/openmc/commit/40b05f -.. _9586ed: https://github.com/mit-crpg/openmc/commit/9586ed -.. _a855e8: https://github.com/mit-crpg/openmc/commit/a855e8 -.. _7294a1: https://github.com/mit-crpg/openmc/commit/7294a1 -.. _12f246: https://github.com/mit-crpg/openmc/commit/12f246 -.. _0227f4: https://github.com/mit-crpg/openmc/commit/0227f4 -.. _51deaa: https://github.com/mit-crpg/openmc/commit/51deaa -.. _fed74b: https://github.com/mit-crpg/openmc/commit/fed74b -.. _8467ae: https://github.com/mit-crpg/openmc/commit/8467ae -.. _493c6f: https://github.com/mit-crpg/openmc/commit/493c6f +.. _70daa7: https://github.com/openmc-dev/openmc/commit/70daa7 +.. _40b05f: https://github.com/openmc-dev/openmc/commit/40b05f +.. _9586ed: https://github.com/openmc-dev/openmc/commit/9586ed +.. _a855e8: https://github.com/openmc-dev/openmc/commit/a855e8 +.. _7294a1: https://github.com/openmc-dev/openmc/commit/7294a1 +.. _12f246: https://github.com/openmc-dev/openmc/commit/12f246 +.. _0227f4: https://github.com/openmc-dev/openmc/commit/0227f4 +.. _51deaa: https://github.com/openmc-dev/openmc/commit/51deaa +.. _fed74b: https://github.com/openmc-dev/openmc/commit/fed74b +.. _8467ae: https://github.com/openmc-dev/openmc/commit/8467ae +.. _493c6f: https://github.com/openmc-dev/openmc/commit/493c6f ------------ Contributors diff --git a/docs/source/releasenotes/0.9.0.rst b/docs/source/releasenotes/0.9.0.rst index 478c570962..778a7d1357 100644 --- a/docs/source/releasenotes/0.9.0.rst +++ b/docs/source/releasenotes/0.9.0.rst @@ -111,25 +111,25 @@ Bug Fixes - 489540_: Check for void materials in tracklength tallies - f0214f_: Fixes/improvements to the ARES algorithm -.. _c5df6c: https://github.com/mit-crpg/openmc/commit/c5df6c -.. _1cfa39: https://github.com/mit-crpg/openmc/commit/1cfa39 -.. _335359: https://github.com/mit-crpg/openmc/commit/335359 -.. _17c678: https://github.com/mit-crpg/openmc/commit/17c678 -.. _23ec0b: https://github.com/mit-crpg/openmc/commit/23ec0b -.. _7eefb7: https://github.com/mit-crpg/openmc/commit/7eefb7 -.. _7880d4: https://github.com/mit-crpg/openmc/commit/7880d4 -.. _ad2d9f: https://github.com/mit-crpg/openmc/commit/ad2d9f -.. _59fdca: https://github.com/mit-crpg/openmc/commit/59fdca -.. _9eff5b: https://github.com/mit-crpg/openmc/commit/9eff5b -.. _7848a9: https://github.com/mit-crpg/openmc/commit/7848a9 -.. _f139ce: https://github.com/mit-crpg/openmc/commit/f139ce -.. _b8ddfa: https://github.com/mit-crpg/openmc/commit/b8ddfa -.. _ec3cfb: https://github.com/mit-crpg/openmc/commit/ec3cfb -.. _5e9b06: https://github.com/mit-crpg/openmc/commit/5e9b06 -.. _c39990: https://github.com/mit-crpg/openmc/commit/c39990 -.. _c6b67e: https://github.com/mit-crpg/openmc/commit/c6b67e -.. _489540: https://github.com/mit-crpg/openmc/commit/489540 -.. _f0214f: https://github.com/mit-crpg/openmc/commit/f0214f +.. _c5df6c: https://github.com/openmc-dev/openmc/commit/c5df6c +.. _1cfa39: https://github.com/openmc-dev/openmc/commit/1cfa39 +.. _335359: https://github.com/openmc-dev/openmc/commit/335359 +.. _17c678: https://github.com/openmc-dev/openmc/commit/17c678 +.. _23ec0b: https://github.com/openmc-dev/openmc/commit/23ec0b +.. _7eefb7: https://github.com/openmc-dev/openmc/commit/7eefb7 +.. _7880d4: https://github.com/openmc-dev/openmc/commit/7880d4 +.. _ad2d9f: https://github.com/openmc-dev/openmc/commit/ad2d9f +.. _59fdca: https://github.com/openmc-dev/openmc/commit/59fdca +.. _9eff5b: https://github.com/openmc-dev/openmc/commit/9eff5b +.. _7848a9: https://github.com/openmc-dev/openmc/commit/7848a9 +.. _f139ce: https://github.com/openmc-dev/openmc/commit/f139ce +.. _b8ddfa: https://github.com/openmc-dev/openmc/commit/b8ddfa +.. _ec3cfb: https://github.com/openmc-dev/openmc/commit/ec3cfb +.. _5e9b06: https://github.com/openmc-dev/openmc/commit/5e9b06 +.. _c39990: https://github.com/openmc-dev/openmc/commit/c39990 +.. _c6b67e: https://github.com/openmc-dev/openmc/commit/c6b67e +.. _489540: https://github.com/openmc-dev/openmc/commit/489540 +.. _f0214f: https://github.com/openmc-dev/openmc/commit/f0214f ------------ Contributors diff --git a/docs/source/usersguide/beginners.rst b/docs/source/usersguide/beginners.rst index 38e016d1ed..85d50a2d4e 100644 --- a/docs/source/usersguide/beginners.rst +++ b/docs/source/usersguide/beginners.rst @@ -141,13 +141,13 @@ and `Volume II`_. You may also find it helpful to review the following terms: - `Effective multiplication factor`_ - `Flux`_ -.. _nuclear reactor: http://en.wikipedia.org/wiki/Nuclear_reactor -.. _Monte Carlo: http://en.wikipedia.org/wiki/Monte_Carlo_method -.. _fission: http://en.wikipedia.org/wiki/Nuclear_fission -.. _deterministic: http://en.wikipedia.org/wiki/Deterministic_algorithm -.. _neutron transport: http://en.wikipedia.org/wiki/Neutron_transport -.. _discretization: http://en.wikipedia.org/wiki/Discretization -.. _constructive solid geometry: http://en.wikipedia.org/wiki/Constructive_solid_geometry +.. _nuclear reactor: https://en.wikipedia.org/wiki/Nuclear_reactor +.. _Monte Carlo: https://en.wikipedia.org/wiki/Monte_Carlo_method +.. _fission: https://en.wikipedia.org/wiki/Nuclear_fission +.. _deterministic: https://en.wikipedia.org/wiki/Deterministic_algorithm +.. _neutron transport: https://en.wikipedia.org/wiki/Neutron_transport +.. _discretization: https://en.wikipedia.org/wiki/Discretization +.. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry .. _git: http://git-scm.com/ .. _git tutorials: http://git-scm.com/documentation .. _Reactor Concepts Manual: http://www.tayloredge.com/periodic/trivia/ReactorConcepts.pdf @@ -156,6 +156,6 @@ and `Volume II`_. You may also find it helpful to review the following terms: .. _OpenMC source code: https://github.com/openmc-dev/openmc .. _GitHub: https://github.com/ .. _bug reports: https://github.com/openmc-dev/openmc/issues -.. _Neutron cross section: http://en.wikipedia.org/wiki/Neutron_cross_section +.. _Neutron cross section: https://en.wikipedia.org/wiki/Neutron_cross_section .. _Effective multiplication factor: https://en.wikipedia.org/wiki/Nuclear_chain_reaction#Effective_neutron_multiplication_factor -.. _Flux: http://en.wikipedia.org/wiki/Neutron_flux +.. _Flux: https://en.wikipedia.org/wiki/Neutron_flux diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/cross_sections.rst index 636b44db46..144b1c1527 100644 --- a/docs/source/usersguide/cross_sections.rst +++ b/docs/source/usersguide/cross_sections.rst @@ -16,10 +16,10 @@ recommended to use one of the pregenerated libraries. Alternatively, if you have ACE format data that was produced with NJOY_, such as that distributed with MCNP_ or Serpent_, it can be converted to the HDF5 format using the :ref:`using the Python API `. Several sources provide openly available -ACE data including the `ENDF/B`_, JEFF_, and TENDL_ -libraries. In addition to tabulated cross sections in the HDF5 files, OpenMC -relies on :ref:`windowed multipole ` data to perform -on-the-fly Doppler broadening. +ACE data including the `ENDF/B`_, JEFF_, and TENDL_ libraries as well as the +`LANL Nuclear Data Team `_. In addition to +tabulated cross sections in the HDF5 files, OpenMC relies on :ref:`windowed +multipole ` data to perform on-the-fly Doppler broadening. In multi-group mode, OpenMC utilizes an HDF5-based library format which can be used to describe nuclide- or material-specific quantities. @@ -30,11 +30,11 @@ Environment Variables When :ref:`scripts_openmc` is run, it will look for several environment variables that indicate where cross sections can be found. While the location of -cross sections can also be indicated through the :class:`openmc.Materials` class -(or in the :ref:`materials.xml ` file), if you always use the same -set of cross section data, it is often easier to just set an environment -variable that will be picked up by default every time OpenMC is run. The -following environment variables are used: +cross sections can also be indicated through the +:attr:`openmc.Materials.cross_setion` attribute (or in the :ref:`materials.xml +` file), if you always use the same set of cross section data, it +is often easier to just set an environment variable that will be picked up by +default every time OpenMC is run. The following environment variables are used: :envvar:`OPENMC_CROSS_SECTIONS` Indicates the path to the :ref:`cross_sections.xml ` diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index 5dd98406e6..9e26930986 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -29,8 +29,8 @@ operator class requires a :class:`openmc.Geometry` instance and a op = openmc.deplete.Operator(geom, settings) :mod:`openmc.deplete` supports multiple time-integration methods for determining -material compositions over time. Each method appears as a different function. -For example, :func:`openmc.deplete.integrator.cecm` runs a depletion calculation +material compositions over time. Each method appears as a different class. +For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation using the CE/CM algorithm (deplete over a timestep using the middle-of-step reaction rates). An instance of :class:`openmc.deplete.Operator` is passed to one of these functions along with the power level and timesteps:: @@ -38,7 +38,7 @@ one of these functions along with the power level and timesteps:: power = 1200.0e6 days = 24*60*60 timesteps = [10.0*days, 10.0*days, 10.0*days] - openmc.deplete.cecm(op, power, timesteps) + openmc.deplete.CECMIntegrator(op, power, timesteps).integrate() The coupled transport-depletion problem is executed, and once it is done a ``depletion_results.h5`` file is written. The results can be analyzed using the @@ -46,8 +46,92 @@ The coupled transport-depletion problem is executed, and once it is done a easy retrieval of k-effective, nuclide concentrations, and reaction rates over time:: - results = openmc.deplete.ResultsList("depletion_results.h5") + results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") time, keff = results.get_eigenvalue() Note that the coupling between the transport solver and the transmutation solver happens in-memory rather than by reading/writing files on disk. + +Caveats +======= + +Energy Deposition +----------------- + +The default energy deposition mode, ``"fission-q"``, instructs the +:class:`openmc.deplete.Operator` to normalize reaction rates using the product +of fission reaction rates and fission Q values taken from the depletion chain. +This approach does not consider indirect contributions to energy deposition, +such as neutron heating and energy from secondary photons. In doing this, +the energy deposited during a transport calculation will be lower than expected. +This causes the reaction rates to be over-adjusted to hit the user-specific power, +or power density, leading to an over-depletion of burnable materials. + +There are some remedies. First, the fission Q values can be directly set in a +variety of ways. This requires knowing what the total fission energy release should +be, including indirect components. Some examples are provided below:: + + # use a dictionary of fission_q values + fission_q = {"U235": 202} # energy in MeV + + # create a modified chain and write it to a new file + chain = openmc.deplete.Chain.from_xml("chain.xml", fission_q) + chain.export_to_xml("chain_mod_q.xml") + op = openmc.deplete.Operator(geometry, setting, "chain_mod_q.xml") + + # alternatively, pass the modified fission Q directly to the operator + op = openmc.deplete.Operator(geometry, setting, "chain.xml", + fission_q=fission_q) + + +A more complete way to model the energy deposition is to use the modified heating +reactions described in :ref:`methods_heating`. These values can be used to normalize +reaction rates instead of using the fission reaction rates with:: + + op = openmc.deplete.Operator(geometry, settings, "chain.xml", + energy_mode="energy-deposition") + +These modified heating libraries can be generated by running the latest version +of :meth:`openmc.data.IncidentNeutron.from_njoy`, and will eventually be bundled into +the distributed libraries. + +Local Spectra and Repeated Materials +------------------------------------ + +It is not uncommon to explicitly create a single burnable material across many locations. +From a pure transport perspective, there is nothing wrong with creating a single +3.5 wt.% enriched fuel ``fuel_3``, and placing that fuel in every fuel pin in an assembly +or even full core problem. This certainly expedites the model making process, but can pose +issues with depletion. +Under this setup, :mod:`openmc.deplete` will deplete a single ``fuel_3`` material using +a single set of reaction rates, and produce a single new composition for the next time +step. This can be problematic if the same ``fuel_3`` is used in very different regions +of the problem. + +As an example, consider a full-scale power reactor core with vacuum boundary +conditions, and with fuel pins solely composed of the same ``fuel_3`` material. +The fuel pins towards the center of the problem will surely experience a more intense +neutron flux and greater reaction rates than those towards the edge of the domain. +This indicates that the fuel in the center should be at a more depleted state than +periphery pins, at least for the fist depletion step. +However, without any other instructions, OpenMC will deplete ``fuel_3`` as a single +material, and all of the fuel pins will have an identical composition at the next +transport step. + +This can be countered by instructing the operator to treat repeated instances +of the same material as a unique material definition with:: + + op = openmc.deplete.Operator(geometry, settings, chain_file, + diff_burnable_mats=True) + +For our example problem, this would deplete fuel on the outer region of the problem +with different reaction rates than those in the center. Materials will be depleted +corresponding to their local neutron spectra, and have unique compositions at each +transport step. + + +.. note:: + + This will increase the total memory usage and run time due to an increased + number of tallies and material definitions. + diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index 51daf7b5d1..dc7dd978eb 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -121,11 +121,11 @@ For many regions, a bounding-box can be determined automatically:: While a bounding box can be determined for regions involving half-spaces of spheres, cylinders, and axis-aligned planes, it generally cannot be determined if the region involves cones, non-axis-aligned planes, or other exotic -second-order surfaces. For example, the :func:`openmc.get_hexagonal_prism` +second-order surfaces. For example, the :func:`openmc.model.hexagonal_prism` function returns the interior region of a hexagonal prism; because it is bounded by a :class:`openmc.Plane`, trying to get its bounding box won't work:: - >>> hex = openmc.get_hexagonal_prism() + >>> hex = openmc.model.hexagonal_prism() >>> hex.bounding_box (array([-0.8660254, -inf, -inf]), array([ 0.8660254, inf, inf])) @@ -374,7 +374,7 @@ code would work:: hexlat.universes = [outer_ring, middle_ring, inner_ring] If you need to create a hexagonal boundary (composed of six planar surfaces) for -a hexagonal lattice, :func:`openmc.get_hexagonal_prism` can be used. +a hexagonal lattice, :func:`openmc.model.hexagonal_prism` can be used. .. _usersguide_geom_export: @@ -396,8 +396,15 @@ if needed, lattices, the last step is to create an instance of geom.root_universe = root_univ geom.export_to_xml() -.. _constructive solid geometry: http://en.wikipedia.org/wiki/Constructive_solid_geometry -.. _quadratic surfaces: http://en.wikipedia.org/wiki/Quadric +Note that it's not strictly required to manually create a root universe. You can +also pass a list of cells to the :class:`openmc.Geometry` constructor and it +will handle creating the unverse:: + + geom = openmc.Geometry([cell1, cell2, cell3]) + geom.export_to_xml() + +.. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry +.. _quadratic surfaces: https://en.wikipedia.org/wiki/Quadric -------------------------- Using CAD-based Geometry diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 96b4f5024c..8ca7e0b351 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -17,7 +17,8 @@ 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 `_. +`documentation +`_. Once you have `conda` installed on your system, add the `conda-forge` channel to your configuration with: @@ -444,7 +445,7 @@ as for OpenMC. .. admonition:: Optional :class: note - `mpi4py `_ + `mpi4py `_ mpi4py provides Python bindings to MPI for running distributed-memory parallel runs. This package is needed if you plan on running depletion simulations in parallel using MPI. @@ -483,9 +484,9 @@ Make sure to replace the last string on the second line with the path to the schemas.xml file in your own OpenMC source directory. .. _GNU Emacs: http://www.gnu.org/software/emacs/ -.. _validation: http://en.wikipedia.org/wiki/XML_validation +.. _validation: https://en.wikipedia.org/wiki/XML_validation .. _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/ +.. _Conda: https://docs.conda.io/en/latest/ .. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/usersguide/plots.rst b/docs/source/usersguide/plots.rst index f21d2c8c59..a10f370d6b 100644 --- a/docs/source/usersguide/plots.rst +++ b/docs/source/usersguide/plots.rst @@ -66,7 +66,7 @@ particular cells/materials should be given colors of your choosing:: } Note that colors can be given as RGB tuples or by a string indicating a valid -`SVG color `_. +`SVG color `_. When you're done creating your :class:`openmc.Plot` instances, you need to then assign them to a :class:`openmc.Plots` collection and export it to XML:: diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index 496d18f7fb..1254c4ed5c 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -221,26 +221,11 @@ selected:: settings.electron_treatment = 'led' -.. warning:: - Currently, collision stopping powers used in the TTB approximation come from - the `NIST ESTAR database`_, which provides data for each element calculated - using by default the material density at standard temperature and pressure. - In OpenMC, stopping powers for compounds are calculated from this elemental - data using Bragg's additivity rule. However, this is not a good - approximation --- the collision stopping power is a function of certain - quantities, such as the mean excitation energy and particularly the density - effect correction, that depend on material properties. Data for constituent - elements in a compound cannot simply be summed together, but rather these - quantities should be calculated for the material. This treatment will be - especially poor when the density of a material is different from the - densities used in the NIST data. - .. note:: Some features related to photon transport are not currently implemented, including: * Tallying photon energy deposition. - * Properly accounting for energy deposition in coupled n-p calculations. * Generating a photon source from a neutron calculation that can be used for a later fixed source photon calculation. * Photoneutron reactions. diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index f005b50606..c147858eb1 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -261,7 +261,13 @@ The following tables show all valid scores: | |produced by NJOY's HEATR module while for photons, | | |this is tallied from either direct photon energy | | |deposition (analog estimator) or pre-generated | - | |photon heating number. | + | |photon heating number. See :ref:`methods_heating` | + +----------------------+---------------------------------------------------+ + |heating-local |Total nuclear heating in units of eV per source | + | |particle assuming energy from secondary photons is | + | |deposited locally. Note that this score should only| + | |be used for incident neutrons. See | + | |:ref:`methods_heating`. | +----------------------+---------------------------------------------------+ |kappa-fission |The recoverable energy production rate due to | | |fission. The recoverable energy is defined as the | diff --git a/docs/source/usersguide/volume.rst b/docs/source/usersguide/volume.rst index e4bdbf7155..c040eb2d7f 100644 --- a/docs/source/usersguide/volume.rst +++ b/docs/source/usersguide/volume.rst @@ -37,6 +37,16 @@ arguments are not necessary. For example, Of course, the volumes that you *need* this capability for are often the ones with complex definitions. +A threshold can be applied for the calculation's variance, standard deviation, +or relative error of volume estimates using :meth:`openmc.VolumeCalculation.set_trigger`:: + + vol_calc.set_trigger(1e-05, 'std_dev') + +If a threshold is provided, calculations will be performed iteratively using the +number of samples specified on the calculation until all volume uncertainties are below +the threshold value. If no threshold is provided, the calculation will run the number of +samples specified once and return the result. + Once you have one or more :class:`openmc.VolumeCalculation` objects created, you can then assign then to :attr:`Settings.volume_calculations`:: diff --git a/examples/jupyter/chain_simple.xml b/examples/jupyter/chain_simple.xml new file mode 120000 index 0000000000..8b1b45535c --- /dev/null +++ b/examples/jupyter/chain_simple.xml @@ -0,0 +1 @@ +/home/romano/openmc/tests/chain_simple.xml \ No newline at end of file diff --git a/examples/jupyter/mg-mode-part-iii.ipynb b/examples/jupyter/mg-mode-part-iii.ipynb index 2cf9981889..e953d64d33 100644 --- a/examples/jupyter/mg-mode-part-iii.ipynb +++ b/examples/jupyter/mg-mode-part-iii.ipynb @@ -30,7 +30,6 @@ "\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", - "\n", "import openmc\n", "\n", "%matplotlib inline" @@ -40,7 +39,12 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will be running a rodded 8x8 assembly with Gadolinia fuel pins. Let's create all the elemental data we would need for this case." + "We will be running a rodded 8x8 assembly with Gadolinia fuel pins. Let's start by creating the materials that we will use later.\n", + "\n", + "Material Definition Simplifications:\n", + "\n", + "- This model will be run at room temperature so the NNDC ENDF-B/VII.1 data set can be used but the water density will be representative of a module with around 20% voiding. This water density will be non-physically used in all regions of the problem.\n", + "- Steel is composed of more than just iron, but we will only treat it as such here." ] }, { @@ -48,67 +52,43 @@ "execution_count": 2, "metadata": {}, "outputs": [], - "source": [ - "# Instantiate some elements\n", - "elements = {}\n", - "for elem in ['H', 'O', 'U', 'Zr', 'Gd', 'B', 'C', 'Fe']:\n", - " elements[elem] = openmc.Element(elem)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "With the elements we defined, we will now create the materials we will use later.\n", - "\n", - "Material Definition Simplifications:\n", - "\n", - "- This model will be run at room temperature so the NNDC ENDF-B/VII.1 data set can be used but the water density will be representative of a module with around 20% voiding. This water density will be non-physically used in all regions of the problem.\n", - "- Steel is composed of more than just iron, but we will only treat it as such here.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], "source": [ "materials = {}\n", "\n", "# Fuel\n", "materials['Fuel'] = openmc.Material(name='Fuel')\n", "materials['Fuel'].set_density('g/cm3', 10.32)\n", - "materials['Fuel'].add_element(elements['O'], 2)\n", - "materials['Fuel'].add_element(elements['U'], 1, enrichment=3.)\n", + "materials['Fuel'].add_element('O', 2)\n", + "materials['Fuel'].add_element('U', 1, enrichment=3.)\n", "\n", "# Gadolinia bearing fuel\n", "materials['Gad'] = openmc.Material(name='Gad')\n", "materials['Gad'].set_density('g/cm3', 10.23)\n", - "materials['Gad'].add_element(elements['O'], 2)\n", - "materials['Gad'].add_element(elements['U'], 1, enrichment=3.)\n", - "materials['Gad'].add_element(elements['Gd'], .02)\n", + "materials['Gad'].add_element('O', 2)\n", + "materials['Gad'].add_element('U', 1, enrichment=3.)\n", + "materials['Gad'].add_element('Gd', .02)\n", "\n", "# Zircaloy\n", "materials['Zirc2'] = openmc.Material(name='Zirc2')\n", "materials['Zirc2'].set_density('g/cm3', 6.55)\n", - "materials['Zirc2'].add_element(elements['Zr'], 1)\n", + "materials['Zirc2'].add_element('Zr', 1)\n", "\n", "# Boiling Water\n", "materials['Water'] = openmc.Material(name='Water')\n", "materials['Water'].set_density('g/cm3', 0.6)\n", - "materials['Water'].add_element(elements['H'], 2)\n", - "materials['Water'].add_element(elements['O'], 1)\n", + "materials['Water'].add_element('H', 2)\n", + "materials['Water'].add_element('O', 1)\n", "\n", "# Boron Carbide for the Control Rods\n", "materials['B4C'] = openmc.Material(name='B4C')\n", "materials['B4C'].set_density('g/cm3', 0.7 * 2.52)\n", - "materials['B4C'].add_element(elements['B'], 4)\n", - "materials['B4C'].add_element(elements['C'], 1)\n", + "materials['B4C'].add_element('B', 4)\n", + "materials['B4C'].add_element('C', 1)\n", "\n", "# Steel \n", "materials['Steel'] = openmc.Material(name='Steel')\n", "materials['Steel'].set_density('g/cm3', 7.75)\n", - "materials['Steel'].add_element(elements['Fe'], 1)" + "materials['Steel'].add_element('Fe', 1)" ] }, { @@ -120,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -147,7 +127,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -164,27 +144,27 @@ "surfaces = {}\n", "\n", "# Create boundary planes to surround the geometry\n", - "surfaces['Global x-'] = openmc.XPlane(x0=0., boundary_type='reflective')\n", - "surfaces['Global x+'] = openmc.XPlane(x0=length, boundary_type='reflective')\n", - "surfaces['Global y-'] = openmc.YPlane(y0=0., boundary_type='reflective')\n", - "surfaces['Global y+'] = openmc.YPlane(y0=length, boundary_type='reflective')\n", + "surfaces['Global x-'] = openmc.XPlane(0., boundary_type='reflective')\n", + "surfaces['Global x+'] = openmc.XPlane(length, boundary_type='reflective')\n", + "surfaces['Global y-'] = openmc.YPlane(0., boundary_type='reflective')\n", + "surfaces['Global y+'] = openmc.YPlane(length, boundary_type='reflective')\n", "\n", "# Create cylinders for the fuel and clad\n", - "surfaces['Fuel Radius'] = openmc.ZCylinder(R=fuel_rad)\n", - "surfaces['Clad Radius'] = openmc.ZCylinder(R=clad_rad)\n", + "surfaces['Fuel Radius'] = openmc.ZCylinder(r=fuel_rad)\n", + "surfaces['Clad Radius'] = openmc.ZCylinder(r=clad_rad)\n", "\n", - "surfaces['Assembly x-'] = openmc.XPlane(x0=pin_pitch)\n", - "surfaces['Assembly x+'] = openmc.XPlane(x0=length - pin_pitch)\n", - "surfaces['Assembly y-'] = openmc.YPlane(y0=pin_pitch)\n", - "surfaces['Assembly y+'] = openmc.YPlane(y0=length - pin_pitch)\n", + "surfaces['Assembly x-'] = openmc.XPlane(pin_pitch)\n", + "surfaces['Assembly x+'] = openmc.XPlane(length - pin_pitch)\n", + "surfaces['Assembly y-'] = openmc.YPlane(pin_pitch)\n", + "surfaces['Assembly y+'] = openmc.YPlane(length - pin_pitch)\n", "\n", "# Set surfaces for the control blades\n", - "surfaces['Top Blade y-'] = openmc.YPlane(y0=length - rod_thick)\n", - "surfaces['Top Blade x-'] = openmc.XPlane(x0=pin_pitch)\n", - "surfaces['Top Blade x+'] = openmc.XPlane(x0=rod_span)\n", - "surfaces['Left Blade x+'] = openmc.XPlane(x0=rod_thick)\n", - "surfaces['Left Blade y-'] = openmc.YPlane(y0=length - rod_span)\n", - "surfaces['Left Blade y+'] = openmc.YPlane(y0=9. * pin_pitch)" + "surfaces['Top Blade y-'] = openmc.YPlane(length - rod_thick)\n", + "surfaces['Top Blade x-'] = openmc.XPlane(pin_pitch)\n", + "surfaces['Top Blade x+'] = openmc.XPlane(rod_span)\n", + "surfaces['Left Blade x+'] = openmc.XPlane(rod_thick)\n", + "surfaces['Left Blade y-'] = openmc.YPlane(length - rod_span)\n", + "surfaces['Left Blade y+'] = openmc.YPlane(9. * pin_pitch)" ] }, { @@ -196,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -240,7 +220,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -280,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -319,7 +299,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -353,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -375,27 +355,29 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -422,7 +404,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -444,7 +426,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -481,7 +463,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -499,7 +481,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -520,7 +502,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -540,7 +522,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -572,7 +554,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -602,7 +584,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -632,7 +614,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -652,14 +634,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/mgxs/mgxs.py:4116: UserWarning: The legendre order will be ignored since the scatter format is set to histogram\n", + "/home/romano/openmc/openmc/mgxs/mgxs.py:4144: UserWarning: The legendre order will be ignored since the scatter format is set to histogram\n", " warnings.warn(msg)\n" ] } @@ -679,7 +661,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -698,26 +680,26 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=1.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=1.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=11.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=11.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=21.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=21.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=22.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=22.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=12.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=12.\n", " warn(msg, IDWarning)\n", - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=18.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=18.\n", " warn(msg, IDWarning)\n" ] } @@ -749,57 +731,58 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - " %%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", - " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%%%%%%\n", - " ##################### %%%%%%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%\n", - " ################# %%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%\n", - " ############ %%%%%%%%%%%%%%%\n", - " ######## %%%%%%%%%%%%%%\n", - " %%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%%%%%%\n", + " ##################### %%%%%%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%\n", + " ################# %%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%\n", + " ############ %%%%%%%%%%%%%%%\n", + " ######## %%%%%%%%%%%%%%\n", + " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2018 Massachusetts Institute of Technology\n", + " Copyright | 2011-2019 MIT and OpenMC contributors\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.10.0\n", - " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", - " Date/Time | 2018-04-24 19:15:17\n", - " OpenMP Threads | 8\n", + " Version | 0.11.0-dev\n", + " Git SHA1 | ed7123f4e7ce7b097c4b2bfb0ef5283eaa8afaea\n", + " Date/Time | 2019-10-04 10:54:35\n", + " OpenMP Threads | 4\n", "\n", + " Minimum neutron data temperature: 294.000000 K\n", + " Maximum neutron data temperature: 294.000000 K\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", "\n", "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 0.83880 +/- 0.00101\n", - " k-effective (Track-length) = 0.83917 +/- 0.00118\n", - " k-effective (Absorption) = 0.83859 +/- 0.00104\n", - " Combined k-effective = 0.83879 +/- 0.00086\n", - " Leakage Fraction = 0.00000 +/- 0.00000\n", + " k-effective (Collision) = 0.83866 +/- 0.00102\n", + " k-effective (Track-length) = 0.83799 +/- 0.00118\n", + " k-effective (Absorption) = 0.83968 +/- 0.00103\n", + " Combined k-effective = 0.83900 +/- 0.00085\n", + " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] } @@ -818,7 +801,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -841,7 +824,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 25, "metadata": {}, "outputs": [], "source": [ @@ -858,7 +841,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 26, "metadata": {}, "outputs": [], "source": [ @@ -875,7 +858,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -912,14 +895,14 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", " warn(msg, IDWarning)\n" ] } @@ -947,7 +930,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -967,7 +950,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -990,27 +973,29 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 32, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -1031,45 +1016,44 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - " %%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", - " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%%%%%%\n", - " ##################### %%%%%%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%\n", - " ################# %%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%\n", - " ############ %%%%%%%%%%%%%%%\n", - " ######## %%%%%%%%%%%%%%\n", - " %%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%%%%%%\n", + " ##################### %%%%%%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%\n", + " ################# %%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%\n", + " ############ %%%%%%%%%%%%%%%\n", + " ######## %%%%%%%%%%%%%%\n", + " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2018 Massachusetts Institute of Technology\n", + " Copyright | 2011-2019 MIT and OpenMC contributors\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.10.0\n", - " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", - " Date/Time | 2018-04-24 19:16:03\n", - " OpenMP Threads | 8\n", + " Version | 0.11.0-dev\n", + " Git SHA1 | ed7123f4e7ce7b097c4b2bfb0ef5283eaa8afaea\n", + " Date/Time | 2019-10-04 10:56:58\n", + " OpenMP Threads | 4\n", "\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -1077,11 +1061,11 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 0.82313 +/- 0.00100\n", - " k-effective (Track-length) = 0.82363 +/- 0.00102\n", - " k-effective (Absorption) = 0.82532 +/- 0.00076\n", - " Combined k-effective = 0.82484 +/- 0.00067\n", - " Leakage Fraction = 0.00000 +/- 0.00000\n", + " k-effective (Collision) = 0.82471 +/- 0.00104\n", + " k-effective (Track-length) = 0.82466 +/- 0.00103\n", + " k-effective (Absorption) = 0.82482 +/- 0.00075\n", + " Combined k-effective = 0.82477 +/- 0.00068\n", + " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] } @@ -1100,7 +1084,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -1125,14 +1109,14 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "/home/nelsonag/git/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + "/home/romano/openmc/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", " warn(msg, IDWarning)\n" ] } @@ -1153,45 +1137,44 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "\n", - " %%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", - " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n", " %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", - " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", - " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%%%%%%\n", - " ##################### %%%%%%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%%\n", - " ####################### %%%%%%%%%%%%%%%%%\n", - " ###################### %%%%%%%%%%%%%%%%%\n", - " #################### %%%%%%%%%%%%%%%%%\n", - " ################# %%%%%%%%%%%%%%%%%\n", - " ############### %%%%%%%%%%%%%%%%\n", - " ############ %%%%%%%%%%%%%%%\n", - " ######## %%%%%%%%%%%%%%\n", - " %%%%%%%%%%%\n", + " %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%%%%%%%%%\n", + " ################## %%%%%%%%%%%%%%%%%%%%%%%\n", + " ################### %%%%%%%%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%%%%%%\n", + " ##################### %%%%%%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%%\n", + " ####################### %%%%%%%%%%%%%%%%%\n", + " ###################### %%%%%%%%%%%%%%%%%\n", + " #################### %%%%%%%%%%%%%%%%%\n", + " ################# %%%%%%%%%%%%%%%%%\n", + " ############### %%%%%%%%%%%%%%%%\n", + " ############ %%%%%%%%%%%%%%%\n", + " ######## %%%%%%%%%%%%%%\n", + " %%%%%%%%%%%\n", "\n", " | The OpenMC Monte Carlo Code\n", - " Copyright | 2011-2018 Massachusetts Institute of Technology\n", + " Copyright | 2011-2019 MIT and OpenMC contributors\n", " License | http://openmc.readthedocs.io/en/latest/license.html\n", - " Version | 0.10.0\n", - " Git SHA1 | 6c2d82a4d7dfe10312329d5969568fc03a698416\n", - " Date/Time | 2018-04-24 19:16:12\n", - " OpenMP Threads | 8\n", + " Version | 0.11.0-dev\n", + " Git SHA1 | ed7123f4e7ce7b097c4b2bfb0ef5283eaa8afaea\n", + " Date/Time | 2019-10-04 10:57:24\n", + " OpenMP Threads | 4\n", "\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", @@ -1199,11 +1182,11 @@ "\n", " ============================> RESULTS <============================\n", "\n", - " k-effective (Collision) = 0.83745 +/- 0.00108\n", - " k-effective (Track-length) = 0.83712 +/- 0.00108\n", - " k-effective (Absorption) = 0.83694 +/- 0.00078\n", - " Combined k-effective = 0.83695 +/- 0.00070\n", - " Leakage Fraction = 0.00000 +/- 0.00000\n", + " k-effective (Collision) = 0.83564 +/- 0.00103\n", + " k-effective (Track-length) = 0.83572 +/- 0.00104\n", + " k-effective (Absorption) = 0.83478 +/- 0.00073\n", + " Combined k-effective = 0.83504 +/- 0.00066\n", + " Leakage Fraction = 0.00000 +/- 0.00000\n", "\n" ] } @@ -1225,7 +1208,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -1248,7 +1231,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -1257,8 +1240,8 @@ "angle_mg_keff = angle_mgsp.k_combined\n", "\n", "# Find eigenvalue bias\n", - "iso_bias = 1.0E5 * (ce_keff - iso_mg_keff)\n", - "angle_bias = 1.0E5 * (ce_keff - angle_mg_keff)" + "iso_bias = 1.0e5 * (ce_keff - iso_mg_keff)\n", + "angle_bias = 1.0e5 * (ce_keff - angle_mg_keff)" ] }, { @@ -1270,15 +1253,15 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Isotropic to CE Bias [pcm]: 1394.6\n", - "Angle to CE Bias [pcm]: 183.4\n" + "Isotropic to CE Bias [pcm]: 1423.5\n", + "Angle to CE Bias [pcm]: 396.6\n" ] } ], @@ -1305,19 +1288,21 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 39, "metadata": { "scrolled": false }, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -1363,27 +1348,29 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 41, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -1430,7 +1417,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.7" + "version": "3.7.0" } }, "nbformat": 4, diff --git a/examples/jupyter/pincell_depletion.ipynb b/examples/jupyter/pincell_depletion.ipynb new file mode 100644 index 0000000000..94ec479f62 --- /dev/null +++ b/examples/jupyter/pincell_depletion.ipynb @@ -0,0 +1,996 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook is intended to introduce the reader to the depletion interface contained in OpenMC. It is recommended that you are moderately familiar with building models using the OpenMC Python API. The earlier examples are excellent starting points, as this notebook will not focus heavily on model building.\n", + "\n", + "If you have a real power reactor, the fuel composition is constantly changing as fission events produce energy, remove some fissile isotopes, and produce fission products. Other reactions, like $(n, \\alpha)$ and $(n, \\gamma)$ will alter the composition as well. Furthermore, some nuclides undergo spontaneous decay with widely ranging frequencies. Depletion is the process of modeling this behavior.\n", + "\n", + "In this notebook, we will model a simple fuel pin in an infinite lattice using the Python API. We will then build and examine some of the necessary components for performing depletion analysis. Then, we will use the depletion interface in OpenMC to simulate the fuel pin producing power over several months. Lastly, we will wrap up with some helpful tips to improve the fidelity of depletion simulations." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline\n", + "import math\n", + "import openmc" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Build the Geometry\n", + "\n", + "Much of this section is borrowed from the \"Modeling a Pin-Cell\" example. If you find yourself not understanding some aspects of this section, feel free to refer to that example, as some details may be glossed over for brevity.\n", + "\n", + "First, we will create our fuel, cladding, and water materials to represent a typical PWR." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "fuel = openmc.Material(name=\"uo2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "fuel.add_element(\"U\", 1, percent_type=\"ao\", enrichment=4.25)\n", + "fuel.add_element(\"O\", 2)\n", + "fuel.set_density(\"g/cc\", 10.4)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "clad = openmc.Material(name=\"clad\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "clad.add_element(\"Zr\", 1)\n", + "clad.set_density(\"g/cc\", 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "water = openmc.Material(name=\"water\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "water.add_element(\"O\", 1)\n", + "water.add_element(\"H\", 2)\n", + "water.set_density(\"g/cc\", 1.0)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "water.add_s_alpha_beta(\"c_H_in_H2O\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "materials = openmc.Materials([fuel, clad, water])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here, we are going to use the `openmc.model.pin` function to build our pin cell. The `pin` function anticipates concentric cylinders and materials to fill the inner regions. One additional material is needed than the number of cylinders to cover the domain outside the final ring. \n", + "\n", + "To do this, we define two radii for the outer radius of our fuel pin, and the outer radius of the cladding." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "radii = [0.42, 0.45]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Using these radii, we define concentric `ZCylinder` objects. So long as the cylinders are concentric and increasing in radius, any orientation can be used. We also take advantage of the fact that the `openmc.Materials` object is a subclass of the `list` object to assign materials to the regions defined by the surfaces." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "pin_surfaces = [openmc.ZCylinder(r=r) for r in radii]" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "pin_univ = openmc.model.pin(pin_surfaces, materials)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first material, in our case `fuel`, is placed inside the first cylinder in the inner-most region. The second material, `clad`, fills the space between our cylinders, while `water` is placed outside the last ring. The `pin` function returns an `openmc.Universe` object, and has some additional features we will mention later." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pin_univ.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "bound_box = openmc.rectangular_prism(0.62, 0.62, boundary_type=\"reflective\")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "root_cell = openmc.Cell(fill=pin_univ, region=bound_box)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "root_univ = openmc.Universe(cells=[root_cell])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "geometry = openmc.Geometry(root_univ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly we construct our settings. For the sake of time, a relatively low number of particles will be used." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "settings = openmc.Settings()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "settings.particles = 100\n", + "settings.inactive = 10\n", + "settings.batches = 50" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The depletion interface relies on `OpenMC` to perform the transport simulation and obtain reaction rates and other important information. We then have to create the `xml` input files that `openmc` expects, specifically `geometry.xml`, `settings.xml`, and `materials.xml`." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "geometry.export_to_xml()" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "settings.export_to_xml()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before we write the material file, we must add one bit of information: the volume of our fuel. In order to translate the reaction rates obtained by `openmc` to meaningful units for depletion, we have to normalize them to a correct power. This requires us to know, or be able to calculate, how much fuel is in our problem. Correctly setting the volumes is a critical step, and can lead to incorrect answers, as the fuel is over- or under-depleted due to poor normalization.\n", + "\n", + "For our problem, we can assign the \"volume\" to be the cross-sectional area of our fuel. This is identical to modeling our fuel pin inside a box with height of 1 cm." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "fuel.volume = math.pi * radii[0] ** 2" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "materials.export_to_xml()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Setting up for depletion\n", + "\n", + "The OpenMC depletion interface can be accessed from the `openmc.deplete` module, and has a variety of classes that will help us." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "import openmc.deplete" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to run the depletion calculation we need the following information:\n", + "\n", + "1. Nuclide decay, fission yield, and reaction data\n", + "2. Operational power or power density\n", + "3. Desired depletion schedule\n", + "4. Desired time integration scheme\n", + "\n", + "The first item is necessary to determine the paths by which nuclides transmute over the depletion simulation. This includes spontaneous decay, fission product yield distributions, and nuclides produced through neutron-reactions. For example,\n", + "* Te129 decays to I129 with a half life of ~70 minutes\n", + "* A fission event for U-235 produces fission products like Xe135 according to a distribution\n", + "* For thermal problems, Am241 will produce metastable Am242 about 8% of the time during an $(n,\\gamma)$ reaction. The other 92% of capture reactions will produce ground state Am242\n", + "\n", + "These data are often distributed with other nuclear data, like incident neutron cross sections with ENDF/B-VII.\n", + "OpenMC uses the [`openmc.deplete.Chain`](https://docs.openmc.org/en/latest/pythonapi/generated/openmc.deplete.Chain.html#openmc.deplete.Chain) to collect represent the various decay and transmutation pathways in a single object.\n", + "While a complete `Chain` can be created using nuclear data files, users may prefer to download pre-generated XML-representations instead.\n", + "Such files can be found at https://openmc.org/depletion-chains/ and include full and compressed chains, with capture branching ratios derived using PWR- or SFR-spectra.\n", + "\n", + "For this problem, we will be using a much smaller depletion chain that contains very few nuclides. In a realistic problem, over 1000 isotopes may be included in the depletion chain." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "chain = openmc.deplete.Chain.from_xml(\"./chain_simple.xml\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('I135', 0),\n", + " ('Xe135', 1),\n", + " ('Xe136', 2),\n", + " ('Cs135', 3),\n", + " ('Gd157', 4),\n", + " ('Gd156', 5),\n", + " ('U234', 6),\n", + " ('U235', 7),\n", + " ('U238', 8)])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chain.nuclide_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The primary entry point for depletion is the `openmc.deplete.Operator`. It relies on the `openmc.deplete.Chain` and helper classes to run `openmc`, retrieve and normalize reaction rates, and other perform other tasks. For a thorough description, please see the full API documentation." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will create our Operator using the geometry and settings from above, and our simple chain file. The materials are read in automatically using the `materials.xml` file." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "operator = openmc.deplete.Operator(geometry, settings, \"./chain_simple.xml\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will then simulate our fuel pin operating at linear power of 174 W/cm, or 174 W given a unit height for our problem." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "power = 174" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this problem, we will take depletion step sizes of 30 days, and instruct OpenMC to re-run a transport simulation every 30 days until we have modeled the problem over a six month cycle. The depletion interface expects the time to be given in seconds, so we will have to convert. Note that these values are not cumulative." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "time_steps = [30 * 24 * 60 * 60] * 6" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And lastly, we will use the basic predictor, or forward Euler, time integration scheme. Other, more advanced methods are provided to the user through `openmc.deplete`" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "integrator = openmc.deplete.PredictorIntegrator(operator, time_steps, power)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To perform the simulation, we use the `integrate` method, and let `openmc` take care of the rest." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "integrator.integrate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Processing the outputs\n", + "\n", + "The depletion simulation produces a few output files. First, the statepoint files from each individual transport simulation are written to `openmc_simulation_n.h5`, where `` indicates the current depletion step. Any tallies that we defined in `tallies.xml` will be included in these files across our simulations. We have 7 such files, one for each our of 6 depletion steps and the initial state." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "c5g7.h5\t\t\t openmc_simulation_n2.h5 openmc_simulation_n6.h5\r\n", + "depletion_results.h5\t openmc_simulation_n3.h5 statepoint.50.h5\r\n", + "openmc_simulation_n0.h5 openmc_simulation_n4.h5 summary.h5\r\n", + "openmc_simulation_n1.h5 openmc_simulation_n5.h5\r\n" + ] + } + ], + "source": [ + "!ls *.h5" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `depletion_results.h5` file contains information that is aggregated over all time steps through depletion. This includes the multiplication factor, as well as concentrations. We can process this file using the `openmc.deplete.ResultsList` object" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "results = openmc.deplete.ResultsList.from_hdf5(\"./depletion_results.h5\")" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "time, k = results.get_eigenvalue()" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "time /= (24 * 60 * 60) # convert back to days from seconds" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.76882937, 0.00982155],\n", + " [0.75724033, 0.00827689],\n", + " [0.75532242, 0.01031746],\n", + " [0.74796855, 0.00919769],\n", + " [0.74066561, 0.01157708],\n", + " [0.73184492, 0.00971504],\n", + " [0.7207293 , 0.00703074]])" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "k" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first column of `k` is the value of `k-combined` at each point in our simulation, while the second column contains the associated uncertainty. We can plot this using `matplotlib`" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYcAAAEGCAYAAACO8lkDAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy8QZhcZAAAgAElEQVR4nO3deXgV5dnH8e+djQQCCUJIgBACCgKyiEYhCIgUUYx9FRVwqwso2Fq3Wq1YW1vtW/dXrVoFd1wQXLBiBWllk0UgKApVqSCbLBpQMCIowv3+kYmEnAgEcpYkv8915cpznplzzp1hyC/PnHlmzN0REREpKy7aBYiISOxROIiISAiFg4iIhFA4iIhICIWDiIiEUDiIiEiIhGgXUFUaN27subm50S5DRKRaWbhw4UZ3zyjfX2PCITc3l8LCwmiXISJSrZjZqor6dVhJRERCRGzkYGYJwM3Au0B74HZ332VmBiwEdgWrprl7GzMbBmwGDgMWu/sbkapVRKS2i+TI4VJgrbtPAL4CBgX92UB/d88D+gATg/7z3f1l4GHglxGsU0Sk1otkOHQHFgXtRUABgLuvcfeNQX8BMDloF5nZdcA5wH0RrFNEpNaL5AfSWUBx0C4GMitYpy/w66B9BfDvYN3TKnpBMxsODAfIycmpylpFRGq1SI4cNgGpQTsV2Fh2oZklAbj7jqDrTqAb8AzwSEUv6O6j3T3P3fMyMkLOxBIRkQMUyXCYAnQJ2p2BKWbWpMzyfsDUMo+z3f1bd38YaByhGkVEhMiGwxggx8wGAznAEuDBMssLgEllHr9kZiPM7CLg3ohVKSIiWE252U9eXp4fyCS4IaPmAjBuRH5VlyQiEvPMbGFwtugeNAlORERCKBxERCSEwkFEREIoHEREJITCQUREQigcREQkhMJBRERCKBxERCSEwkFEREIoHEREJITCQUREQigcREQkhMJBRERCKBxERCSEwkFEREIoHEREJITCQUREQigcREQkhMJBRERCKBxERCSEwkFEREIoHEREJERCpN7IzBKAm4F3gfbA7e6+y8wMWAjsClZNA9qW73P3NpGqVUSktovkyOFSYK27TwC+AgYF/dlAf3fPA/oAE3+iT0REIiSS4dAdWBS0FwEFAO6+xt03Bv0FwOSK+iJYp4hIrRfJcMgCioN2MZBZwTp9gWn70QeAmQ03s0IzKywqKqqyQkVEartIhsMmIDVopwIbyy40syQAd9+xt76y3H20u+e5e15GRkZYihYRqY0iGQ5TgC5BuzMwxcyalFneD5ha7jkV9VWpnbscdw/nW4iIVDuRDIcxQI6ZDQZygCXAg2WWFwCTyj2nor4q4+58unErSz8vZsOW7eF6GxGRasdqyl/NeXl5XlhYWKnnuDsn3D2d1V9+S/3kRP53YEdO7dwsTBWKiMQeM1sYnBm6h1o9Cc7MyGyQTMfmaeQ2rsevn3+Pq194jy3bKvyIQ0Sk1qjV4VAqJTGely/L55p+bZn4wXpOvm8ms5dt3PcTRURqKIVDICE+jqv6teGVX/YgJTGe8x6bx62vf8j2HTujXZqISMQpHMrp0iKdf17ZiwvyW/L4rBX8/IFZLFm7JdpliYhElMKhAilJ8dxyWkeeuvgYtmzbwcC/z+bv05exc1fN+PBeRGRfFA570efwJrx5dW9O7JDJnZOXMmTUXFZv+jbaZYmIhJ3CYR8a1kvioXOP4t4hXVi6oZgB989k3ILVmjgnIjWawmE/mBkDu2Yz+ZredM5O53cvL2b4MwvZ+M130S5NRCQsFA6V0Dw9hecu6cZNBe2ZsbSIk++byb8//DzaZYmIVDmFQyXFxRmX9GrNxCt6klE/mUvGFDLylQ/Y+t0P0S6NIaPmMmTU3GiXcdBqys8hUp0pHA7Q4Vn1efXyHlx2/KG8sGANA+5/m4Wrvox2WSIiVULhcBDqJMRzw4B2jBuezy53Bj0yl7ve/Jjvf9i17yeLiMQwhUMVOLbVIUy6qhdnHZ3NQ9OWM/Dvs/nk8+J9P1FEJEYpHKpI/eRE7jyrC4+cfzTrt2zn1Adm8eTsFezSxDkRqYYUDlXs5I5ZTL66F8cd1pg/T/yQC56Yz/ot26JdlohIpSgcwqBJ/WQevzCPvw7sxMJVX3HSvTN57f110S5LRGS/KRzCxMw4t1sOk67qxaFNUrly7HtcMfY9tnyre0WISOxTOIRZbuN6vDgin2tPbMukxes56b6ZzPpE94oQkdimcIiAhPg4rvhZGyb86jjq1Ynn/Mfn8afX/qN7RYhIzFI4RFCn7DRev6IXF/XI5ak5KzlV94oQkRilcIiwlKR4/vQ/RzBm6LEUb9/B6Q/N5qFpy/hhpybOiUjsUDhESe+2Gbx5dW9O6pjFXW8uZfCouazatDXaZYmIADEeDmZWz8wuNrMTol1LOKTXTeLBc7py/9lH8skX3zDg/rcZO1/3ihCR6ItYOJhZgpndamYDzexGM4sL+s3M3jWzwuDrk6C/MTABmOru0yJVZ6SZGacd2Zw3r+7NkS3SGfnKYi55upCiYt0rQkSiJ5Ijh0uBte4+AfgKGBT0ZwP93T0P6ANMDPrvAZ5291URrDFqmqWn8Oywbvzh1A68vWwjJ983kyn/2RDtskSklopkOHQHFgXtRUABgLuvcffSE/8LgMlmlkhJeDQ1szFm9ucI1hk1cXHGsJ6teP2KnmQ2SGb4Mwv53Usf8E0M3CtCRGqXSIZDFlB6qdJiILOCdfoC04AMYKW73+3uFwBnmVl2+ZXNbHjp4aiioqJw1R1xbTPr8+rlx/GrPofy4sI1DLh/JgtW6l4RIhI5kQyHTUBq0E4F9pgmbGZJAO6+A9gMlJ0h9l+gWfkXdPfR7p7n7nkZGRlhKTpakhLiuP7kdowfkY9hDB41lzsm614RIhIZkQyHKUCXoN0ZmGJmTcos7wdMBXD3b4EiM6sfLEsBPolUobEkL/cQ3riqF0PyWvDw9OWc/tBs/qt7RYhImEUyHMYAOWY2GMgBlgAPllleAEwq8/h3wJ/N7FzgGXf/KhxFjRuRz7gR+eF46SqTWieB28/szKMX5PH51yX3injs7U91rwgRCZuESL2Ru+8Cbgoejg++Dy6z/PJy6y8AFkSmuurhxA6ZHNmiNyNf+YC//PMjpn78BXcP6kKz9JRolyYiNUxMT4KTUBn16/DoBXncfkYnFq3ZzEn3zeTV99Zq4pyIVKmIjRyk6pgZZx+bQ/6hjbhm3CKuHreIf330OT/s3EVCvPJeRA6ewqEaa9moHuNH5DNq5qfc+6//YgaN6tXhiVkraJaeTLP0FJqmpdA4NQkzi3a5IlKNKByquYT4OC4/4TCOb5vB2aPn8nnxdm55/cM91klKiKNpWjLN0lJomp5M8yA0dgdIMvWTE6P0E9RsQ0bNBYj5kx5EylM41BAdm6dxRLM03J1HfpHHus3bWLd5G+u3bC9pB9/fWb6JDV9vp/yJTvWTE2gWBEbT9JQgQErCo1laCllpySQl6JCVSG2hcKhhzIxD6iVxSL0kOjZPq3CdH3bu4ovi7/YIjfVl2u9/toUvt35f7nWhcWqdICyS9xh5lPY1Tq1DXJwOX4nUBAqHWighPu7HX+o/Zdv3O1m/ZRvrNm9n3ZZgFBK0//t5MdOXFrGt3G1OE+ONrODwVcnrl4RI8/SSw1nN0lNooMNXItWCwkEqlJIUT+uMVFpnpFa43N3Zsm1HSXhs3sb6LdtYu3l7ECjbmL/iSzZ8vZ2d5Y5fpdZJ2H24Kr30c5Dd7ay05Ej8eCKyDwoHOSBmRnrdJNLrJtGhWYMK19m5yykq/o61QXis37z9x/a6zdv5z7otbPzm+5DnJcQZyYnxPPPOKs48qjl1k7SbikSa/tdJ2MTHlRxmKhkNNKxwne07drKh3Ifmz7yziq3f/cAfXl3C3W8u5dxuOVyYn6tRhUgEKRwkqpIT48ltXI/cxvV+7Ju9bCPuznUnt+Pxt1cwasZyHp35KQWdmzKsZys6Z6dHsWKR2kHhIDHJzDgm9xCOyT2ENV9+y5OzVzK+cA3/WLSOvJYNGdazFf2PyCJeZ0eJhIVOXJeY1+KQuvzx5x2YO7IvNxW0Z8PX2/nlc+9y/F3TeOztTyneviPaJYrUOAoHqTbqJydySa/WzLjuBB4+7yiyGiTzl39+RP5tU7ll4oes+fLbaJcoUmPosJJUO/FxxoBOTRnQqSnvr9nME7NXMGbuSp6as4L+HbIY1qsVeS0b6npSIgdB4SDVWpcW6dx/dlduGNCOMXNX8fy81Uz+zwY6Z6cxrGcrTunUlERdqVak0vS/RmqEpmkp/O7kdswd2ZdbT+/IN9t/4KoXFtHrjmn8ffoyNn8bOp9CRH6awkFqlLpJCfyie0v+/ZvjeeKiPA5tUo87Jy8l/7ap3PTqYpYXfRPtEkWqBR1WkhopLs7o2y6Tvu0y+Wj91zwxawXjF3zGs++spm+7Jgzr2YoehzbS5xKVoMuP1y4aOUiN175pA+4a1IXZN/Tlqp+14f01mznvsXkMuP9txheuYXu5CwiKiMJBapGM+nW45sS2zL6hL3ee2RmA61/6gJ53TOXef/2XouLvolyhSOyoFoeVzKyBu38d7TqkZkhOjGfwMS0YlJfN7GWbeHzWp9z/1ic8PH05px3ZjGG9WtEuq+KLCYrUFhELBzNLAG4G3gXaA7e7+y4rOei7ENgVrJrm7m3MrBEwB4gHxgJ/iFStUjuYGT3bNKZnm8YsL/qGJ2ev4KWFn/Hiws847rBGDOvZij5tm+gGRlIrRXLkcCmw1t0nmFkWMAgYB2QD/d19o5mlArcE618MnObuH0ewRqmlDs1I5S+nd+K3/Q/n+fmrGTNnFUOfKqR1Rj0uPq6VLh0utc5+f+ZgZh0P8r26A4uC9iKgAMDd17j7xqC/AJgctDOA181sejCKEAm79LpJ/KrPYbz9uxO4/+wjSa2TwB9eXUL+bVO5Y/LHbNiyPdolikREZT6QHm9mjczsDDNrcgDvlQUUB+1iILOCdfoC0wDc/XfA4ZQEyZ8rekEzG25mhWZWWFRUdAAliVQsMT6O045szj8uP44XL8snv3UjRs1YTs87pnLVC+/xwWebo12iSFhVZpzcELgW2A6cb2b3uPvsSjx/E1B6z8lUYGPZhWaWBODuP15i0913mtktwFMVvaC7jwZGA+Tl5XlF64gcjL1dOvyY3JJLh5/YQZcOl5qnMuGwAvibu28AMLMhlXyvKUAXYB7QGZhiZk3c/YtgeT9gaunKZlbH3b8DmgDvVPK9RKpc6aXDrzmxDeMWrOGpOSu57Nl3aXFIChf1aMXgvGzqJydGu0yRKlGZw0o3AVPN7Hwza0vFh4X2ZgyQY2aDgRxgCfBgmeUFwCQAM2sFLDSzK4E+wD2VfK9aadyIfM1ejYDylw7PrJ/Mra9/qEuHS42y3yMHd59qZmcCv6LkTKPnKvNG7r6LkoABGB98H1xm+eVl2iuAg/0AXCSsyl86/PFZoZcOd3ddokOqpUqdm+fuHwFXhKkWkWqrS4t0/nZOV0ae0o6n56xi7PySS4fXS4onKy2ZHTt36dLhUq1Uam81s5/t7bFIbdc0LYUbBuy+dPhOd5YXbaXnHVN5aNoyvtqqS4dL9VDZP2Wu3cdjEWH3pcM7N0/j8MxU2mbW5643l5J/+1uMfGUxn3xevO8XEYmig53yqYOpInthZqTXTeKZYd1YuqGYJ2ev4OV3P2Ps/NX0bpvB0ONy6d0mQ5fokJizX+FgZpcCPYBOZvZE0P0SoLkFIvvp8Kz63H5mZ6476XDGzl/NmLmruOjJBRwaXKLjDF2iQ2LIfh1WcvdH3f1iYIm7Dw2+3ghzbSI1UqPUOvy6bxtm/a4v9w05krpJCdwUXKLj9kkfs37LtmiXKKLDSiLRkpQQx+ldm3Pakc0oXPUVT8xaweiZy3n07U85pVNThh6XS9echtEuU2qpyobD0/t4LCKVVP4SHWPmruSF+WuY+P46uuakM/S4VgzomEWCToWVCKrsPIcX9vZYRA5Oi0Pq8vuCDlzVry0vL/yMJ2ev4Iqx79EsLZkLeuRy9jEtSK+bFO0ypRao7DyHs4LvKeEpR0QAUuskcGGPXKZe24fHLsgjt3E9bp/0Mfm3TeWmVxez7Itvol2i1HCVPay01cxGAjvN7Gl3/zwcRUntputD7RYXZ/TrkEm/Dpl8tP5rnpy9gvGFn/HsO6vpc3gGw3q2oudhjXWJDqlylT2IWc/dbwM2AGeZ2UjNkhaJjPZNG3DnWV2Yc0NfrunXliVrv+YXj8+n/70zGTt/Ndt37Ix2iVKDVDYcSkcOWcBLQVA0rvqyROSnNE6tw1X92jD7hhO4Z1AXEuPjGPnKYvJve4u73tTd6qRqVPYD6UnApOCzhzPNLA2YH5bKRGSv6iTEc+bR2ZxxVHPmr/iSx2et4O/TlzNqxqcUdG7KsJ6t6JydHu0ypZo60HkO7Sm5P8P3QO+qK0dEKsvM6Na6Ed1aN2L1pm95as7uu9XltWzI0J6t6N8hU6fCSqUcaDh8SskNe+q5+7gqrEdEDkJOo913q3ux8DOemrOSXz33Ls3TU7iwR0uGHJNDWoruVif7tt9/SphZhzIP/wWcAxxb5RWJyEGrn5zI0J6tmPbbPoz+xdFkN0zhr298TP5tb3HzP5awYuPWaJcoMa4yI4cbzGyEu28Dvgbi3f2WMNUlIlUgPs7of0QW/Y/IYsnaLTw5eyVj569hzDur6Ht4E4b2bEWPQxvpVFgJsdeRg5n9xcwGm1k2MBzob2b3Am8BcyJRoIhUjY7N07hncBdm3XACV/Ztw6I1mznvsXkMuP9txi9Yo1NhZQ/7Gjl8DjQAbgZaAJuBJpTc5GdheEsTkXBoUj+Za05syy/7HMrE99fx+KwVXP/yB9wx+WPO65bD+d1b0qRBcrTLlCjbazi4+wNB8zEAM0un5L4OpwJ/BE4Ja3UiEjbJifEMymvBWUdnM/fTTTwxayUPTFvGwzOW8/POzRjasxUdm6dFu0yJksrOc9gMvBF8iUgNYGb0OLQxPQ5tzMqNW388FfaV99ZybKtDGHpcK07skBntMqvUkFFzAV2qZW902ykR+VFu43r86X+O4JoT2/Ji4RqenL2Sy55dSHbDFOLMyKhfJ9olSoREbFaMmSWY2a1mNtDMbjSzuKDfzOxdMysMvj4p97yXzCw3UnWKCKSlJHJJr9bMuK4Pj5x/FM3SUlj95be8v2Yzo2cu14fXtUAkp0xeCqx19wnAV8CgoD8b6O/ueUAfYGLpE8xsIKA/VUSiJCE+jpM7NmX8Zfkc0awB9eok8Nc3PqbPXdMZO381P+zcFe0SJUwiGQ7dgUVBexFQAODua9x9Y9BfAEwGMLOuwBpgUwRrFJGfkFongXZZ9Rl7aXeapicz8pXF9L93Jq9/sI5duzza5UkVi2Q4ZAHFQbsYqOgTrr7ANDNrCBzm7oV7e0EzG156OKqoqKhqqxWRCuUf2ohXftmDRy/IIzE+jl8//x4/f3AWM/5bhLtCoqaIZDhsAlKDdiqwsexCM0sCcPcdlIwgzjezVykJjNFm1rz8C7r7aHfPc/e8jIyMsBYvIruZGSd2yOSNq3rxf4O7sGXbDi58Yj7nPPoO767+KtrlSRWIZDhMAboE7c7AFDNrUmZ5P2AqgLs/6+6nufvpQd9wd18bwVpFZD/ExxlnHJXNW9cez5//5wiWffENZ/x9DpeOKWTphuJ9v4DErEiGwxggx8wGU3JF1yXAg2WWFwCTIliPSNiNG5FfK86lr5MQz4U9cplx3Qn8tn9b3lm+iZPvn8lvxi1izZffRrs8OQARm+fg7ruAm4KH44Pvg8ssv/wnnndReCsTkapSr04Cv+7bhvO6teSRGct5as5KJn6wjnOPzeHXfdtonkQ1ort/iEiVa1gviZGntGfGdSdw1tEteHbeao6/axp3v7mUr7fviHZ5sh8UDiISNllpydx2Rif+dU1v+rZrwoPTltH7zmmMmqGJdLFO4SAiYdc6I5UHzz2K16/oSZfsdG6bVDKR7vl5q9mhiXQxSeEgIhHTsXkaTw89lheGd6dZejI3TiiZSDfxfU2kizUKBxGJuO6tG/HyL3vw2AV5JMXHccXYkol005d+oYl0MULhICJRYWb0CybS3TukZCLdRU8u4OzR77BwlSbSRZvCQUSiKj7OGNg1m6nX9uGW045gedFWznx4Dpc8rYl00aRwEJGYkJQQxwX5ucy8vg/XnXQ481aUTKS7ZtwiVm/SRLpIUziISEypm5TA5SccxtvXn8Dw3q15Y/F6fvZ/0/njP5bwRfH2aJdXaygcRCQmpddNYuSAkol0g/Ja8Ny81Rx/53TuevNjtmzTRLpwUziISEzLSkvmrwM78e/fHE+/Dpk8NG05ve+cxiMzlrPte02kCxeFg4hUC60a1+OBc7ry+hU96ZqTzu2TPqbP3dN4bt4qTaQLA4WDiFQrHZun8dTFxzJueHeyG9bl9xOWcOL/zeA1TaSrUgoHEamWurVuxEuX5fP4hXkkJ8Zz5dj3OPWBWUzTRLoqoXAQkWrLzPhZ+0z+eWUv7htyJMXf7eDiJxcwZPQ7LFz1ZbTLq9YUDiJS7cXHGad3bc5bv+nDracdwadFWznz4bkMe2oBH63/OtrlVUsKBxGpMZIS4vhFmYl081d+ySl/e5urX3hPE+kqKWJ3ghMRiZTSiXTndcvhkRmf8tScFbz+wXrOOTaHK/oeFu3yqgWFg4jUWOl1k7hhQDsuPi6Xv731CWPnr+alhZ/RsG4iTdNTol1eTNNhJRGp8TIbJPO/wUS6Eztksm7LdhZ/toVZn2yMdmkxS+EgIrVGbuN6/O2crnRoWp+4ODj/8XmMfGUxxbqvdQiFg4jUOvWTE+nULI3hvVvzwoLVnHzf2xpFlBOxcDCzBDO71cwGmtmNZhYX9JuZvWtmhcHXJ0H/IDN72sxmmVnTSNUpIrVDXJxx4ynteemyHtRJiPtxFPHNdz9Eu7SYEMmRw6XAWnefAHwFDAr6s4H+7p4H9AEmmlk88Im7Xwi8BBwTwTpFpBY5umVD3riq14+jiJPunalRBJENh+7AoqC9CCgAcPc17l76L1EATHb3ne6+yMwSgAzgXxGsU0RqmeTE+JBRxI0TavcoIpLhkAWU3vOvGMisYJ2+wDQoOdwEXACcBZxd0Qua2fDSw1FFRUVVX7GI1CplRxFj59fuUUQkw2ETkBq0U4E9triZJQG4+47gu7v7E8CJlARECHcf7e557p6XkZERtsJFpPbQKKJEJMNhCtAlaHcGpphZkzLL+wFTK3je98CSMNcmIrKH2j6KiGQ4jAFyzGwwkEPJL/wHyywvACYBmFkjM3vfzC4ATgZujWCdIlKBcSPyGTciP9plRFRtHkVE7PIZ7r4LuCl4OD74PrjM8svLtDexe5QhIhJVpaOIe6Ys5bFZK5ixtIg7zuxMzzaNo11a2GgSnIjIfkhOjOf3BR146bL8WjGKUDiIiFTC0S0P4Y2renFpr1Y/fhYxe1nN+yxC4SAiUknlRxHnPTaP30dhFDFk1FyGjJobltdWOIiIHKCyo4jna9goQuEgInIQYmUUUdUUDiIiVaCmjSIUDiIiVaQmjSIUDiIiVawmjCIUDiIiYVDdRxEKBxGRMKpoFDGnGowiFA4iImFWfhRxbjUYRSgcREQipHQUcUnP2B9FKBxERCIoOTGem06N/VGEwkFEJApifRShcBARiZJYHkUoHEREoiwWRxEKBxGRGFDRKOKmVxezNUqjCIWDiEgMKTuKeG7eak66LzqjCIWDiEiMKTuKSIqPzigiYveQFhGJFeNG5Ee7hP1SOoq4+82lPD57BdOXFnHnmZ3pcVj4712tkYOISAwrHUW8OCKfxAiOIjRyEBGpBvJyD+GNK3txz5Tdo4jUOgmkpSSG5f0iNnIwswQzu9XMBprZjWYWF/Sbmb1rZoXB1ydB/9lmNtvMlplZj0jVKSISq1KS9hxFfLyhmBUbt4ZlFBHJw0qXAmvdfQLwFTAo6M8G+rt7HtAHmGhmKcBOdz8O+CPwhwjWKSIS00pHEVkNktn4zXd8/vX2Kn+PSIZDd2BR0F4EFAC4+xp3Lz1PqwCYDOwAXg763gM2RbBOEZGYl5IUT8tGdTmyRTqtM1Kr/PUjGQ5ZQHHQLgYyK1inLzDN3X9w911BX2/gzope0MyGlx6OKioqqvKCRURiXWJ8eH6NRzIcNgGl8ZYK7DGrw8ySANx9R5m+1sBqd/+gohd099HunufueRkZGeGpWkSkFopkOEwBugTtzsAUM2tSZnk/YGrpg2BZO3efZGbJ5dYVEZEwimQ4jAFyzGwwkAMsAR4ss7wAmARgZnWBfwB3mtkSYAHwZQRrFRGp1SI2zyH4DOGm4OH44PvgMssvL9P+FqgeUxhFRGogzZAWEZEQCgcREQmhcBARkRAKBxERCaFwEBGREAoHEREJoXAQEZEQCgcREQmhcBARkRAKBxERCaFwEBGREAoHEREJoXAQEZEQCgcREQmhcBARkRARu5+DiIhUrXEjwnfbG40cREQkhMJBRERCKBxERCSEwkFEREIoHEREJITCQUREQigcREQkhMJBRERCKBxERCSEuXu0a6gSZlYErDrApzcGNlZhOeFSXeqE6lOr6qxa1aVOqD61hrvOlu6eUb6zxoTDwTCzQnfPi3Yd+1Jd6oTqU6vqrFrVpU6oPrVGq04dVhIRkRAKBxERCaFwKDE62gXsp+pSJ1SfWlVn1aoudUL1qTUqdeozBxERCaGRQzVmZvWjXUNFYrWufYmVus2sk5nFR7uOfalMndHctj9VZ6z8e5cVS9u0VoeDmSWY2a1mNtDMbjSzmNoeZtbAzMaa2adm9pSVuNnMlpnZR0DM7Nzl6moYi9vVzC4ys/+YWaGZLTezS2Jte5pZN+AdILGi/TNW9tlydYbsp8E6Ud+2ZeusqKZY2Z7la61oX62o/nDWExP/aaPoUmCtu08AvgIGRbme8voDQ4H2wNFAbyAF6Oju7d19XTSLK2VmqZSpCyggNrfr++5+RHBa4PPAZGJse7r7PKAoeFjR/hkT+2y5Osvvp8eW3yeitW3L1sBkgjgAAAO8SURBVPkTNcXE9ixfK6H76uuR3qa1PRy6A4uC9iJKfqnFktfcfZu7fwd8CGwDjgTWmtnQ6Ja2h7bsWVdMbld3f6/Mw2ZAE2Jze5aqaDvG4rYtv59uInSfiAUV1RSL2zNkX3X3DUR4m9b2e0hnAcVBuxjIjGItIdz9ewAzSwY+c/f5wMlm1h54y8wmufv6qBYJuPu7ZesCFhPD29XMDgeWlq87VrZnGT+1f8bUtq1gP10WLIqpbVvRvzcx/jugdF+FiusP5zat7SOHTUBq0E4ldqfSDwFuLn3g7h8BLwFNo1ZRBcrUtZPY3q5nAP8ofRCr25OK989Y3mf32E8hNrdtuZpieXtCuX0VIrdNa3s4TAG6BO3OweOYYmanAG+4+zdm1rLMomRKhvBRF/zFWCoZeI3Y3q7t3H1pBXXHxPYso6L9Myb32fL7aSxu25+oKSa3Zxnt3H0p/GT9YVPbDyuNAW4xs8FADuX+6ok2MzsbuAvYEpzelmlmb1Hyy/dZd98e1QJ3+0sQXK8BzwKziNHtambZwNrg4R51x8L2NLM8IIOSD3kr2j+9gr6o1mlmddlzP30AaB0L27bc9uxdviYzi5nfAeVqfa3cvgoR3l81CU5ERELU9sNKIiJSAYWDiIiEUDiIiEgIhYOIiIRQOIiISAiFg0iMMbM6ZnahmbWNdi1Se9X2eQ4ie2VmU4A5QI+gazYl56GfALzp7icc5OtfBBwHPOTuiwDc/Tsz6wSsCq4SegHwvbv/6WDeS6QyFA4ie/e/7j7DzP4E4O5/NrMZ7v69mZ1cRe8xuzQYyvgmeL+Pg4DqU0XvJbJfdFhJZC/cfUYFfdPNLBf4LUBwL4B/m9l1ZjbHzH5uZnea2fPB8mPN7GIze9TMTv2p9zKzeDMbaWY/B7qF5ycS2T8KB5EDswG4Mmi/D6S7+13ADCDX3a8HegU3vrmWksutfwAcs5fXPBcocveJQGHYKhfZDzqsJHIAguvybAse7gJ+CNrfATuC9k6gDtDc3V/Yj5ftzu4Lv+2sqlpFDoRGDiLh1yj4gBkzG7iX9T5n98giDrBwFybyUxQOIvtgZk0p+aV9jJm1CPoOo+Re2cdScsnnJmbWDGgFHGFmOUA6JXfuuh6YYGYvA5/t5a0eAY43s7uBFkDXcP1MIvuiq7KKRFFwKqu5+5N7WacP0EenskokaeQgEl3LgWZmdnRFC82sA3AEJR9mi0SMRg4iIhJCIwcREQmhcBARkRAKBxERCaFwEBGREAoHEREJoXAQEZEQ/w/PBlt2EYARKQAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pyplot.errorbar(time, k[:, 0], yerr=k[:, 1])\n", + "pyplot.xlabel(\"Time [d]\")\n", + "pyplot.ylabel(\"$k_{eff}\\pm \\sigma$\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Due to the low number of particles selected, we have not only a very high uncertainty, but likely a horrendously poor fission source. This pin cell should have $k>1$, but we can still see the decline over time due to fuel consumption." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then examine concentrations of atoms in each of our materials. This requires knowing the material ID, which can be obtained from the `materials.xml` file." + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "_time, u5 = results.get_atoms(\"1\", \"U235\")\n", + "_time, xe135 = results.get_atoms(\"1\", \"Xe135\")" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pyplot.plot(time, u5, label=\"U235\")\n", + "pyplot.xlabel(\"Time [d]\")\n", + "pyplot.ylabel(\"Number of atoms - U235\");" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pyplot.plot(time, xe135, label=\"Xe135\")\n", + "pyplot.xlabel(\"Time [d]\")\n", + "pyplot.ylabel(\"Number of atoms - Xe135\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also examine reaction rates over time using the `ResultsList`" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "_time, u5_fission = results.get_reaction_rate(\"1\", \"U235\", \"fission\")" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pyplot.plot(time, u5_fission)\n", + "pyplot.xlabel(\"Time [d]\")\n", + "pyplot.ylabel(\"Fission reactions / s\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Helpful tips\n", + "\n", + "Depletion is a tricky task to get correct. Use too short of time steps and you may never get your results due to running many transport simulations. Use long of time steps and you may get incorrect answers. Consider the xenon plot from above. Xenon-135 is a fission product with a thermal absorption cross section on the order of millions of barns, but has a half life of ~9 hours. Taking smaller time steps at the beginning of your simulation to build up some equilibrium in your fission products is highly recommended.\n", + "\n", + "When possible, differentiate materials that reappear in multiple places. If we had built an entire core with the single `fuel` material, every pin would be depleted using the same averaged spectrum and reaction rates which is incorrect. The `Operator` can differentiate these materials using the `diff_burnable_mats` argument, but note that the volumes will be copied from the original material.\n", + "\n", + "Using higher-order integrators, like the `CECMIntegrator`, `EPCRK4Integrator` with a fourth order Runge-Kutta, or the `LEQIIntegrator`, can improve the accuracy of a simulation, or at least allow you to take longer depletion steps between transport simulations with similar accuracy.\n", + "\n", + "Fuel pins with integrated burnable absorbers, like gadolinia, experience strong flux gradients until the absorbers are mostly burned away. This means that the spectrum and magnitude of the flux at the edge of the fuel pin can be vastly different than that in the interior. The helper `pin` function can be used to subdivide regions into equal volume segments, as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "div_surfs_1 = [openmc.ZCylinder(r=1)]\n", + "div_1 = openmc.model.pin(div_surfs_1, [fuel, water], subdivisions={0: 10})" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "div_1.plot(width=(2.0, 2.0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The innermost region has been divided into 10 equal volume regions. We can pass additional arguments to divide multiple regions, except for the region outside the last cylinder." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Register depletion chain\n", + "\n", + "The depletion chain we created can be registered into the OpenMC `cross_sections.xml` file, so we don't have to always pass the `chain_file` argument to the `Operator`. To do this, we create a `DataLibrary` using `openmc.data`. Without any arguments, the `from_xml` method will look for the file located at `OPENMC_CROSS_SECTIONS`. For this example, we will just create a bare library." + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [], + "source": [ + "data_lib = openmc.data.DataLibrary()" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "data_lib.register_file(\"./chain_simple.xml\")" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "data_lib.export_to_xml()" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\r\n", + "\r\n", + " \r\n", + "\r\n" + ] + } + ], + "source": [ + "!cat cross_sections.xml" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This allows us to make an `Operator` simply with the geometry and settings arguments, provided we exported our library to `OPENMC_CROSS_SECTIONS`. For a problem where we built and registered a `Chain` using all the available nuclear data, we might see something like the following." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "new_op = openmc.deplete.Operator(geometry, settings)" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "3820" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(new_op.chain.nuclide_dict)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7', 'He3', 'He4', 'He5']" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[nuc.name for nuc in new_op.chain.nuclides[:10]]" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Ds268',\n", + " 'Ds269',\n", + " 'Ds270',\n", + " 'Ds270_m1',\n", + " 'Ds271',\n", + " 'Ds271_m1',\n", + " 'Ds272',\n", + " 'Ds273',\n", + " 'Ds279_m1',\n", + " 'Rg272']" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[nuc.name for nuc in new_op.chain.nuclides[-10:]]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Choice of depletion step size\n", + "\n", + "A general rule of thumb is to use depletion step sizes around 2 MWd/kgHM, where kgHM is really the initial heavy metal mass in kg. If your problem includes integral burnable absorbers, these typically require shorter time steps at or below 1 MWd/kgHM. These are typically valid for the predictor scheme, as the point of recent schemes is to extend this step size. A good convergence study, where the step size is decreased until some convergence metric is satisfied, is a beneficial exercise.\n", + "\n", + "We can use the `Operator` to determine our maximum step size using this recommendation. The `heavy_metal` attribute returns the mass of initial heavy metal in g, which, using our power, can be used to compute this step size. $$\\frac{2\\,MWd}{kgHM} = \\frac{P\\times\\Delta}{hm_{op}}$$" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5.080339195584719" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "operator.heavy_metal" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "max_step = 2 * operator.heavy_metal / power * 1E3" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"Maximum\" depletion step: 58.4 [d]\n" + ] + } + ], + "source": [ + "print(\"\\\"Maximum\\\" depletion step: {:5.3} [d]\".format(max_step))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Alternatively, if we were provided the power density of our problem, we can provide this directly with `openmc.deplete.PredictorIntegrator(operator, time_steps, power_density=pdens)`. The values of `power` and `power_density` do not have to be scalars. For problems with variable power, we can provide an iterable with the same number of elements as `time_steps`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index d5a0e2234a..013a2469e0 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -61,14 +61,15 @@ op = openmc.deplete.Operator(geometry, settings_file, chain_file, previous_results) # Perform simulation using the predictor algorithm -openmc.deplete.integrator.predictor(op, time_steps, power) +integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) +integrator.integrate() ############################################################################### # Read depletion calculation results ############################################################################### # Open results file -results = openmc.deplete.ResultsList("depletion_results.h5") +results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index 5a018cda10..9933edd48a 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -132,14 +132,15 @@ settings_file.entropy_mesh = entropy_mesh op = openmc.deplete.Operator(geometry, settings_file, chain_file) # Perform simulation using the predictor algorithm -openmc.deplete.integrator.predictor(op, time_steps, power) +integrator = openmc.deplete.PredictorIntegrator(op, time_steps, power) +integrator.integrate() ############################################################################### # Read depletion calculation results ############################################################################### # Open results file -results = openmc.deplete.ResultsList("depletion_results.h5") +results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5") # Obtain K_eff as a function of time time, keff = results.get_eigenvalue() diff --git a/include/openmc/capi.h b/include/openmc/capi.h index dbdf713973..0c0f18baee 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -110,6 +110,7 @@ extern "C" { int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n); int openmc_tally_get_scores(int32_t index, int** scores, int* n); int openmc_tally_get_type(int32_t index, int32_t* type); + int openmc_tally_get_writable(int32_t index, bool* writable); int openmc_tally_reset(int32_t index); int openmc_tally_results(int32_t index, double** ptr, size_t shape_[3]); int openmc_tally_set_active(int32_t index, bool active); @@ -119,6 +120,7 @@ extern "C" { int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides); int openmc_tally_set_scores(int32_t index, int n, const char** scores); int openmc_tally_set_type(int32_t index, const char* type); + int openmc_tally_set_writable(int32_t index, bool writable); int openmc_zernike_filter_get_order(int32_t index, int* order); int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r); int openmc_zernike_filter_set_order(int32_t index, int order); diff --git a/include/openmc/cell.h b/include/openmc/cell.h index db8d60ed48..8a330060c3 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -179,10 +179,10 @@ public: //! \brief Rotational tranfsormation of the filled universe. // - //! The vector is empty if there is no rotation. Otherwise, the first three - //! values are the rotation angles respectively about the x-, y-, and z-, axes - //! in degrees. The next 9 values give the rotation matrix in row-major - //! order. + //! The vector is empty if there is no rotation. Otherwise, the first 9 values + //! give the rotation matrix in row-major order. When the user specifies + //! rotation angles about the x-, y- and z- axes in degrees, these values are + //! also present at the end of the vector, making it of length 12. std::vector rotation_; std::vector offset_; //!< Distribcell offset table @@ -212,7 +212,26 @@ protected: bool contains_complex(Position r, Direction u, int32_t on_surface) const; BoundingBox bounding_box_simple() const; static BoundingBox bounding_box_complex(std::vector rpn); - static void apply_demorgan(std::vector& rpn); + + //! Applies DeMorgan's laws to a section of the RPN + //! \param start Starting point for token modification + //! \param stop Stopping point for token modification + static void apply_demorgan(std::vector::iterator start, + std::vector::iterator stop); + + //! Removes complement operators from the RPN + //! \param rpn The rpn to remove complement operators from. + static void remove_complement_ops(std::vector& rpn); + + //! Returns the beginning position of a parenthesis block (immediately before + //! two surface tokens) in the RPN given a starting position at the end of + //! that block (immediately after two surface tokens) + //! \param start Starting position of the search + //! \param rpn The rpn being searched + static std::vector::iterator + find_left_parenthesis(std::vector::iterator start, + const std::vector& rpn); + }; //============================================================================== diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 394f90740f..d557441179 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -20,7 +20,7 @@ using double_4dvec = std::vector>>>; // OpenMC major, minor, and release numbers constexpr int VERSION_MAJOR {0}; -constexpr int VERSION_MINOR {11}; +constexpr int VERSION_MINOR {12}; constexpr int VERSION_RELEASE {0}; constexpr bool VERSION_DEV {true}; constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; @@ -232,7 +232,7 @@ constexpr int N_XD {204}; constexpr int N_XT {205}; constexpr int N_X3HE {206}; constexpr int N_XA {207}; -constexpr int NEUTRON_HEATING {301}; +constexpr int HEATING {301}; constexpr int DAMAGE_ENERGY {444}; constexpr int COHERENT {502}; constexpr int INCOHERENT {504}; @@ -252,6 +252,7 @@ constexpr int N_A0 {800}; constexpr int N_AC {849}; constexpr int N_2N0 {875}; constexpr int N_2NC {891}; +constexpr int HEATING_LOCAL {901}; constexpr std::array DEPLETION_RX {N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N}; @@ -369,7 +370,6 @@ constexpr int SCORE_INVERSE_VELOCITY {-13}; // flux-weighted inverse velocity constexpr int SCORE_FISS_Q_PROMPT {-14}; // prompt fission Q-value constexpr int SCORE_FISS_Q_RECOV {-15}; // recoverable fission Q-value constexpr int SCORE_DECAY_RATE {-16}; // delayed neutron precursor decay rate -constexpr int SCORE_HEATING {-17}; // nuclear heating (neutron or photon) // Tally map bin finding constexpr int NO_BIN_FOUND {-1}; diff --git a/include/openmc/material.h b/include/openmc/material.h index 652f3db8e1..70c6b38522 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -132,7 +132,7 @@ public: //---------------------------------------------------------------------------- // Data - int32_t id_; //!< Unique ID + int32_t id_ {-1}; //!< Unique ID std::string name_; //!< Name of material std::vector nuclide_; //!< Indices in nuclides vector std::vector element_; //!< Indices in elements vector diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index 6d59826aaf..34782b5e30 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -33,9 +33,10 @@ extern std::unordered_map mesh_map; class Mesh { public: - // Constructors + // Constructors and destructor Mesh() = default; Mesh(pugi::xml_node node); + virtual ~Mesh() = default; // Methods diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h index a936ac0102..a1571ea58e 100644 --- a/include/openmc/nuclide.h +++ b/include/openmc/nuclide.h @@ -93,7 +93,7 @@ public: std::vector urr_data_; std::vector> reactions_; //!< Reactions - std::array reaction_index_; //!< Index of each reaction + std::array reaction_index_; //!< Index of each reaction std::vector index_inelastic_scatter_; private: diff --git a/include/openmc/surface.h b/include/openmc/surface.h index d61ae07cef..5272dab3fb 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -25,6 +25,7 @@ extern "C" const int BC_TRANSMIT; extern "C" const int BC_VACUUM; extern "C" const int BC_REFLECT; extern "C" const int BC_PERIODIC; +extern "C" const int BC_WHITE; //============================================================================== // Global variables @@ -115,6 +116,8 @@ public: //! \return Outgoing direction of the ray virtual Direction reflect(Position r, Direction u) const; + virtual Direction diffuse_reflect(Position r, Direction u) const; + //! Evaluate the equation describing the surface. //! //! Surfaces can be described by some function f(x, y, z) = 0. This member diff --git a/include/openmc/tallies/tally.h b/include/openmc/tallies/tally.h index 2a166738d8..dc7cd8ce27 100644 --- a/include/openmc/tallies/tally.h +++ b/include/openmc/tallies/tally.h @@ -37,6 +37,8 @@ public: void set_active(bool active) { active_ = active; } + void set_writable(bool writable) { writable_ = writable; } + void set_scores(pugi::xml_node node); void set_scores(const std::vector& scores); @@ -55,6 +57,8 @@ public: int32_t n_filter_bins() const {return n_filter_bins_;} + bool writable() const { return writable_;} + //---------------------------------------------------------------------------- // Other methods. @@ -98,6 +102,9 @@ public: //! (e.g. specific cell, specific energy group, etc.) xt::xtensor results_; + //! True if this tally should be written to statepoint files + bool writable_ {true}; + //---------------------------------------------------------------------------- // Miscellaneous public members. diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index a084c18884..cb6a7ce7bd 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -76,11 +76,6 @@ private: std::unique_ptr distribution; //!< Secondary angle-energy distribution }; - //! Upper threshold for incoherent inelastic scattering (usually ~4 eV) - double threshold_inelastic_; - //! Upper threshold for coherent/incoherent elastic scattering - double threshold_elastic_ {0.0}; - // Inelastic scattering data Reaction elastic_; Reaction inelastic_; diff --git a/include/openmc/volume_calc.h b/include/openmc/volume_calc.h index 7ee660d8bd..1b49da814d 100644 --- a/include/openmc/volume_calc.h +++ b/include/openmc/volume_calc.h @@ -2,12 +2,14 @@ #define OPENMC_VOLUME_CALC_H #include "openmc/position.h" +#include "openmc/tallies/trigger.h" #include "pugixml.hpp" #include "xtensor/xtensor.hpp" #include #include +#include namespace openmc { @@ -16,6 +18,7 @@ namespace openmc { //============================================================================== class VolumeCalculation { + public: // Aliases, types struct Result { @@ -23,6 +26,7 @@ public: std::vector nuclides; //!< Index of nuclides std::vector atoms; //!< Number of atoms for each nuclide std::vector uncertainty; //!< Uncertainty on number of atoms + int iterations; //!< Number of iterations needed to obtain the results }; // Results for a single domain // Constructors @@ -44,7 +48,9 @@ public: // Data members int domain_type_; //!< Type of domain (cell, material, etc.) - int n_samples_; //!< Number of samples to use + size_t n_samples_; //!< Number of samples to use + double threshold_ {-1.0}; //!< Error threshold for domain volumes + TriggerMetric trigger_type_ {TriggerMetric::not_active}; //!< Trigger metric for the volume calculation Position lower_left_; //!< Lower-left position of bounding box Position upper_right_; //!< Upper-right position of bounding box std::vector domain_ids_; //!< IDs of domains to find volumes of diff --git a/openmc/__init__.py b/openmc/__init__.py index 1661c4ac47..6af6294227 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -34,4 +34,4 @@ from . import examples # Import a few convencience functions that used to be here from openmc.model import rectangular_prism, hexagonal_prism -__version__ = '0.10.0' +__version__ = '0.12.0-dev' diff --git a/openmc/cell.py b/openmc/cell.py index 3fd70b3455..58e3d48891 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -63,6 +63,10 @@ class Cell(IDManagerMixin): \sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi \sin\theta \sin\psi \\ -\sin\theta & \sin\phi \cos\theta & \cos\phi \cos\theta \end{array} \right ] + + A rotation matrix can also be specified directly by setting this + attribute to a nested list (or 2D numpy array) that specifies each + element of the matrix. rotation_matrix : numpy.ndarray The rotation matrix defined by the angles specified in the :attr:`Cell.rotation` property. @@ -227,21 +231,24 @@ class Cell(IDManagerMixin): @rotation.setter def rotation(self, rotation): - cv.check_type('cell rotation', rotation, Iterable, Real) cv.check_length('cell rotation', rotation, 3) self._rotation = np.asarray(rotation) # Save rotation matrix -- the reason we do this instead of having it be # automatically calculated when the rotation_matrix property is accessed # is so that plotting on a rotated geometry can be done faster. - phi, theta, psi = self.rotation*(-pi/180.) - c3, s3 = cos(phi), sin(phi) - c2, s2 = cos(theta), sin(theta) - c1, s1 = cos(psi), sin(psi) - self._rotation_matrix = np.array([ - [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], - [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], - [-s2, c2*s3, c2*c3]]) + if self._rotation.ndim == 2: + # User specified rotation matrix directly + self._rotation_matrix = self._rotation + else: + phi, theta, psi = self.rotation*(-pi/180.) + c3, s3 = cos(phi), sin(phi) + c2, s2 = cos(theta), sin(theta) + c1, s1 = cos(psi), sin(psi) + self._rotation_matrix = np.array([ + [c1*c2, c1*s2*s3 - c3*s1, s1*s3 + c1*c3*s2], + [c2*s1, c1*c3 + s1*s2*s3, c3*s1*s2 - c1*s3], + [-s2, c2*s3, c2*c3]]) @translation.setter def translation(self, translation): @@ -516,7 +523,7 @@ class Cell(IDManagerMixin): element.set("translation", ' '.join(map(str, self.translation))) if self.rotation is not None: - element.set("rotation", ' '.join(map(str, self.rotation))) + element.set("rotation", ' '.join(map(str, self.rotation.ravel()))) return element diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 445eee7f4a..f4708c3032 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -22,7 +22,7 @@ import numpy as np from scipy import sparse import h5py -import openmc.capi +import openmc.lib from openmc.checkvalue import (check_type, check_length, check_value, check_greater_than, check_less_than) from openmc.exceptions import OpenMCError @@ -700,7 +700,7 @@ class CMFDRun(object): ---------- **kwargs All keyword arguments are passed to - :func:`openmc.capi.run_in_memory`. + :func:`openmc.lib.run_in_memory`. """ with self.run_in_memory(**kwargs): @@ -725,7 +725,7 @@ class CMFDRun(object): Parameters ---------- **kwargs - All keyword arguments passed to :func:`openmc.capi.run_in_memory`. + All keyword arguments passed to :func:`openmc.lib.run_in_memory`. """ # Store intracomm for part of CMFD routine where MPI reduce and @@ -736,7 +736,7 @@ class CMFDRun(object): self._intracomm = MPI.COMM_WORLD # Run and pass arguments to C API run_in_memory function - with openmc.capi.run_in_memory(**kwargs): + with openmc.lib.run_in_memory(**kwargs): self.init() yield self.finalize() @@ -758,7 +758,7 @@ class CMFDRun(object): def init(self): """ Initialize CMFDRun instance by setting up CMFD parameters and - calling :func:`openmc.capi.simulation_init` + calling :func:`openmc.lib.simulation_init` """ # Configure CMFD parameters @@ -767,7 +767,7 @@ class CMFDRun(object): # Create tally objects self._create_cmfd_tally() - if openmc.capi.master(): + if openmc.lib.master(): # Compute and store array indices used to build cross section # arrays self._precompute_array_indices() @@ -780,10 +780,10 @@ class CMFDRun(object): self._initialize_linsolver() # Initialize simulation - openmc.capi.simulation_init() + openmc.lib.simulation_init() # Set cmfd_run variable to True through C API - openmc.capi.settings.cmfd_run = True + openmc.lib.settings.cmfd_run = True def next_batch(self): """ Run next batch for CMFDRun. @@ -799,26 +799,26 @@ class CMFDRun(object): self._cmfd_init_batch() # Run next batch - status = openmc.capi.next_batch() + status = openmc.lib.next_batch() # Perform CMFD calculations self._execute_cmfd() # Write CMFD data to statepoint - if openmc.capi.is_statepoint_batch(): + if openmc.lib.is_statepoint_batch(): self.statepoint_write() return status def finalize(self): """ Finalize simulation by calling - :func:`openmc.capi.simulation_finalize` and print out CMFD timing + :func:`openmc.lib.simulation_finalize` and print out CMFD timing information. """ # Finalize simuation - openmc.capi.simulation_finalize() + openmc.lib.simulation_finalize() - if openmc.capi.master(): + if openmc.lib.master(): # Print out CMFD timing statistics self._write_cmfd_timing_stats() @@ -832,13 +832,13 @@ class CMFDRun(object): """ if filename is None: - batch_str_len = len(str(openmc.capi.settings.batches)) - batch_str = str(openmc.capi.current_batch()).zfill(batch_str_len) + batch_str_len = len(str(openmc.lib.settings.batches)) + batch_str = str(openmc.lib.current_batch()).zfill(batch_str_len) filename = 'statepoint.{}.h5'.format(batch_str) # Call C API statepoint_write to save source distribution with CMFD # feedback - openmc.capi.statepoint_write(filename=filename) + openmc.lib.statepoint_write(filename=filename) # Append CMFD data to statepoint file using h5py self._write_cmfd_statepoint(filename) @@ -852,10 +852,10 @@ class CMFDRun(object): Filename of statepoint """ - if openmc.capi.master(): + if openmc.lib.master(): with h5py.File(filename, 'a') as f: if 'cmfd' not in f: - if openmc.capi.settings.verbosity >= 5: + if openmc.lib.settings.verbosity >= 5: print(' Writing CMFD data to {}...'.format(filename)) sys.stdout.flush() cmfd_group = f.create_group("cmfd") @@ -918,7 +918,7 @@ class CMFDRun(object): args = temp_loss.indptr, len(temp_loss.indptr), \ temp_loss.indices, len(temp_loss.indices), n, \ self._spectral, self._indices, coremap, self._use_all_threads - return openmc.capi._dll.openmc_initialize_linsolver(*args) + return openmc.lib._dll.openmc_initialize_linsolver(*args) def _write_cmfd_output(self): """Write CMFD output to buffer at the end of each batch""" @@ -956,13 +956,13 @@ class CMFDRun(object): def _configure_cmfd(self): """Initialize CMFD parameters and set CMFD input variables""" # Check if restarting simulation from statepoint file - if not openmc.capi.settings.restart_run: + if not openmc.lib.settings.restart_run: # Define all variables necessary for running CMFD self._initialize_cmfd() else: # Reset CMFD parameters from statepoint file - path_statepoint = openmc.capi.settings.path_statepoint + path_statepoint = openmc.lib.settings.path_statepoint self._reset_cmfd(path_statepoint) def _initialize_cmfd(self): @@ -972,7 +972,7 @@ class CMFDRun(object): """ # Print message to user and flush output to stdout - if openmc.capi.settings.verbosity >= 7 and openmc.capi.master(): + if openmc.lib.settings.verbosity >= 7 and openmc.lib.master(): print(' Configuring CMFD parameters for simulation') sys.stdout.flush() @@ -986,7 +986,7 @@ class CMFDRun(object): self._indices[i] = n # Check if in continuous energy mode - if not openmc.capi.settings.run_CE: + if not openmc.lib.settings.run_CE: raise OpenMCError('CMFD must be run in continuous energy mode') # Set number of energy groups @@ -1004,10 +1004,10 @@ class CMFDRun(object): if self._mesh.map is not None: check_length('CMFD coremap', self._mesh.map, np.product(self._indices[0:3])) - if openmc.capi.master(): + if openmc.lib.master(): self._coremap = np.array(self._mesh.map) else: - if openmc.capi.master(): + if openmc.lib.master(): self._coremap = np.ones((np.product(self._indices[0:3])), dtype=int) @@ -1020,7 +1020,7 @@ class CMFDRun(object): self._set_tally_window() # Define all variables that will exist only on master process - if openmc.capi.master(): + if openmc.lib.master(): # Set global albedo if self._mesh.albedo is not None: self._albedo = np.array(self._mesh.albedo) @@ -1062,8 +1062,8 @@ class CMFDRun(object): 'file {}'.format(filename)) else: # Overwrite CMFD values from statepoint - if (openmc.capi.master() and - openmc.capi.settings.verbosity >= 5): + if (openmc.lib.master() and + openmc.lib.settings.verbosity >= 5): print(' Loading CMFD data from {}...'.format(filename)) sys.stdout.flush() cmfd_group = f['cmfd'] @@ -1099,7 +1099,7 @@ class CMFDRun(object): self._mesh.width = cmfd_mesh['width'][()] # Define variables that exist only on master process - if openmc.capi.master(): + if openmc.lib.master(): self._time_cmfd = cmfd_group.attrs['time_cmfd'] self._time_cmfdbuild = cmfd_group.attrs['time_cmfdbuild'] self._time_cmfdsolve = cmfd_group.attrs['time_cmfdsolve'] @@ -1132,7 +1132,7 @@ class CMFDRun(object): """Handles CMFD options at the beginning of each batch""" # Get current batch through C API # Add 1 as next_batch has not been called yet - current_batch = openmc.capi.current_batch() + 1 + current_batch = openmc.lib.current_batch() + 1 # Check to activate CMFD solver and possible feedback if self._solver_begin == current_batch: @@ -1145,18 +1145,18 @@ class CMFDRun(object): def _execute_cmfd(self): """Runs CMFD calculation on master node""" - if openmc.capi.master(): + if openmc.lib.master(): # Start CMFD timer time_start_cmfd = time.time() - if openmc.capi.current_batch() >= self._tally_begin: + if openmc.lib.current_batch() >= self._tally_begin: # Calculate all cross sections based on tally window averages self._compute_xs() # Execute CMFD algorithm if CMFD on for current batch if self._cmfd_on: # Run CMFD on single processor on master - if openmc.capi.master(): + if openmc.lib.master(): # Create CMFD data based on OpenMC tallies self._set_up_cmfd() @@ -1167,7 +1167,7 @@ class CMFDRun(object): self._k_cmfd.append(self._keff) # Check to perform adjoint on last batch - if (openmc.capi.current_batch() == openmc.capi.settings.batches + if (openmc.lib.current_batch() == openmc.lib.settings.batches and self._run_adjoint): self._cmfd_solver_execute(adjoint=True) @@ -1178,7 +1178,7 @@ class CMFDRun(object): self._cmfd_reweight() # Stop CMFD timer - if openmc.capi.master(): + if openmc.lib.master(): time_stop_cmfd = time.time() self._time_cmfd += time_stop_cmfd - time_start_cmfd if self._cmfd_on: @@ -1189,13 +1189,13 @@ class CMFDRun(object): def _cmfd_tally_reset(self): """Resets all CMFD tallies in memory""" # Print message - if (openmc.capi.settings.verbosity >= 6 and openmc.capi.master() and + if (openmc.lib.settings.verbosity >= 6 and openmc.lib.master() and not self._reset_every): print(' CMFD tallies reset') sys.stdout.flush() # Reset CMFD tallies - tallies = openmc.capi.tallies + tallies = openmc.lib.tallies for tally_id in self._tally_ids: tallies[tally_id].reset() @@ -1379,7 +1379,7 @@ class CMFDRun(object): self._cmfd_src = cmfd_src / np.sum(cmfd_src) # Compute entropy - if openmc.capi.settings.entropy_on: + if openmc.lib.settings.entropy_on: # Compute source times log_2(source) source = self._cmfd_src[self._cmfd_src > 0] \ * np.log(self._cmfd_src[self._cmfd_src > 0])/np.log(2) @@ -1403,12 +1403,12 @@ class CMFDRun(object): outside = self._count_bank_sites() # Check and raise error if source sites exist outside of CMFD mesh - if openmc.capi.master() and outside: + if openmc.lib.master() and outside: raise OpenMCError('Source sites outside of the CMFD mesh') # Have master compute weight factors, ignore any zeros in # sourcecounts or cmfd_src - if openmc.capi.master(): + if openmc.lib.master(): # Compute normalization factor norm = np.sum(self._sourcecounts) / np.sum(self._cmfd_src) @@ -1440,13 +1440,13 @@ class CMFDRun(object): self._weightfactors = self._intracomm.bcast( self._weightfactors) - m = openmc.capi.meshes[self._mesh_id] + m = openmc.lib.meshes[self._mesh_id] energy = self._egrid ng = self._indices[3] # Get locations and energies of all particles in source bank - source_xyz = openmc.capi.source_bank()['r'] - source_energies = openmc.capi.source_bank()['E'] + source_xyz = openmc.lib.source_bank()['r'] + source_energies = openmc.lib.source_bank()['E'] # Convert xyz location to the CMFD mesh index mesh_ijk = np.floor((source_xyz - m.lower_left)/m.width).astype(int) @@ -1464,13 +1464,13 @@ class CMFDRun(object): # Determine weight factor of each particle based on its mesh index # and energy bin and updates its weight - openmc.capi.source_bank()['wgt'] *= self._weightfactors[ + openmc.lib.source_bank()['wgt'] *= self._weightfactors[ mesh_ijk[:,0], mesh_ijk[:,1], mesh_ijk[:,2], energy_bins] - if openmc.capi.master() and np.any(source_energies < energy[0]): + if openmc.lib.master() and np.any(source_energies < energy[0]): print(' WARNING: Source point below energy grid') sys.stdout.flush() - if openmc.capi.master() and np.any(source_energies > energy[-1]): + if openmc.lib.master() and np.any(source_energies > energy[-1]): print(' WARNING: Source point above energy grid') sys.stdout.flush() @@ -1484,8 +1484,8 @@ class CMFDRun(object): """ # Initialize variables - m = openmc.capi.meshes[self._mesh_id] - bank = openmc.capi.source_bank() + m = openmc.lib.meshes[self._mesh_id] + bank = openmc.lib.source_bank() energy = self._egrid sites_outside = np.zeros(1, dtype=bool) nxnynz = np.prod(self._indices[0:3]) @@ -1496,8 +1496,8 @@ class CMFDRun(object): count = np.zeros(self._sourcecounts.shape) # Get location and energy of each particle in source bank - source_xyz = openmc.capi.source_bank()['r'] - source_energies = openmc.capi.source_bank()['E'] + source_xyz = openmc.lib.source_bank()['r'] + source_energies = openmc.lib.source_bank()['E'] # Convert xyz location to mesh index and ravel index to scalar mesh_locations = np.floor((source_xyz - m.lower_left) / m.width) @@ -1721,7 +1721,7 @@ class CMFDRun(object): s_o = np.zeros((n,)) # Set initial guess - k_n = openmc.capi.keff()[0] + k_n = openmc.lib.keff()[0] k_o = k_n dw = self._w_shift k_s = k_o + dw @@ -1752,7 +1752,7 @@ class CMFDRun(object): s_o /= k_lo # Compute new flux with C++ solver - innerits = openmc.capi._dll.openmc_run_linsolver(loss.data, s_o, + innerits = openmc.lib._dll.openmc_run_linsolver(loss.data, s_o, phi_n, toli) # Compute new source vector @@ -1824,7 +1824,7 @@ class CMFDRun(object): iconv = kerr < self._cmfd_ktol and serr < self._stol # Print out to user - if self._power_monitor and openmc.capi.master(): + if self._power_monitor and openmc.lib.master(): str1 = ' {:d}:'.format(iter) str2 = 'k-eff: {:0.8f}'.format(k_n) str3 = 'k-error: {:.5e}'.format(kerr) @@ -1865,7 +1865,7 @@ class CMFDRun(object): """ # Update window size for expanding window if necessary - num_cmfd_batches = openmc.capi.current_batch() - self._tally_begin + 1 + num_cmfd_batches = openmc.lib.current_batch() - self._tally_begin + 1 if (self._window_type == 'expanding' and num_cmfd_batches == self._window_size * 2): self._window_size *= 2 @@ -1886,7 +1886,7 @@ class CMFDRun(object): nx, ny, nz, ng = self._indices # Get tallies in-memory - tallies = openmc.capi.tallies + tallies = openmc.lib.tallies # Set conditional numpy array as boolean vector based on coremap is_accel = self._coremap != _CMFD_NOACCEL @@ -2135,7 +2135,7 @@ class CMFDRun(object): num_accel = self._mat_dim # Get openmc k-effective - keff = openmc.capi.keff()[0] + keff = openmc.lib.keff()[0] # Define leakage in each mesh cell and energy group leakage = (((self._current[:,:,:,_CURRENTS['out_right'],:] - @@ -2186,7 +2186,7 @@ class CMFDRun(object): # Allocate dimensions for each mesh cell self._hxyz = np.zeros((nx, ny, nz, 3)) - self._hxyz[:] = openmc.capi.meshes[self._mesh_id].width + self._hxyz[:] = openmc.lib.meshes[self._mesh_id].width # Allocate flux, cross sections and diffusion coefficient self._flux = np.zeros((nx, ny, nz, ng)) @@ -2953,7 +2953,7 @@ class CMFDRun(object): def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = openmc.capi.RegularMesh() + cmfd_mesh = openmc.lib.RegularMesh() # Store id of mesh object self._mesh_id = cmfd_mesh.id # Set dimension and parameters of mesh object @@ -2963,29 +2963,29 @@ class CMFDRun(object): width=self._mesh.width) # Create mesh Filter object, stored internally - mesh_filter = openmc.capi.MeshFilter() + mesh_filter = openmc.lib.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh # Set up energy filters, if applicable if self._energy_filters: # Create Energy Filter object, stored internally - energy_filter = openmc.capi.EnergyFilter() + energy_filter = openmc.lib.EnergyFilter() # Set bins for Energy Filter energy_filter.bins = self._egrid # Create Energy Out Filter object, stored internally - energyout_filter = openmc.capi.EnergyoutFilter() + energyout_filter = openmc.lib.EnergyoutFilter() # Set bins for Energy Filter energyout_filter.bins = self._egrid # Create Mesh Surface Filter object, stored internally - meshsurface_filter = openmc.capi.MeshSurfaceFilter() + meshsurface_filter = openmc.lib.MeshSurfaceFilter() # Set mesh for Mesh Surface Filter meshsurface_filter.mesh = cmfd_mesh # Create Legendre Filter object, stored internally - legendre_filter = openmc.capi.LegendreFilter() + legendre_filter = openmc.lib.LegendreFilter() # Set order for Legendre Filter legendre_filter.order = 1 @@ -2993,7 +2993,7 @@ class CMFDRun(object): n_tallies = 4 self._tally_ids = [] for i in range(n_tallies): - cmfd_tally = openmc.capi.Tally() + cmfd_tally = openmc.lib.Tally() # Set nuclide bins cmfd_tally.nuclides = ['total'] self._tally_ids.append(cmfd_tally.id) diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index 9024b932e4..7420387da1 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -118,7 +118,12 @@ class IncidentNeutron(EqualityMixin): if mt in self.reactions: return self.reactions[mt] else: - raise KeyError('No reaction with MT={}.'.format(mt)) + # Try to create a redundant cross section + mts = self.get_reaction_components(mt) + if len(mts) > 0: + return self._get_redundant_reaction(mt, mts) + else: + raise KeyError('No reaction with MT={}.'.format(mt)) def __repr__(self): return "".format(self.name) @@ -452,7 +457,7 @@ class IncidentNeutron(EqualityMixin): if rx.redundant: photon_rx = any(p.particle == 'photon' for p in rx.products) keep_mts = (4, 16, 103, 104, 105, 106, 107, - 203, 204, 205, 206, 207, 301, 318, 444) + 203, 204, 205, 206, 207, 301, 444, 901) if not (photon_rx or rx.mt in keep_mts): continue @@ -553,20 +558,6 @@ class IncidentNeutron(EqualityMixin): fer_group = group['fission_energy_release'] data.fission_energy = FissionEnergyRelease.from_hdf5(fer_group) - # Rebuild non-fission heating - total_heating = data.reactions.get(301) - fission_heating = data.reactions.get(318) - if total_heating is not None and fission_heating is not None: - non_fission_heating = Reaction(999) - non_fission_heating.redundant = True - for strT, total in total_heating.xs.items(): - fission = fission_heating.xs.get(strT) - if fission is None: - continue - non_fission_heating.xs[strT] = Tabulated1D( - total.x, total.y - fission(total.x)) - data.reactions[999] = non_fission_heating - return data @classmethod @@ -602,6 +593,9 @@ class IncidentNeutron(EqualityMixin): # If mass number hasn't been specified, make an educated guess zaid, xs = ace.name.split('.') + if not xs.endswith('c'): + raise TypeError( + "{} is not a continuous-energy neutron ACE table.".format(ace)) name, element, Z, mass_number, metastable = \ get_metadata(int(zaid), metastable_scheme) @@ -744,7 +738,7 @@ class IncidentNeutron(EqualityMixin): # Instantiate incident neutron data data = cls(name, atomic_number, mass_number, metastable, - atomic_weight_ratio, temperature) + atomic_weight_ratio, [temperature]) if (2, 151) in ev.section: data.resonances = res.Resonances.from_endf(ev) @@ -777,9 +771,10 @@ class IncidentNeutron(EqualityMixin): for mt, rx in data.reactions.items(): if mt in (19, 20, 21, 38): if (5, mt) not in ev.section: - neutron = data.reactions[18].products[0] - rx.products[0].applicability = neutron.applicability - rx.products[0].distribution = neutron.distribution + if rx.products: + neutron = data.reactions[18].products[0] + rx.products[0].applicability = neutron.applicability + rx.products[0].distribution = neutron.distribution # Read fission energy release (requires that we already know nu for # fission) @@ -826,34 +821,6 @@ class IncidentNeutron(EqualityMixin): for table in lib.tables[1:]: data.add_temperature_from_ace(table) - # Add fission energy release data - ev = evaluation if evaluation is not None else Evaluation(filename) - if (1, 458) in ev.section: - data.fission_energy = FissionEnergyRelease.from_endf(ev, data) - # Add 318 fission heating data from heatr - non_fission_heating = Reaction(999) - non_fission_heating.redundant = True - fission_heating = Reaction(318) - - heatr_evals = get_evaluations(kwargs["heatr"]) - for heatr in heatr_evals: - temp = "{}K".format(round(heatr.target["temperature"])) - f318 = StringIO(heatr.section[3, 318]) - get_head_record(f318) - _params, fission_kerma = get_tab1_record(f318) - fission_heating.xs[temp] = fission_kerma - total_heating_xs = data.reactions[301].xs.get(temp) - if total_heating_xs is None: - continue - non_fission_heating.xs[temp] = Tabulated1D( - fission_kerma.x, - total_heating_xs(fission_kerma.x) - fission_kerma.y, - breakpoints=fission_kerma.breakpoints, - interpolation=fission_kerma.interpolation) - - data.reactions[318] = fission_heating - data.reactions[999] = non_fission_heating - # Add 0K elastic scattering cross section if '0K' not in data.energy: pendf = Evaluation(kwargs['pendf']) @@ -863,6 +830,74 @@ class IncidentNeutron(EqualityMixin): data.energy['0K'] = xs.x data[2].xs['0K'] = xs + # Add fission energy release data + ev = evaluation if evaluation is not None else Evaluation(filename) + if (1, 458) in ev.section: + data.fission_energy = f = FissionEnergyRelease.from_endf(ev, data) + else: + f = None + + # For energy deposition, we want to store two different KERMAs: + # one calculated assuming outgoing photons deposit their energy + # locally, and one calculated assuming they carry their energy + # away. This requires two HEATR runs (which make_ace does by + # default). Here, we just need to correct for the fact that NJOY + # uses a fission heating number of h = EFR, whereas we want: + # + # 1) h = EFR + EGP + EGD + EB (for local case) + # 2) h = EFR + EB (for non-local case) + # + # The best way to handle this is to subtract off the fission + # KERMA that NJOY calculates and add back exactly what we want. + + # If NJOY is not run with HEATR at all, skip everything below + if not kwargs["heatr"]: + return data + + # Helper function to get a cross section from an ENDF file on a + # given energy grid + def get_file3_xs(ev, mt, E): + file_obj = StringIO(ev.section[3, mt]) + get_head_record(file_obj) + _, xs = get_tab1_record(file_obj) + return xs(E) + + heating_local = Reaction(901) + heating_local.redundant = True + + heatr_evals = get_evaluations(kwargs["heatr"]) + heatr_local_evals = get_evaluations(kwargs["heatr"] + "_local") + for ev, ev_local in zip(heatr_evals, heatr_local_evals): + temp = "{}K".format(round(ev.target["temperature"])) + + # Get total KERMA (originally from ACE file) and energy grid + kerma = data.reactions[301].xs[temp] + E = kerma.x + + if f is not None: + # Replace fission KERMA with (EFR + EB)*sigma_f + fission = data.reactions[18].xs[temp] + kerma_fission = get_file3_xs(ev, 318, E) + kerma.y = kerma.y - kerma_fission + ( + f.fragments(E) + f.betas(E)) * fission(E) + + # For local KERMA, we first need to get the values from the + # HEATR run with photon energy deposited locally and put + # them on the same energy grid + kerma_local = get_file3_xs(ev_local, 301, E) + + if f is not None: + # When photons deposit their energy locally, we replace the + # fission KERMA with (EFR + EGP + EGD + EB)*sigma_f + kerma_fission_local = get_file3_xs(ev_local, 318, E) + kerma_local = kerma_local - kerma_fission_local + ( + f.fragments(E) + f.prompt_photons(E) + + f.delayed_photons(E) + f.betas(E))*fission(E) + + heating_local.xs[temp] = Tabulated1D(E, kerma_local) + + data.reactions[901] = heating_local + return data def _get_redundant_reaction(self, mt, mts): @@ -881,16 +916,17 @@ class IncidentNeutron(EqualityMixin): Redundant reaction """ - # Get energy grid - strT = self.temperatures[0] - energy = self.energy[strT] rx = Reaction(mt) - xss = [self.reactions[mt_i].xs[strT] for mt_i in mts] - idx = min([xs._threshold_idx if hasattr(xs, '_threshold_idx') - else 0 for xs in xss]) - rx.xs[strT] = Tabulated1D(energy[idx:], Sum(xss)(energy[idx:])) - rx.xs[strT]._threshold_idx = idx + # Get energy grid + for strT in self.temperatures: + energy = self.energy[strT] + xss = [self.reactions[mt_i].xs[strT] for mt_i in mts] + idx = min([xs._threshold_idx if hasattr(xs, '_threshold_idx') + else 0 for xs in xss]) + rx.xs[strT] = Tabulated1D(energy[idx:], Sum(xss)(energy[idx:])) + rx.xs[strT]._threshold_idx = idx + rx.redundant = True return rx diff --git a/openmc/data/njoy.py b/openmc/data/njoy.py index 7993ec4004..0c853102f3 100644 --- a/openmc/data/njoy.py +++ b/openmc/data/njoy.py @@ -10,7 +10,7 @@ from . import endf # For a given MAT number, give a name for the ACE table and a list of ZAID -# identifiers +# identifiers. This is based on Appendix C in the ENDF manual. ThermalTuple = namedtuple('ThermalTuple', ['name', 'zaids', 'nmix']) _THERMAL_DATA = { 1: ThermalTuple('hh2o', [1001], 1), @@ -23,31 +23,77 @@ _THERMAL_DATA = { 11: ThermalTuple('dd2o', [1002], 1), 12: ThermalTuple('parad', [1002], 1), 13: ThermalTuple('orthod', [1002], 1), + 14: ThermalTuple('dice', [1002], 1), 26: ThermalTuple('be', [4009], 1), 27: ThermalTuple('bebeo', [4009], 1), - 31: ThermalTuple('graph', [6000, 6012, 6013], 1), + 28: ThermalTuple('bebe2c', [4009], 1), + 30: ThermalTuple('graph', [6000, 6012, 6013], 1), + 31: ThermalTuple('grph10', [6000, 6012, 6013], 1), + 32: ThermalTuple('grph30', [6000, 6012, 6013], 1), 33: ThermalTuple('lch4', [1001], 1), 34: ThermalTuple('sch4', [1001], 1), + 35: ThermalTuple('sch4p2', [1001], 1), 37: ThermalTuple('hch2', [1001], 1), + 38: ThermalTuple('mesi00', [1001], 1), 39: ThermalTuple('lucite', [1001], 1), 40: ThermalTuple('benz', [1001, 6000, 6012], 2), - 41: ThermalTuple('od2o', [8016, 8017, 8018], 1), + 42: ThermalTuple('tol00', [1001], 1), 43: ThermalTuple('sisic', [14028, 14029, 14030], 1), 44: ThermalTuple('csic', [6000, 6012, 6013], 1), + 45: ThermalTuple('ouo2', [8016, 8017, 8018], 1), 46: ThermalTuple('obeo', [8016, 8017, 8018], 1), 47: ThermalTuple('sio2-a', [8016, 8017, 8018, 14028, 14029, 14030], 3), - 48: ThermalTuple('uuo2', [92238], 1), + 48: ThermalTuple('osap00', [92238], 1), 49: ThermalTuple('sio2-b', [8016, 8017, 8018, 14028, 14029, 14030], 3), 50: ThermalTuple('oice', [8016, 8017, 8018], 1), + 51: ThermalTuple('od2o', [8016, 8017, 8018], 1), 52: ThermalTuple('mg24', [12024], 1), 53: ThermalTuple('al27', [13027], 1), 55: ThermalTuple('yyh2', [39089], 1), 56: ThermalTuple('fe56', [26056], 1), 58: ThermalTuple('zrzrh', [40000, 40090, 40091, 40092, 40094, 40096], 1), - 59: ThermalTuple('cacah2', [20040, 20042, 20043, 20044, 20046, 20048], 1), - 75: ThermalTuple('ouo2', [8016, 8017, 8018], 1), + 59: ThermalTuple('si00', [14028], 1), + 60: ThermalTuple('asap00', [13027], 1), + 71: ThermalTuple('n-un', [7014, 7015], 1), + 72: ThermalTuple('u-un', [92238], 1), + 75: ThermalTuple('uuo2', [8016, 8017, 8018], 1), } + +def _get_thermal_data(ev, mat): + """Return appropriate ThermalTuple, accounting for bugs.""" + + # JEFF assigns MAT=59 to Ca in CaH2 (which is supposed to be silicon). + if ev.info['library'][0] == 'JEFF': + if ev.material == 59: + if 'CaH2' in ''.join(ev.info['description']): + zaids = [20040, 20042, 20043, 20044, 20046, 20048] + return ThermalTuple('cacah2', zaids, 1) + + # Before ENDF/B-VIII.0, crystalline graphite was MAT=31 + if ev.info['library'] != ('ENDF/B', 8, 0): + if ev.material == 31: + return _THERMAL_DATA[30] + + # ENDF/B incorrectly assigns MAT numbers for UO2 + # + # Material | ENDF Manual | VII.0 | VII.1 | VIII.0 + # ---------|-------------|-------|-------|------- + # O in UO2 | 45 | 75 | 75 | 75 + # U in UO2 | 75 | 76 | 48 | 48 + if ev.info['library'][0] == 'ENDF/B': + if ev.material == 75: + return _THERMAL_DATA[45] + version = ev.info['library'][1:] + if version in ((7, 1), (8, 0)) and ev.material == 48: + return _THERMAL_DATA[75] + if version == (7, 0) and ev.material == 76: + return _THERMAL_DATA[75] + + # If not a problematic material, use the dictionary as is + return _THERMAL_DATA[mat] + + _TEMPLATE_RECONR = """ reconr / %%%%%%%%%%%%%%%%%%% Reconstruct XS for neutrons %%%%%%%%%%%%%%%%%%%%%%% {nendf} {npendf} @@ -71,7 +117,14 @@ broadr / %%%%%%%%%%%%%%%%%%%%%%% Doppler broaden XS %%%%%%%%%%%%%%%%%%%%%%%%%%%% _TEMPLATE_HEATR = """ heatr / %%%%%%%%%%%%%%%%%%%%%%%%% Add heating kerma %%%%%%%%%%%%%%%%%%%%%%%%%%%% {nendf} {nheatr_in} {nheatr} / -{mat} 4 / +{mat} 4 0 0 0 / +302 318 402 444 / +""" + +_TEMPLATE_HEATR_LOCAL = """ +heatr / %%%%%%%%%%%%%%%%% Add heating kerma (local photons) %%%%%%%%%%%%%%%%%%%% +{nendf} {nheatr_in} {nheatr_local} / +{mat} 4 0 0 1 / 302 318 402 444 / """ @@ -216,8 +269,7 @@ def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): def make_ace(filename, temperatures=None, acer=True, xsdir=None, output_dir=None, pendf=False, error=0.001, broadr=True, - heatr=True, gaspr=True, purr=True, evaluation=None, - **kwargs): + heatr=True, gaspr=True, purr=True, evaluation=None, **kwargs): """Generate incident neutron ACE file from an ENDF file File names can be passed to @@ -320,7 +372,11 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None, # heatr if heatr: nheatr_in = nlast - nheatr = nheatr_in + 1 + nheatr_local = nheatr_in + 1 + tapeout[nheatr_local] = (output_dir / "heatr_local") if heatr is True \ + else heatr + '_local' + commands += _TEMPLATE_HEATR_LOCAL + nheatr = nheatr_local + 1 tapeout[nheatr] = (output_dir / "heatr") if heatr is True else heatr commands += _TEMPLATE_HEATR nlast = nheatr @@ -434,7 +490,8 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, mat_thermal = ev_thermal.material zsymam_thermal = ev_thermal.target['zsymam'] - data = _THERMAL_DATA[mat_thermal] + # Determine name, isotopes based on MAT number + data = _get_thermal_data(ev_thermal, mat_thermal) zaids = ' '.join(str(zaid) for zaid in data.zaids[:3]) # Determine name of library diff --git a/openmc/data/photon.py b/openmc/data/photon.py index 0d0e527533..3766b697e0 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -549,7 +549,9 @@ class IncidentPhoton(EqualityMixin): ace = get_table(ace_or_filename) # Get atomic number based on name of ACE table - zaid = ace.name.split('.')[0] + zaid, xs = ace.name.split('.') + if not xs.endswith('p'): + raise TypeError("{} is not a photoatomic transport ACE table.".format(ace)) Z = get_metadata(int(zaid))[2] # Read each reaction diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index 696310ccaf..83e773f204 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -54,9 +54,8 @@ REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', 198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 203: '(n,Xp)', 204: '(n,Xd)', 205: '(n,Xt)', 206: '(n,X3He)', 207: '(n,Xa)', 301: 'heating', 444: 'damage-energy', - 318: "fission-heating", 999: "non-fission-heating", 649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)', - 849: '(n,ac)', 891: '(n,2nc)'} + 849: '(n,ac)', 891: '(n,2nc)', 901: 'heating-local'} REACTION_NAME.update({i: '(n,n{})'.format(i - 50) for i in range(50, 91)}) REACTION_NAME.update({i: '(n,p{})'.format(i - 600) for i in range(600, 649)}) REACTION_NAME.update({i: '(n,d{})'.format(i - 650) for i in range(650, 699)}) diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index 06e50c6618..067c0a7034 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -33,10 +33,12 @@ _THERMAL_NAMES = { 'c_Be': ('be', 'be-metal', 'be-met', 'be00'), 'c_BeO': ('beo',), 'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00'), + 'c_Be_in_Be2C': ('bebe2c',), 'c_C6H6': ('benz', 'c6h6'), 'c_C_in_SiC': ('csic', 'c-sic'), 'c_Ca_in_CaH2': ('cah', 'cah00'), 'c_D_in_D2O': ('dd2o', 'd-d2o', 'hwtr', 'hw', 'dhw00'), + 'c_D_in_D2O_ice': ('dice',), 'c_Fe56': ('fe', 'fe56', 'fe-56'), 'c_Graphite': ('graph', 'grph', 'gr', 'gr00'), 'c_Graphite_10p': ('grph10',), @@ -45,6 +47,7 @@ _THERMAL_NAMES = { 'c_H_in_CH2': ('hch2', 'poly', 'pol', 'h-poly', 'pol00'), 'c_H_in_CH4_liquid': ('lch4', 'lmeth'), 'c_H_in_CH4_solid': ('sch4', 'smeth'), + 'c_H_in_CH4_solid_phase_II': ('sch4p2',), 'c_H_in_H2O': ('hh2o', 'h-h2o', 'lwtr', 'lw', 'lw00'), 'c_H_in_H2O_solid': ('hice', 'h-ice', 'ice00'), 'c_H_in_C5O2H8': ('lucite', 'c5o2h8', 'h-luci'), @@ -600,6 +603,8 @@ class ThermalScattering(EqualityMixin): # Get new name that is GND-consistent ace_name, xs = ace.name.split('.') + if not xs.endswith('t'): + raise TypeError("{} is not a thermal scattering ACE table.".format(ace)) name = get_thermal_name(ace_name) # Assign temperature to the running list diff --git a/openmc/deplete/__init__.py b/openmc/deplete/__init__.py index 8c19615ece..b54d7f11d3 100644 --- a/openmc/deplete/__init__.py +++ b/openmc/deplete/__init__.py @@ -4,12 +4,11 @@ openmc.deplete A depletion front-end tool. """ -from sys import exit +import sys +from unittest.mock import Mock from h5py import get_config -from unittest.mock import Mock - from .dummy_comm import DummyCommunicator try: @@ -22,7 +21,7 @@ try: if not get_config().mpi and comm.size > 1: # Raise exception only on process 0 if comm.rank: - exit() + sys.exit() raise RuntimeError( "Need parallel HDF5 installed to perform depletion with MPI" ) @@ -35,7 +34,10 @@ from .nuclide import * from .chain import * from .operator import * from .reaction_rates import * -from .abc import * +from .atom_number import * from .results import * from .results_list import * from .integrators import * +from . import abc +from . import cram +from . import helpers diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index fb638e5c21..462d0f3460 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -5,6 +5,7 @@ to run a full depletion simulation. """ from collections import namedtuple +from collections import defaultdict from collections.abc import Iterable import os from pathlib import Path @@ -17,12 +18,19 @@ from numpy import nonzero, empty, asarray from uncertainties import ufloat from openmc.data import DataLibrary, JOULE_PER_EV -from openmc.capi import MaterialFilter, Tally +from openmc.lib import MaterialFilter, Tally from openmc.checkvalue import check_type, check_greater_than from .results import Results from .chain import Chain from .results_list import ResultsList + +__all__ = [ + "OperatorResult", "TransportOperator", "ReactionRateHelper", + "EnergyHelper", "FissionYieldHelper", "TalliedFissionYieldHelper", + "Integrator", "SIIntegrator", "DepSystemSolver"] + + OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) OperatorResult.__doc__ = """\ Result of applying transport operator @@ -376,14 +384,17 @@ class FissionYieldHelper(ABC): Attributes ---------- - constant_yields : dict of str to :class:`openmc.deplete.FissionYield` + constant_yields : collections.defaultdict Fission yields for all nuclides that only have one set of - fission yield data. Can be accessed as ``{parent: {product: yield}}`` + fission yield data. Dictionary of form ``{str: {str: float}}`` + representing yields for ``{parent: {product: yield}}``. Default + return object is an empty dictionary + """ def __init__(self, chain_nuclides): self._chain_nuclides = {} - self._constant_yields = {} + self._constant_yields = defaultdict(dict) # Get all nuclides with fission yield data for nuc in chain_nuclides: @@ -407,14 +418,16 @@ class FissionYieldHelper(ABC): Parameters ---------- local_mat_index : int - Index for material tracked on this process that - exists in :attr:`local_mat_index` and fits within - the first axis in :attr:`results` + Index for the material with requested fission yields. + Should correspond to the material represented in + ``mat_indexes[local_mat_index]`` during + :meth:`generate_tallies`. Returns ------- - library : dict - Dictionary of ``{parent: {product: fyield}}`` + library : collections.abc.Mapping + Dictionary-like object mapping ``{str: {str: float}``. + This reflects fission yields for ``{parent: {product: fyield}}``. """ @staticmethod @@ -438,7 +451,7 @@ class FissionYieldHelper(ABC): Parameters ---------- materials : iterable of C-API materials - Materials to be used in :class:`openmc.capi.MaterialFilter` + Materials to be used in :class:`openmc.lib.MaterialFilter` mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the @@ -520,8 +533,8 @@ class TalliedFissionYieldHelper(FissionYieldHelper): Parameters ---------- - materials : iterable of :class:`openmc.capi.Material` - Materials to be used in :class:`openmc.capi.MaterialFilter` + materials : iterable of :class:`openmc.lib.Material` + Materials to be used in :class:`openmc.lib.MaterialFilter` mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the @@ -533,6 +546,7 @@ class TalliedFissionYieldHelper(FissionYieldHelper): # Tally group-wise fission reaction rates self._fission_rate_tally = Tally() + self._fission_rate_tally.writable = False self._fission_rate_tally.scores = ['fission'] self._fission_rate_tally.filters = [MaterialFilter(materials)] @@ -842,3 +856,40 @@ class SIIntegrator(Integrator): Results.save(self.operator, [conc], [res_list[-1]], [t, t], p, self._i_res + len(self), proc_time) self.operator.write_bos_data(self._i_res + len(self)) + + +class DepSystemSolver(ABC): + r"""Abstract class for solving depletion equations + + Responsible for solving + + .. math:: + + \frac{\partial \vec{N}}{\partial t} = \bar{A}\vec{N}(t), + + for :math:`0< t\leq t +\Delta t`, given :math:`\vec{N}(0) = \vec{N}_0` + + """ + + @abstractmethod + def __call__(self, A, n0, dt): + """Solve the linear system of equations for depletion + + Parameters + ---------- + A : scipy.sparse.csr_matrix + Sparse transmutation matrix ``A[j, i]`` desribing rates at + which isotope ``i`` transmutes to isotope ``j`` + n0 : numpy.ndarray + Initial compositions, typically given in number of atoms in some + material or an atom density + dt : float + Time [s] of the specific interval to be solved + + Returns + ------- + numpy.ndarray + Final compositions after ``dt``. Should be of identical shape + to ``n0``. + + """ diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index c2b5b75b31..a2f7db8184 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -45,6 +45,8 @@ _REACTIONS = [ ] +__all__ = ["Chain"] + def replace_missing(product, decay_data): """Replace missing product with suitable decay daughter. @@ -398,7 +400,7 @@ class Chain(object): clean_indentation(root_elem) tree.write(str(filename), encoding='utf-8') - def get_thermal_fission_yields(self): + def get_default_fission_yields(self): """Return fission yields at lowest incident neutron energy Used as the default set of fission yields for :meth:`form_matrix` @@ -412,7 +414,7 @@ class Chain(object): names of nuclides with yield data and ``f_yield`` is a float for the fission yield. """ - out = {} + out = defaultdict(dict) for nuc in self.nuclides: if nuc.yield_data is None: continue @@ -440,13 +442,13 @@ class Chain(object): See Also -------- - :meth:`get_thermal_fission_yields` + :meth:`get_default_fission_yields` """ matrix = defaultdict(float) reactions = set() if fission_yields is None: - fission_yields = self.get_thermal_fission_yields() + fission_yields = self.get_default_fission_yields() for i, nuc in enumerate(self.nuclides): @@ -721,7 +723,7 @@ class Chain(object): @property def fission_yields(self): if self._fission_yields is None: - self._fission_yields = [self.get_thermal_fission_yields()] + self._fission_yields = [self.get_default_fission_yields()] return self._fission_yields @fission_yields.setter diff --git a/openmc/deplete/cram.py b/openmc/deplete/cram.py index 67a455cb35..7dad820a9c 100644 --- a/openmc/deplete/cram.py +++ b/openmc/deplete/cram.py @@ -3,6 +3,7 @@ Implements two different forms of CRAM for use in openmc.deplete. """ +import numbers from itertools import repeat from multiprocessing import Pool import time @@ -11,9 +12,12 @@ import numpy as np import scipy.sparse as sp import scipy.sparse.linalg as sla -from . import comm +from openmc.checkvalue import check_type, check_length +from .abc import DepSystemSolver -__all__ = ["deplete", "timed_deplete", "CRAM16", "CRAM48"] +__all__ = [ + "deplete", "timed_deplete", "CRAM16", "CRAM48", + "Cram16Solver", "Cram48Solver", "IPFCramSolver"] def deplete(chain, x, rates, dt, matrix_func=None): @@ -83,147 +87,169 @@ def timed_deplete(*args, **kwargs): return time.time() - start, results -def CRAM16(A, n0, dt): - """Chebyshev Rational Approximation Method, order 16 +class IPFCramSolver(DepSystemSolver): + r"""CRAM depletion solver that uses incomplete partial factorization - Algorithm is the 16th order Chebyshev Rational Approximation Method, - implemented in the more stable `incomplete partial fraction (IPF) - `_ form. + Provides a :meth:`__call__` that utilizes an incomplete + partial factorization (IPF) for the Chebyshev Rational Approximation + Method (CRAM), as described in the following paper: M. Pusa, "`Higher-Order + Chebyshev Rational Approximation Method and Application to Burnup Equations + `_," Nucl. Sci. Eng., 182:3, 297-318. Parameters ---------- - A : scipy.linalg.csr_matrix - Matrix to take exponent of. - n0 : numpy.array - Vector to operate a matrix exponent on. - dt : float - Time to integrate to. + alpha : numpy.ndarray + Complex residues of poles used in the factorization. Must be a + vector with even number of items. + theta : numpy.ndarray + Complex poles. Must have an equal size as ``alpha``. + alpha0 : float + Limit of the approximation at infinity - Returns - ------- - numpy.array - Results of the matrix exponent. - - """ - - alpha = np.array([+2.124853710495224e-16, - +5.464930576870210e+3 - 3.797983575308356e+4j, - +9.045112476907548e+1 - 1.115537522430261e+3j, - +2.344818070467641e+2 - 4.228020157070496e+2j, - +9.453304067358312e+1 - 2.951294291446048e+2j, - +7.283792954673409e+2 - 1.205646080220011e+5j, - +3.648229059594851e+1 - 1.155509621409682e+2j, - +2.547321630156819e+1 - 2.639500283021502e+1j, - +2.394538338734709e+1 - 5.650522971778156e+0j], - dtype=np.complex128) - theta = np.array([+0.0, - +3.509103608414918 + 8.436198985884374j, - +5.948152268951177 + 3.587457362018322j, - -5.264971343442647 + 16.22022147316793j, - +1.419375897185666 + 10.92536348449672j, - +6.416177699099435 + 1.194122393370139j, - +4.993174737717997 + 5.996881713603942j, - -1.413928462488886 + 13.49772569889275j, - -10.84391707869699 + 19.27744616718165j], - dtype=np.complex128) - - n = A.shape[0] - - alpha0 = 2.124853710495224e-16 - - k = 8 - - y = np.array(n0, dtype=np.float64) - for l in range(1, k+1): - y = 2.0*np.real(alpha[l]*sla.spsolve(A*dt - theta[l]*sp.eye(n), y)) + y - - y *= alpha0 - return y - - -def CRAM48(A, n0, dt): - """Chebyshev Rational Approximation Method, order 48 - - Algorithm is the 48th order Chebyshev Rational Approximation Method, - implemented in the more stable `incomplete partial fraction (IPF) - `_ form. - - Parameters + Attributes ---------- - A : scipy.linalg.csr_matrix - Matrix to take exponent of. - n0 : numpy.array - Vector to operate a matrix exponent on. - dt : float - Time to integrate to. - - Returns - ------- - numpy.array - Results of the matrix exponent. + alpha : numpy.ndarray + Complex residues of poles :attr:`theta` in the incomplete partial + factorization. Denoted as :math:`\tilde{\alpha}` + theta : numpy.ndarray + Complex poles :math:`\theta` of the rational approximation + alpha0 : float + Limit of the approximation at infinity """ - theta_r = np.array([-4.465731934165702e+1, -5.284616241568964e+0, - -8.867715667624458e+0, +3.493013124279215e+0, - +1.564102508858634e+1, +1.742097597385893e+1, - -2.834466755180654e+1, +1.661569367939544e+1, - +8.011836167974721e+0, -2.056267541998229e+0, - +1.449208170441839e+1, +1.853807176907916e+1, - +9.932562704505182e+0, -2.244223871767187e+1, - +8.590014121680897e-1, -1.286192925744479e+1, - +1.164596909542055e+1, +1.806076684783089e+1, - +5.870672154659249e+0, -3.542938819659747e+1, - +1.901323489060250e+1, +1.885508331552577e+1, - -1.734689708174982e+1, +1.316284237125190e+1]) - theta_i = np.array([+6.233225190695437e+1, +4.057499381311059e+1, - +4.325515754166724e+1, +3.281615453173585e+1, - +1.558061616372237e+1, +1.076629305714420e+1, - +5.492841024648724e+1, +1.316994930024688e+1, - +2.780232111309410e+1, +3.794824788914354e+1, - +1.799988210051809e+1, +5.974332563100539e+0, - +2.532823409972962e+1, +5.179633600312162e+1, - +3.536456194294350e+1, +4.600304902833652e+1, - +2.287153304140217e+1, +8.368200580099821e+0, - +3.029700159040121e+1, +5.834381701800013e+1, - +1.194282058271408e+0, +3.583428564427879e+0, - +4.883941101108207e+1, +2.042951874827759e+1]) - theta = np.array(theta_r + theta_i * 1j, dtype=np.complex128) + def __init__(self, alpha, theta, alpha0): + check_type("alpha", alpha, np.ndarray, numbers.Complex) + check_type("theta", theta, np.ndarray, numbers.Complex) + check_length("theta", theta, alpha.size) + check_type("alpha0", alpha0, numbers.Real) + self.alpha = alpha + self.theta = theta + self.alpha0 = alpha0 - alpha_r = np.array([+6.387380733878774e+2, +1.909896179065730e+2, - +4.236195226571914e+2, +4.645770595258726e+2, - +7.765163276752433e+2, +1.907115136768522e+3, - +2.909892685603256e+3, +1.944772206620450e+2, - +1.382799786972332e+5, +5.628442079602433e+3, - +2.151681283794220e+2, +1.324720240514420e+3, - +1.617548476343347e+4, +1.112729040439685e+2, - +1.074624783191125e+2, +8.835727765158191e+1, - +9.354078136054179e+1, +9.418142823531573e+1, - +1.040012390717851e+2, +6.861882624343235e+1, - +8.766654491283722e+1, +1.056007619389650e+2, - +7.738987569039419e+1, +1.041366366475571e+2]) - alpha_i = np.array([-6.743912502859256e+2, -3.973203432721332e+2, - -2.041233768918671e+3, -1.652917287299683e+3, - -1.783617639907328e+4, -5.887068595142284e+4, - -9.953255345514560e+3, -1.427131226068449e+3, - -3.256885197214938e+6, -2.924284515884309e+4, - -1.121774011188224e+3, -6.370088443140973e+4, - -1.008798413156542e+6, -8.837109731680418e+1, - -1.457246116408180e+2, -6.388286188419360e+1, - -2.195424319460237e+2, -6.719055740098035e+2, - -1.693747595553868e+2, -1.177598523430493e+1, - -4.596464999363902e+3, -1.738294585524067e+3, - -4.311715386228984e+1, -2.777743732451969e+2]) - alpha = np.array(alpha_r + alpha_i * 1j, dtype=np.complex128) - n = A.shape[0] + def __call__(self, A, n0, dt): + """Solve depletion equations using IPF CRAM - alpha0 = 2.258038182743983e-47 + Parameters + ---------- + A : scipy.sparse.csr_matrix + Sparse transmutation matrix ``A[j, i]`` desribing rates at + which isotope ``i`` transmutes to isotope ``j`` + n0 : numpy.ndarray + Initial compositions, typically given in number of atoms in some + material or an atom density + dt : float + Time [s] of the specific interval to be solved - k = 24 + Returns + ------- + numpy.ndarray + Final compositions after ``dt`` - y = np.array(n0, dtype=np.float64) - for l in range(k): - y = 2.0*np.real(alpha[l]*sla.spsolve(A*dt - theta[l]*sp.eye(n), y)) + y + """ + A = sp.csr_matrix(A * dt, dtype=np.float64) + y = np.asarray(n0, dtype=np.float64) + ident = sp.eye(A.shape[0]) + for alpha, theta in zip(self.alpha, self.theta): + y += 2*np.real(alpha*sla.spsolve(A - theta*ident, y)) + return y * self.alpha0 - y *= alpha0 - return y + +# Coefficients for IPF Cram 16 +c16_alpha = np.array([ + +5.464930576870210e+3 - 3.797983575308356e+4j, + +9.045112476907548e+1 - 1.115537522430261e+3j, + +2.344818070467641e+2 - 4.228020157070496e+2j, + +9.453304067358312e+1 - 2.951294291446048e+2j, + +7.283792954673409e+2 - 1.205646080220011e+5j, + +3.648229059594851e+1 - 1.155509621409682e+2j, + +2.547321630156819e+1 - 2.639500283021502e+1j, + +2.394538338734709e+1 - 5.650522971778156e+0j], + dtype=np.complex128) + +c16_theta = np.array([ + +3.509103608414918 + 8.436198985884374j, + +5.948152268951177 + 3.587457362018322j, + -5.264971343442647 + 16.22022147316793j, + +1.419375897185666 + 10.92536348449672j, + +6.416177699099435 + 1.194122393370139j, + +4.993174737717997 + 5.996881713603942j, + -1.413928462488886 + 13.49772569889275j, + -10.84391707869699 + 19.27744616718165j], + dtype=np.complex128) + +c16_alpha0 = 2.124853710495224e-16 +Cram16Solver = IPFCramSolver(c16_alpha, c16_theta, c16_alpha0) +CRAM16 = Cram16Solver.__call__ + +del c16_alpha, c16_alpha0, c16_theta + +# Coefficients for 48th order IPF Cram + +theta_r = np.array([ + -4.465731934165702e+1, -5.284616241568964e+0, + -8.867715667624458e+0, +3.493013124279215e+0, + +1.564102508858634e+1, +1.742097597385893e+1, + -2.834466755180654e+1, +1.661569367939544e+1, + +8.011836167974721e+0, -2.056267541998229e+0, + +1.449208170441839e+1, +1.853807176907916e+1, + +9.932562704505182e+0, -2.244223871767187e+1, + +8.590014121680897e-1, -1.286192925744479e+1, + +1.164596909542055e+1, +1.806076684783089e+1, + +5.870672154659249e+0, -3.542938819659747e+1, + +1.901323489060250e+1, +1.885508331552577e+1, + -1.734689708174982e+1, +1.316284237125190e+1]) + +theta_i = np.array([ + +6.233225190695437e+1, +4.057499381311059e+1, + +4.325515754166724e+1, +3.281615453173585e+1, + +1.558061616372237e+1, +1.076629305714420e+1, + +5.492841024648724e+1, +1.316994930024688e+1, + +2.780232111309410e+1, +3.794824788914354e+1, + +1.799988210051809e+1, +5.974332563100539e+0, + +2.532823409972962e+1, +5.179633600312162e+1, + +3.536456194294350e+1, +4.600304902833652e+1, + +2.287153304140217e+1, +8.368200580099821e+0, + +3.029700159040121e+1, +5.834381701800013e+1, + +1.194282058271408e+0, +3.583428564427879e+0, + +4.883941101108207e+1, +2.042951874827759e+1]) + +c48_theta = np.array(theta_r + theta_i * 1j, dtype=np.complex128) + +alpha_r = np.array([ + +6.387380733878774e+2, +1.909896179065730e+2, + +4.236195226571914e+2, +4.645770595258726e+2, + +7.765163276752433e+2, +1.907115136768522e+3, + +2.909892685603256e+3, +1.944772206620450e+2, + +1.382799786972332e+5, +5.628442079602433e+3, + +2.151681283794220e+2, +1.324720240514420e+3, + +1.617548476343347e+4, +1.112729040439685e+2, + +1.074624783191125e+2, +8.835727765158191e+1, + +9.354078136054179e+1, +9.418142823531573e+1, + +1.040012390717851e+2, +6.861882624343235e+1, + +8.766654491283722e+1, +1.056007619389650e+2, + +7.738987569039419e+1, +1.041366366475571e+2]) + +alpha_i = np.array([ + -6.743912502859256e+2, -3.973203432721332e+2, + -2.041233768918671e+3, -1.652917287299683e+3, + -1.783617639907328e+4, -5.887068595142284e+4, + -9.953255345514560e+3, -1.427131226068449e+3, + -3.256885197214938e+6, -2.924284515884309e+4, + -1.121774011188224e+3, -6.370088443140973e+4, + -1.008798413156542e+6, -8.837109731680418e+1, + -1.457246116408180e+2, -6.388286188419360e+1, + -2.195424319460237e+2, -6.719055740098035e+2, + -1.693747595553868e+2, -1.177598523430493e+1, + -4.596464999363902e+3, -1.738294585524067e+3, + -4.311715386228984e+1, -2.777743732451969e+2]) + +c48_alpha = np.array(alpha_r + alpha_i * 1j, dtype=np.complex128) + +c48_alpha0 = 2.258038182743983e-47 + +Cram48Solver = IPFCramSolver(c48_alpha, c48_theta, c48_alpha0) + +del c48_alpha, c48_alpha0, c48_theta, alpha_r, alpha_i, theta_r, theta_i + +CRAM48 = Cram48Solver.__call__ diff --git a/openmc/deplete/dummy_comm.py b/openmc/deplete/dummy_comm.py index b3fa272648..2648fdedce 100644 --- a/openmc/deplete/dummy_comm.py +++ b/openmc/deplete/dummy_comm.py @@ -1,3 +1,5 @@ +import sys + class DummyCommunicator(object): rank = 0 size = 1 @@ -25,3 +27,6 @@ class DummyCommunicator(object): def scatter(self, sendobj, root=0): return sendobj[0] + + def Abort(self, exit_code_or_msg): + sys.exit(exit_code_or_msg) diff --git a/openmc/deplete/helpers.py b/openmc/deplete/helpers.py index 56644a3bba..d79a031116 100644 --- a/openmc/deplete/helpers.py +++ b/openmc/deplete/helpers.py @@ -5,11 +5,13 @@ from copy import deepcopy from itertools import product from numbers import Real import bisect +from collections import defaultdict from numpy import dot, zeros, newaxis +from . import comm from openmc.checkvalue import check_type, check_greater_than -from openmc.capi import ( +from openmc.lib import ( Tally, MaterialFilter, EnergyFilter, EnergyFunctionFilter) from .abc import ( ReactionRateHelper, EnergyHelper, FissionYieldHelper, @@ -44,7 +46,7 @@ class DirectReactionRateHelper(ReactionRateHelper): def generate_tallies(self, materials, scores): """Produce one-group reaction rate tally - Uses the :mod:`openmc.capi` to generate a tally + Uses the :mod:`openmc.lib` to generate a tally of relevant reactions across all burnable materials. Parameters @@ -57,6 +59,7 @@ class DirectReactionRateHelper(ReactionRateHelper): ``"(n, gamma)"``, needed for the reaction rate tally. """ self._rate_tally = Tally() + self._rate_tally.writable = False self._rate_tally.scores = scores self._rate_tally.filters = [MaterialFilter(materials)] @@ -156,6 +159,57 @@ class ChainFissionHelper(EnergyHelper): self._energy += dot(fission_rates, self._fission_q_vector) +class EnergyScoreHelper(EnergyHelper): + """Class responsible for obtaining system energy via a tally score + + Parameters + ---------- + score : string + Valid score to use when obtaining system energy from OpenMC. + Defaults to "heating-local" + + Attributes + ---------- + nuclides : list of str + List of nuclides with reaction rates. Not needed, but provided + for a consistent API across other :class:`EnergyHelper` + energy : float + System energy [eV] computed from the tally. Will be zero for + all MPI processes that are not the "master" process to avoid + artificially increasing the tallied energy. + score : str + Score used to obtain system energy + + """ + + def __init__(self, score="heating-local"): + super().__init__() + self.score = score + self._tally = None + + def prepare(self, *args, **kwargs): + """Create a tally for system energy production + + Input arguments are not used, as the only information needed + is :attr:`score` + + """ + self._tally = Tally() + self._tally.writable = False + self._tally.scores = [self.score] + + def reset(self): + """Obtain system energy from tally + + Only the master process, ``comm.rank == 0`` will + have a non-zero :attr:`energy` taken from the tally. + This avoids accidentally scaling the system power by + the number of MPI processes + """ + super().reset() + if comm.rank == 0: + self._energy = self._tally.results[0, 0, 1] + # ------------------------------------ # Helper for collapsing fission yields # ------------------------------------ @@ -179,9 +233,11 @@ class ConstantFissionYieldHelper(FissionYieldHelper): Attributes ---------- - constant_yields : dict of str to :class:`openmc.deplete.FissionYield` + constant_yields : collections.defaultdict Fission yields for all nuclides that only have one set of - fission yield data. Can be accessed as ``{parent: {product: yield}}`` + fission yield data. Dictionary of form ``{str: {str: float}}`` + representing yields for ``{parent: {product: yield}}``. Default + return object is an empty dictionary energy : float Energy of fission yield libraries. """ @@ -237,7 +293,7 @@ class ConstantFissionYieldHelper(FissionYieldHelper): Returns ------- - library : dict + library : collections.defaultdict Dictionary of ``{parent: {product: fyield}}`` """ return self.constant_yields @@ -282,6 +338,11 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): fast_yields : dict Dictionary of the form ``{parent: {product: yield}}`` with fast yields + constant_yields : collections.defaultdict + Fission yields for all nuclides that only have one set of + fission yield data. Dictionary of form ``{str: {str: float}}`` + representing yields for ``{parent: {product: yield}}``. Default + return object is an empty dictionary results : numpy.ndarray Array of fission rate fractions with shape ``(n_mats, 2, n_nucs)``. ``results[:, 0]`` @@ -362,13 +423,13 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): def generate_tallies(self, materials, mat_indexes): """Use C API to produce a fission rate tally in burnable materials - Include a :class:`openmc.capi.EnergyFilter` to tally fission rates + Include a :class:`openmc.lib.EnergyFilter` to tally fission rates above and below cutoff energy. Parameters ---------- - materials : iterable of :class:`openmc.capi.Material` - Materials to be used in :class:`openmc.capi.MaterialFilter` + materials : iterable of :class:`openmc.lib.Material` + Materials to be used in :class:`openmc.lib.MaterialFilter` mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the @@ -417,7 +478,7 @@ class FissionYieldCutoffHelper(TalliedFissionYieldHelper): Returns ------- - library : dict + library : collections.defaultdict Dictionary of ``{parent: {product: fyield}}`` """ yields = self.constant_yields @@ -469,9 +530,11 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): Attributes ---------- - constant_yields : dict of str to :class:`openmc.deplete.FissionYield` + constant_yields : collections.defaultdict Fission yields for all nuclides that only have one set of - fission yield data. Can be accessed as ``{parent: {product: yield}}`` + fission yield data. Dictionary of form ``{str: {str: float}}`` + representing yields for ``{parent: {product: yield}}``. Default + return object is an empty dictionary results : None or numpy.ndarray If tallies have been generated and unpacked, then the array will have shape ``(n_mats, n_tnucs)``, where ``n_mats`` is the number @@ -490,8 +553,8 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): Parameters ---------- - materials : iterable of :class:`openmc.capi.Material` - Materials to be used in :class:`openmc.capi.MaterialFilter` + materials : iterable of :class:`openmc.lib.Material` + Materials to be used in :class:`openmc.lib.MaterialFilter` mat_indexes : iterable of int Indices of tallied materials that will have their fission yields computed by this helper. Necessary as the @@ -509,6 +572,7 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): func_filter = EnergyFunctionFilter() func_filter.set_data((0, self._upper_energy), (0, self._upper_energy)) weighted_tally = Tally() + weighted_tally.writable = False weighted_tally.scores = ['fission'] weighted_tally.filters = filters + [func_filter] self._weighted_tally = weighted_tally @@ -569,12 +633,13 @@ class AveragedFissionYieldHelper(TalliedFissionYieldHelper): Returns ------- - library : dict - Dictionary of ``{parent: {product: fyield}}`` + library : collections.defaultdict + Dictionary of ``{parent: {product: fyield}}``. Default return + value is an empty dictionary """ if not self._tally_nucs: return self.constant_yields - mat_yields = {} + mat_yields = defaultdict(dict) average_energies = self.results[local_mat_index] for avg_e, nuc in zip(average_energies, self._tally_nucs): nuc_energies = nuc.yield_energies diff --git a/openmc/deplete/nuclide.py b/openmc/deplete/nuclide.py index 8896180a88..ec1668dacb 100644 --- a/openmc/deplete/nuclide.py +++ b/openmc/deplete/nuclide.py @@ -17,6 +17,10 @@ from numpy import empty from openmc.checkvalue import check_type +__all__ = [ + "DecayTuple", "ReactionTuple", "Nuclide", "FissionYield", + "FissionYieldDistribution"] + DecayTuple = namedtuple('DecayTuple', 'type target branching_ratio') DecayTuple.__doc__ = """\ diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 01bf92dcc9..e3a767e574 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -7,19 +7,21 @@ densities is all done in-memory instead of through the filesystem. """ +import sys import copy from collections import OrderedDict from itertools import chain import os import time import xml.etree.ElementTree as ET +from warnings import warn import h5py import numpy as np from uncertainties import ufloat import openmc -import openmc.capi +import openmc.lib from . import comm from .abc import TransportOperator, OperatorResult from .atom_number import AtomNumber @@ -27,7 +29,10 @@ from .reaction_rates import ReactionRates from .results_list import ResultsList from .helpers import ( DirectReactionRateHelper, ChainFissionHelper, ConstantFissionYieldHelper, - FissionYieldCutoffHelper, AveragedFissionYieldHelper) + FissionYieldCutoffHelper, AveragedFissionYieldHelper, EnergyScoreHelper) + + +__all__ = ["Operator", "OperatorResult"] def _distribute(items): @@ -78,9 +83,15 @@ class Operator(TransportOperator): diff_burnable_mats : bool, optional Whether to differentiate burnable materials with multiple instances. Default: False. + energy_mode : {"energy-deposition", "fission-q"} + Indicator for computing system energy. ``"energy-deposition"`` will + compute with a single energy deposition tally, taking fission energy + release data and heating into consideration. ``"fission-q"`` will + use the fission Q values from the depletion chain fission_q : dict, optional Dictionary of nuclides and their fission Q values [eV]. If not given, - values will be pulled from the ``chain_file``. + values will be pulled from the ``chain_file``. Only applicable + if ``"energy_mode" == "fission-q"`` dilute_initial : float, optional Initial atom density [atoms/cm^3] to add for nuclides that are zero in initial condition to ensure they exist in the decay chain. @@ -128,7 +139,7 @@ class Operator(TransportOperator): burnable_mats : list of str All burnable material IDs heavy_metal : float - Initial heavy metal inventory + Initial heavy metal inventory [g] local_mats : list of str All burnable material IDs being managed by a single process prev_res : ResultsList or None @@ -144,13 +155,22 @@ class Operator(TransportOperator): } def __init__(self, geometry, settings, chain_file=None, prev_results=None, - diff_burnable_mats=False, fission_q=None, - dilute_initial=1.0e3, fission_yield_mode="constant", - fission_yield_opts=None): + diff_burnable_mats=False, energy_mode="fission-q", + fission_q=None, dilute_initial=1.0e3, + fission_yield_mode="constant", fission_yield_opts=None): if fission_yield_mode not in self._fission_helpers: raise KeyError( "fission_yield_mode must be one of {}, not {}".format( ", ".join(self._fission_helpers), fission_yield_mode)) + if energy_mode == "energy-deposition": + if fission_q is not None: + warn("Fission Q dictionary not used if energy deposition " + "is used") + fission_q = None + elif energy_mode != "fission-q": + raise ValueError( + "energy_mode {} not supported. Must be energy-deposition " + "or fission-q".format(energy_mode)) super().__init__(chain_file, fission_q, dilute_initial, prev_results) self.round_number = False self.prev_res = None @@ -204,7 +224,11 @@ class Operator(TransportOperator): # Get classes to assist working with tallies self._rate_helper = DirectReactionRateHelper( self.reaction_rates.n_nuc, self.reaction_rates.n_react) - self._energy_helper = ChainFissionHelper() + if energy_mode == "fission-q": + self._energy_helper = ChainFissionHelper() + else: + score = "heating" if settings.photon_transport else "heating-local" + self._energy_helper = EnergyScoreHelper(score) # Select and create fission yield helper fission_helper = self._fission_helpers[fission_yield_mode] @@ -216,6 +240,10 @@ class Operator(TransportOperator): def __call__(self, vec, power): """Runs a simulation. + Simulation will abort under the following circumstances: + + 1) No energy is computed using OpenMC tallies. + Parameters ---------- vec : list of numpy.ndarray @@ -235,8 +263,6 @@ class Operator(TransportOperator): # Update status self.number.set_density(vec) - time_start = time.time() - # Update material compositions and tally nuclides self._update_materials() nuclides = self._get_tally_nuclides() @@ -245,10 +271,8 @@ class Operator(TransportOperator): self._yield_helper.update_tally_nuclides(nuclides) # Run OpenMC - openmc.capi.reset() - openmc.capi.run() - - time_openmc = time.time() + openmc.lib.reset() + openmc.lib.run() # Extract results op_result = self._unpack_tallies_and_normalize(power) @@ -264,7 +288,7 @@ class Operator(TransportOperator): step : int Current depletion step including restarts """ - openmc.capi.statepoint_write( + openmc.lib.statepoint_write( "openmc_simulation_n{}.h5".format(step), write_source=False) @@ -438,10 +462,10 @@ class Operator(TransportOperator): # Initialize OpenMC library comm.barrier() - openmc.capi.init(intracomm=comm) + openmc.lib.init(intracomm=comm) # Generate tallies in memory - materials = [openmc.capi.materials[int(i)] + materials = [openmc.lib.materials[int(i)] for i in self.burnable_mats] self._rate_helper.generate_tallies(materials, self.chain.reactions) self._energy_helper.prepare( @@ -456,7 +480,7 @@ class Operator(TransportOperator): def finalize(self): """Finalize a depletion simulation and release resources.""" - openmc.capi.finalize() + openmc.lib.finalize() def _update_materials(self): """Updates material compositions in OpenMC on all processes.""" @@ -491,7 +515,7 @@ class Operator(TransportOperator): number_i[mat, nuc] = 0.0 # Update densities on C API side - mat_internal = openmc.capi.materials[int(mat)] + mat_internal = openmc.lib.materials[int(mat)] mat_internal.set_densities(nuclides, densities) #TODO Update densities on the Python side, otherwise the @@ -581,7 +605,7 @@ class Operator(TransportOperator): rates.fill(0.0) # Get k and uncertainty - k_combined = ufloat(*openmc.capi.keff()) + k_combined = ufloat(*openmc.lib.keff()) # Extract tally bins nuclides = self._rate_helper.nuclides @@ -634,6 +658,15 @@ class Operator(TransportOperator): # J / s / source neutron energy = comm.allreduce(self._energy_helper.energy) + # Guard against divide by zero + if energy == 0: + if comm.rank == 0: + sys.stderr.flush() + print(" No energy reported from OpenMC tallies. Do your HDF5 " + "files have heating data?\n", file=sys.stderr, flush=True) + comm.barrier() + comm.Abort(1) + # Scale reaction rates to obtain units of reactions/sec rates *= power / energy diff --git a/openmc/deplete/reaction_rates.py b/openmc/deplete/reaction_rates.py index cea2f19976..85c8d8998d 100644 --- a/openmc/deplete/reaction_rates.py +++ b/openmc/deplete/reaction_rates.py @@ -7,6 +7,9 @@ from collections import OrderedDict import numpy as np +__all__ = ["ReactionRates"] + + class ReactionRates(np.ndarray): """Reaction rates resulting from a transport operator call diff --git a/openmc/deplete/results.py b/openmc/deplete/results.py index 6593d5fc5f..b44739bc5f 100644 --- a/openmc/deplete/results.py +++ b/openmc/deplete/results.py @@ -16,6 +16,9 @@ from .reaction_rates import ReactionRates _VERSION_RESULTS = (1, 0) +__all__ = ["Results"] + + class Results(object): """Output of a depletion run diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index 3b5166ed8a..1b5b81ac4c 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -5,6 +5,9 @@ from .results import Results, _VERSION_RESULTS from openmc.checkvalue import check_filetype_version +__all__ = ["ResultsList"] + + class ResultsList(list): """A list of openmc.deplete.Results objects @@ -137,7 +140,7 @@ class ResultsList(list): def get_depletion_time(self): """Return an array of the average time to deplete a material - ..note:: + .. note:: Will have one fewer row than number of other methods, like :meth:`get_eigenvalues`, because no depletion @@ -145,7 +148,6 @@ class ResultsList(list): Returns ------- - times : :class:`numpy.ndarray` Vector of average time to deplete a single material across all processes and materials. diff --git a/openmc/capi/__init__.py b/openmc/lib/__init__.py similarity index 69% rename from openmc/capi/__init__.py rename to openmc/lib/__init__.py index 5ca867d67e..92fd1730d3 100644 --- a/openmc/capi/__init__.py +++ b/openmc/lib/__init__.py @@ -1,14 +1,14 @@ """ -This module provides bindings to C functions defined by OpenMC shared library. -When the :mod:`openmc` package is imported, the OpenMC shared library is -automatically loaded. Calls to the OpenMC library can then be via functions or -objects in the :mod:`openmc.capi` subpackage, for example: +This module provides bindings to C/C++ functions defined by OpenMC shared +library. When the :mod:`openmc.lib` package is imported, the OpenMC shared +library is automatically loaded. Calls to the OpenMC library can then be via +functions or objects in :mod:`openmc.lib`, for example: .. code-block:: python - openmc.capi.init() - openmc.capi.run() - openmc.capi.finalize() + openmc.lib.init() + openmc.lib.run() + openmc.lib.finalize() """ @@ -33,7 +33,7 @@ if os.environ.get('READTHEDOCS', None) != 'True': else: # For documentation builds, we don't actually have the shared library # available. Instead, we create a mock object so that when the modules - # within the openmc.capi package try to configure arguments and return + # within the openmc.lib package try to configure arguments and return # values for symbols, no errors occur from unittest.mock import Mock _dll = Mock() diff --git a/openmc/capi/cell.py b/openmc/lib/cell.py similarity index 99% rename from openmc/capi/cell.py rename to openmc/lib/cell.py index 784ebd087b..6ed4bca1ea 100644 --- a/openmc/capi/cell.py +++ b/openmc/lib/cell.py @@ -63,7 +63,7 @@ class Cell(_FortranObjectWithID): This class exposes a cell that is stored internally in the OpenMC library. To obtain a view of a cell with a given ID, use the - :data:`openmc.capi.cells` mapping. + :data:`openmc.lib.cells` mapping. Parameters ---------- diff --git a/openmc/capi/core.py b/openmc/lib/core.py similarity index 94% rename from openmc/capi/core.py rename to openmc/lib/core.py index 546121c98c..cba96fa4b8 100644 --- a/openmc/capi/core.py +++ b/openmc/lib/core.py @@ -11,7 +11,7 @@ from numpy.ctypeslib import as_array from openmc.exceptions import AllocationError from . import _dll from .error import _error_handler -import openmc.capi +import openmc.lib class _Bank(Structure): @@ -127,7 +127,7 @@ def find_cell(xyz): Returns ------- - openmc.capi.Cell + openmc.lib.Cell Cell containing the point int If the cell at the given point is repeated in the geometry, this @@ -137,7 +137,7 @@ def find_cell(xyz): index = c_int32() instance = c_int32() _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) - return openmc.capi.Cell(index=index.value), instance.value + return openmc.lib.Cell(index=index.value), instance.value def find_material(xyz): @@ -150,7 +150,7 @@ def find_material(xyz): Returns ------- - openmc.capi.Material or None + openmc.lib.Material or None Material containing the point, or None is no material is found """ @@ -158,8 +158,8 @@ def find_material(xyz): instance = c_int32() _dll.openmc_find_cell((c_double*3)(*xyz), index, instance) - mats = openmc.capi.Cell(index=index.value).fill - if isinstance(mats, (openmc.capi.Material, type(None))): + mats = openmc.lib.Cell(index=index.value).fill + if isinstance(mats, (openmc.lib.Material, type(None))): return mats else: return mats[instance.value] @@ -225,21 +225,21 @@ def iter_batches(): This function returns a generator-iterator that allows Python code to be run between batches in an OpenMC simulation. It should be used in conjunction - with :func:`openmc.capi.simulation_init` and - :func:`openmc.capi.simulation_finalize`. For example: + with :func:`openmc.lib.simulation_init` and + :func:`openmc.lib.simulation_finalize`. For example: .. code-block:: Python - with openmc.capi.run_in_memory(): - openmc.capi.simulation_init() - for _ in openmc.capi.iter_batches(): + with openmc.lib.run_in_memory(): + openmc.lib.simulation_init() + for _ in openmc.lib.iter_batches(): # Look at convergence of tallies, for example ... - openmc.capi.simulation_finalize() + openmc.lib.simulation_finalize() See Also -------- - openmc.capi.next_batch + openmc.lib.next_batch """ while True: @@ -365,11 +365,11 @@ def run_in_memory(**kwargs): block, all memory that was allocated during the block is freed. For example:: - with openmc.capi.run_in_memory(): + with openmc.lib.run_in_memory(): for i in range(n_iters): - openmc.capi.reset() + openmc.lib.reset() do_stuff() - openmc.capi.run() + openmc.lib.run() Parameters ---------- diff --git a/openmc/capi/error.py b/openmc/lib/error.py similarity index 100% rename from openmc/capi/error.py rename to openmc/lib/error.py diff --git a/openmc/capi/filter.py b/openmc/lib/filter.py similarity index 100% rename from openmc/capi/filter.py rename to openmc/lib/filter.py diff --git a/openmc/capi/material.py b/openmc/lib/material.py similarity index 99% rename from openmc/capi/material.py rename to openmc/lib/material.py index f0ecac7610..0fbce72458 100644 --- a/openmc/capi/material.py +++ b/openmc/lib/material.py @@ -66,7 +66,7 @@ class Material(_FortranObjectWithID): This class exposes a material that is stored internally in the OpenMC library. To obtain a view of a material with a given ID, use the - :data:`openmc.capi.materials` mapping. + :data:`openmc.lib.materials` mapping. Parameters ---------- diff --git a/openmc/capi/math.py b/openmc/lib/math.py similarity index 100% rename from openmc/capi/math.py rename to openmc/lib/math.py diff --git a/openmc/capi/mesh.py b/openmc/lib/mesh.py similarity index 99% rename from openmc/capi/mesh.py rename to openmc/lib/mesh.py index a1161f48f3..bf51bdfe98 100644 --- a/openmc/capi/mesh.py +++ b/openmc/lib/mesh.py @@ -50,7 +50,7 @@ class RegularMesh(_FortranObjectWithID): This class exposes a mesh that is stored internally in the OpenMC library. To obtain a view of a mesh with a given ID, use the - :data:`openmc.capi.meshes` mapping. + :data:`openmc.lib.meshes` mapping. Parameters ---------- diff --git a/openmc/capi/nuclide.py b/openmc/lib/nuclide.py similarity index 98% rename from openmc/capi/nuclide.py rename to openmc/lib/nuclide.py index 4a21a6d2b4..81ef7e648e 100644 --- a/openmc/capi/nuclide.py +++ b/openmc/lib/nuclide.py @@ -43,7 +43,7 @@ class Nuclide(_FortranObject): This class exposes a nuclide that is stored internally in the OpenMC solver. To obtain a view of a nuclide with a given name, use the - :data:`openmc.capi.nuclides` mapping. + :data:`openmc.lib.nuclides` mapping. Parameters ---------- diff --git a/openmc/capi/plot.py b/openmc/lib/plot.py similarity index 97% rename from openmc/capi/plot.py rename to openmc/lib/plot.py index c4bff4cb3f..c51000e0de 100644 --- a/openmc/capi/plot.py +++ b/openmc/lib/plot.py @@ -52,9 +52,9 @@ class _PlotBase(Structure): C-Type Attributes ----------------- - origin : openmc.capi.plot._Position + origin : openmc.lib.plot._Position A position defining the origin of the plot. - width_ : openmc.capi.plot._Position + width_ : openmc.lib.plot._Position The width of the plot along the x, y, and z axes, respectively basis_ : c_int The axes basis of the plot view. @@ -222,7 +222,7 @@ def id_map(plot): Parameters ---------- - plot : openmc.capi.plot._PlotBase + plot : openmc.lib.plot._PlotBase Object describing the slice of the model to be generated Returns @@ -250,7 +250,7 @@ def property_map(plot): Parameters ---------- - plot : openmc.capi.plot._PlotBase + plot : openmc.lib.plot._PlotBase Object describing the slice of the model to be generated Returns diff --git a/openmc/capi/settings.py b/openmc/lib/settings.py similarity index 100% rename from openmc/capi/settings.py rename to openmc/lib/settings.py diff --git a/openmc/capi/tally.py b/openmc/lib/tally.py similarity index 94% rename from openmc/capi/tally.py rename to openmc/lib/tally.py index 88c14e4494..2e8d43ae85 100644 --- a/openmc/capi/tally.py +++ b/openmc/lib/tally.py @@ -53,6 +53,9 @@ _dll.openmc_tally_get_scores.errcheck = _error_handler _dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_tally_get_type.restype = c_int _dll.openmc_tally_get_type.errcheck = _error_handler +_dll.openmc_tally_get_writable.argtypes = [c_int32, POINTER(c_bool)] +_dll.openmc_tally_get_writable.restype = c_int +_dll.openmc_tally_get_writable.errcheck = _error_handler _dll.openmc_tally_reset.argtypes = [c_int32] _dll.openmc_tally_reset.restype = c_int _dll.openmc_tally_reset.errcheck = _error_handler @@ -81,6 +84,9 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler _dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p] _dll.openmc_tally_set_type.restype = c_int _dll.openmc_tally_set_type.errcheck = _error_handler +_dll.openmc_tally_set_writable.argtypes = [c_int32, c_bool] +_dll.openmc_tally_set_writable.restype = c_int +_dll.openmc_tally_set_writable.errcheck = _error_handler _dll.tallies_size.restype = c_size_t @@ -89,7 +95,7 @@ _SCORES = { -5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission', -9: 'current', -10: 'events', -11: 'delayed-nu-fission', -12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt', - -15: 'fission-q-recoverable', -16: 'decay-rate', -17: 'heating' + -15: 'fission-q-recoverable', -16: 'decay-rate' } _ESTIMATORS = { 1: 'analog', 2: 'tracklength', 3: 'collision' @@ -143,7 +149,7 @@ class Tally(_FortranObjectWithID): This class exposes a tally that is stored internally in the OpenMC library. To obtain a view of a tally with a given ID, use the - :data:`openmc.capi.tallies` mapping. + :data:`openmc.lib.tallies` mapping. Parameters ---------- @@ -344,6 +350,16 @@ class Tally(_FortranObjectWithID): return std_dev + @property + def writable(self): + writable = c_bool() + _dll.openmc_tally_get_writable(self._index, writable) + return writable.value + + @writable.setter + def writable(self, writable): + _dll.openmc_tally_set_writable(self._index, writable) + def reset(self): """Reset results and num_realizations of tally""" _dll.openmc_tally_reset(self._index) diff --git a/openmc/mesh.py b/openmc/mesh.py index 4186b56a88..6ef8776fe8 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -53,6 +53,12 @@ class MeshBase(IDManagerMixin, metaclass=ABCMeta): else: self._name = '' + def __repr__(self): + string = type(self).__name__ + '\n' + string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) + string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) + return string + @classmethod def from_hdf5(cls, group): """Create mesh from HDF5 group @@ -126,7 +132,10 @@ class RegularMesh(MeshBase): @property def n_dimension(self): - return len(self._dimension) + if self._dimension is not None: + return len(self._dimension) + else: + return None @property def lower_left(self): @@ -187,11 +196,9 @@ class RegularMesh(MeshBase): self._width = width def __repr__(self): - string = 'RegularMesh\n' - string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) - string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) - string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type) - string += '{0: <16}{1}{2}\n'.format('\tBasis', '=\t', self._dimension) + string = super().__repr__() + string += '{0: <16}{1}{2}\n'.format('\tDimensions', '=\t', self.n_dimension) + string += '{0: <16}{1}{2}\n'.format('\tMesh Cells', '=\t', self._dimension) string += '{0: <16}{1}{2}\n'.format('\tWidth', '=\t', self._lower_left) string += '{0: <16}{1}{2}\n'.format('\tOrigin', '=\t', self._upper_right) string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width) @@ -350,18 +357,14 @@ class RegularMesh(MeshBase): n_dim = len(self.dimension) # Build the cell which will contain the lattice - xplanes = [openmc.XPlane(x0=self.lower_left[0], - boundary_type=bc[0]), - openmc.XPlane(x0=self.upper_right[0], - boundary_type=bc[1])] + xplanes = [openmc.XPlane(self.lower_left[0], bc[0]), + openmc.XPlane(self.upper_right[0], bc[1])] if n_dim == 1: - yplanes = [openmc.YPlane(y0=-1e10, boundary_type='reflective'), - openmc.YPlane(y0=1e10, boundary_type='reflective')] + yplanes = [openmc.YPlane(-1e10, 'reflective'), + openmc.YPlane(1e10, 'reflective')] else: - yplanes = [openmc.YPlane(y0=self.lower_left[1], - boundary_type=bc[2]), - openmc.YPlane(y0=self.upper_right[1], - boundary_type=bc[3])] + yplanes = [openmc.YPlane(self.lower_left[1], bc[2]), + openmc.YPlane(self.upper_right[1], bc[3])] if n_dim <= 2: # Would prefer to have the z ranges be the max supported float, but @@ -371,13 +374,11 @@ class RegularMesh(MeshBase): # inconsistency between what numpy uses as the max float and what # Fortran expects for a real(8), so this avoids code complication # and achieves the same goal. - zplanes = [openmc.ZPlane(z0=-1e10, boundary_type='reflective'), - openmc.ZPlane(z0=1e10, boundary_type='reflective')] + zplanes = [openmc.ZPlane(-1e10, 'reflective'), + openmc.ZPlane(1e10, 'reflective')] else: - zplanes = [openmc.ZPlane(z0=self.lower_left[2], - boundary_type=bc[4]), - openmc.ZPlane(z0=self.upper_right[2], - boundary_type=bc[5])] + zplanes = [openmc.ZPlane(self.lower_left[2], bc[4]), + openmc.ZPlane(self.upper_right[2], bc[5])] root_cell = openmc.Cell() root_cell.region = ((+xplanes[0] & -xplanes[1]) & (+yplanes[0] & -yplanes[1]) & @@ -528,6 +529,27 @@ class RectilinearMesh(MeshBase): cv.check_type('mesh z_grid', grid, Iterable, Real) self._z_grid = grid + def __repr__(self): + fmt = '{0: <16}{1}{2}\n' + string = super().__repr__() + string += fmt.format('\tDimensions', '=\t', self.n_dimension) + x_grid_str = str(self._x_grid) if not self._x_grid else len(self._x_grid) + string += fmt.format('\tN X pnts:', '=\t', x_grid_str) + if self._x_grid: + string += fmt.format('\tX Min:', '=\t', self._x_grid[0]) + string += fmt.format('\tX Max:', '=\t', self._x_grid[-1]) + y_grid_str = str(self._y_grid) if not self._y_grid else len(self._y_grid) + string += fmt.format('\tN Y pnts:', '=\t', y_grid_str) + if self._y_grid: + string += fmt.format('\tY Min:', '=\t', self._y_grid[0]) + string += fmt.format('\tY Max:', '=\t', self._y_grid[-1]) + z_grid_str = str(self._z_grid) if not self._z_grid else len(self._z_grid) + string += fmt.format('\tN Z pnts:', '=\t', z_grid_str) + if self._z_grid: + string += fmt.format('\tZ Min:', '=\t', self._z_grid[0]) + string += fmt.format('\tZ Max:', '=\t', self._z_grid[-1]) + return string + @classmethod def from_hdf5(cls, group): mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index 886cc904a2..1b1ad0dce2 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -18,7 +18,7 @@ GROUP_STRUCTURES = {} .. _CASMO: https://www.studsvik.com/SharepointFiles/CASMO-5%20Development%20and%20Applications.pdf .. _XMAS-172: https://www-nds.iaea.org/wimsd/energy.htm -.. _SHEM-361: https://www.polymtl.ca/merlin/libraries.htm +.. _SHEM-361: https://www.polymtl.ca/merlin/downloads/FP214.pdf .. _activation: https://fispact.ukaea.uk/wiki/Keyword:GETXS .. _CCFE-709: https://fispact.ukaea.uk/wiki/CCFE-709_group_structure .. _UKAEA-1102: https://fispact.ukaea.uk/wiki/UKAEA-1102_group_structure diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 46b0048385..e9fee5a2f1 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -3552,7 +3552,7 @@ class ScatterMatrixXS(MatrixMGXS): .. math:: \begin{aligned} - \langle \sigma}_{s,\ell,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr + \langle \sigma_{s,\ell,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; P_\ell (\Omega \cdot \Omega') \sigma_s (r, E' \rightarrow E, \Omega' \cdot \Omega) \psi(r, E', \Omega')\\ diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index bb5d39a2d9..3ee5052402 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -253,8 +253,8 @@ def hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), x, y = origin if orientation == 'y': - right = XPlane(x0=x + sqrt(3.)/2*l, boundary_type=boundary_type) - left = XPlane(x0=x - sqrt(3.)/2*l, boundary_type=boundary_type) + right = XPlane(x + sqrt(3.)/2*l, boundary_type) + left = XPlane(x - sqrt(3.)/2*l, boundary_type) c = sqrt(3.)/3. # y = -x/sqrt(3) + a diff --git a/openmc/model/model.py b/openmc/model/model.py index 2f515ca78f..33a38ef224 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -147,7 +147,7 @@ class Model(object): """ # Import the depletion module. This is done here rather than the module - # header to delay importing openmc.capi (through openmc.deplete) which + # header to delay importing openmc.lib (through openmc.deplete) which # can be tough to install properly. import openmc.deplete as dep diff --git a/openmc/plots.py b/openmc/plots.py index b4712c9026..10d7738689 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -173,7 +173,7 @@ class Plot(IDManagerMixin): OpenMC is capable of generating two-dimensional slice plots and three-dimensional voxel plots. Colors that are used in plots can be given as RGB tuples, e.g. (255, 255, 255) would be white, or by a string indicating a - valid `SVG color `_. + valid `SVG color `_. Parameters ---------- diff --git a/openmc/polynomial.py b/openmc/polynomial.py index ac1ad675d6..abcfd8057e 100644 --- a/openmc/polynomial.py +++ b/openmc/polynomial.py @@ -73,9 +73,9 @@ class ZernikeRadial(Polynomial): return self._order def __call__(self, r): - import openmc.capi as capi + import openmc.lib as lib if isinstance(r, Iterable): - return [np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r_i)) + return [np.sum(self._norm_coef * lib.calc_zn_rad(self.order, r_i)) for r_i in r] else: - return np.sum(self._norm_coef * capi.calc_zn_rad(self.order, r)) + return np.sum(self._norm_coef * lib.calc_zn_rad(self.order, r)) diff --git a/openmc/region.py b/openmc/region.py index d12ae7112a..a751e1ebf2 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -527,10 +527,10 @@ class Complement(Region): The Complement of an existing :class:`openmc.Region` can be created by using the ~ operator as the following example demonstrates: - >>> xl = openmc.XPlane(x0=-10.0) - >>> xr = openmc.XPlane(x0=10.0) - >>> yl = openmc.YPlane(y0=-10.0) - >>> yr = openmc.YPlane(y0=10.0) + >>> xl = openmc.XPlane(-10.0) + >>> xr = openmc.XPlane(10.0) + >>> yl = openmc.YPlane(-10.0) + >>> yr = openmc.YPlane(10.0) >>> inside_box = +xl & -xr & +yl & -yr >>> outside_box = ~inside_box >>> type(outside_box) diff --git a/openmc/statepoint.py b/openmc/statepoint.py index c9a8f9f9a8..5802a74cae 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -375,13 +375,18 @@ class StatePoint(object): for tally_id in tally_ids: group = tallies_group['tally {}'.format(tally_id)] - # Read the number of realizations - n_realizations = group['n_realizations'][()] + # Check if tally is internal and therefore has no data + if group.attrs.get("internal"): + continue # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = self._f.filename tally.name = group['name'][()].decode() if 'name' in group else '' + + # Read the number of realizations + n_realizations = group['n_realizations'][()] + tally.estimator = group['estimator'][()].decode() tally.num_realizations = n_realizations diff --git a/openmc/summary.py b/openmc/summary.py index 2ab29c00a4..bcb3ba33b6 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -97,16 +97,17 @@ class Summary(object): self._macroscopics = name.decode() def _read_geometry(self): - if "dagmc" in self._f['geometry'].attrs.keys(): - return - # Read in and initialize the Materials and Geometry + # Read in and initialize the Materials self._read_materials() - self._read_surfaces() - cell_fills = self._read_cells() - self._read_universes() - self._read_lattices() - self._finalize_geometry(cell_fills) + + # Read native geometry only + if "dagmc" not in self._f['geometry'].attrs.keys(): + self._read_surfaces() + cell_fills = self._read_cells() + self._read_universes() + self._read_lattices() + self._finalize_geometry(cell_fills) def _read_materials(self): for group in self._f['materials'].values(): @@ -153,8 +154,9 @@ class Summary(object): if 'rotation' in group: rotation = group['rotation'][()] - rotation = np.asarray(rotation, dtype=np.int) - cell._rotation = rotation + if rotation.size == 9: + rotation.shape = (3, 3) + cell.rotation = rotation elif fill_type == 'material': cell.temperature = group['temperature'][()] diff --git a/openmc/surface.py b/openmc/surface.py index dc251485d2..2ffbf0722b 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -12,7 +12,7 @@ from openmc.region import Region, Intersection, Union from openmc.mixin import IDManagerMixin -_BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic'] +_BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic', 'white'] _WARNING_UPPER = """\ "{}(...) accepts an argument named '{}', not '{}'. Future versions of OpenMC \ @@ -32,7 +32,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Note that periodic boundary conditions @@ -44,7 +44,7 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): Attributes ---------- - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -277,49 +277,48 @@ class Surface(IDManagerMixin, metaclass=ABCMeta): # Create the Surface based on its type if surf_type == 'x-plane': x0 = coeffs[0] - surface = XPlane(surface_id, bc, x0, name) + surface = XPlane(x0, bc, name, surface_id) elif surf_type == 'y-plane': y0 = coeffs[0] - surface = YPlane(surface_id, bc, y0, name) + surface = YPlane(y0, bc, name, surface_id) elif surf_type == 'z-plane': z0 = coeffs[0] - surface = ZPlane(surface_id, bc, z0, name) + surface = ZPlane(z0, bc, name, surface_id) elif surf_type == 'plane': A, B, C, D = coeffs - surface = Plane(surface_id, bc, A, B, C, D, name) + surface = Plane(A, B, C, D, bc, name, surface_id) elif surf_type == 'x-cylinder': y0, z0, r = coeffs - surface = XCylinder(surface_id, bc, y0, z0, r, name) + surface = XCylinder(y0, z0, r, bc, name, surface_id) elif surf_type == 'y-cylinder': x0, z0, r = coeffs - surface = YCylinder(surface_id, bc, x0, z0, r, name) + surface = YCylinder(x0, z0, r, bc, name, surface_id) elif surf_type == 'z-cylinder': x0, y0, r = coeffs - surface = ZCylinder(surface_id, bc, x0, y0, r, name) + surface = ZCylinder(x0, y0, r, bc, name, surface_id) elif surf_type == 'sphere': x0, y0, z0, r = coeffs - surface = Sphere(surface_id, bc, x0, y0, z0, r, name) + surface = Sphere(x0, y0, z0, r, bc, name, surface_id) elif surf_type in ['x-cone', 'y-cone', 'z-cone']: x0, y0, z0, r2 = coeffs if surf_type == 'x-cone': - surface = XCone(surface_id, bc, x0, y0, z0, r2, name) + surface = XCone(x0, y0, z0, r2, bc, name, surface_id) elif surf_type == 'y-cone': - surface = YCone(surface_id, bc, x0, y0, z0, r2, name) + surface = YCone(x0, y0, z0, r2, bc, name, surface_id) elif surf_type == 'z-cone': - surface = ZCone(surface_id, bc, x0, y0, z0, r2, name) + surface = ZCone(x0, y0, z0, r2, bc, name, surface_id) elif surf_type == 'quadric': a, b, c, d, e, f, g, h, j, k = coeffs - surface = Quadric(surface_id, bc, a, b, c, d, e, f, g, - h, j, k, name) + surface = Quadric(a, b, c, d, e, f, g, h, j, k, bc, name, surface_id) return surface @@ -329,13 +328,6 @@ class Plane(Surface): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. a : float, optional The 'A' parameter for the plane. Defaults to 1. b : float, optional @@ -344,8 +336,15 @@ class Plane(Surface): The 'C' parameter for the plane. Defaults to 0. d : float, optional The 'D' parameter for the plane. Defaults to 0. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the plane. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -357,7 +356,7 @@ class Plane(Surface): The 'C' parameter for the plane d : float The 'D' parameter for the plane - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. periodic_surface : openmc.Surface @@ -377,8 +376,8 @@ class Plane(Surface): _type = 'plane' _coeff_keys = ('a', 'b', 'c', 'd') - def __init__(self, surface_id=None, boundary_type='transmission', - a=1., b=0., c=0., d=0., name='', **kwargs): + def __init__(self, a=1., b=0., c=0., d=0., boundary_type='transmission', + name='', surface_id=None, **kwargs): super().__init__(surface_id, boundary_type, name=name) self._periodic_surface = None self.a = a @@ -532,24 +531,24 @@ class XPlane(Plane): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + x0 : float, optional + Location of the plane. Defaults to 0. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is supported, i.e., x-planes can only be paired with x-planes. - x0 : float, optional - Location of the plane. Defaults to 0. name : str, optional Name of the plane. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- x0 : float Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. periodic_surface : openmc.Surface @@ -569,9 +568,9 @@ class XPlane(Plane): _type = 'x-plane' _coeff_keys = ('x0',) - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., name=''): - super().__init__(surface_id, boundary_type, name=name) + def __init__(self, x0=0., boundary_type='transmission', + name='', surface_id=None): + super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) self.x0 = x0 @property @@ -657,24 +656,24 @@ class YPlane(Plane): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + y0 : float, optional + Location of the plane + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is supported, i.e., x-planes can only be paired with x-planes. - y0 : float, optional - Location of the plane name : str, optional Name of the plane. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- y0 : float Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. periodic_surface : openmc.Surface @@ -694,10 +693,9 @@ class YPlane(Plane): _type = 'y-plane' _coeff_keys = ('y0',) - def __init__(self, surface_id=None, boundary_type='transmission', - y0=0., name=''): - # Initialize YPlane class attributes - super().__init__(surface_id, boundary_type, name=name) + def __init__(self, y0=0., boundary_type='transmission', + name='', surface_id=None): + super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) self.y0 = y0 @property @@ -786,7 +784,7 @@ class ZPlane(Plane): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. Only axis-aligned periodicity is @@ -800,7 +798,7 @@ class ZPlane(Plane): ---------- z0 : float Location of the plane - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. periodic_surface : openmc.Surface @@ -820,10 +818,9 @@ class ZPlane(Plane): _type = 'z-plane' _coeff_keys = ('z0',) - def __init__(self, surface_id=None, boundary_type='transmission', - z0=0., name=''): - # Initialize ZPlane class attributes - super().__init__(surface_id, boundary_type, name=name) + def __init__(self, z0=0., boundary_type='transmission', + name='', surface_id=None): + super().__init__(surface_id=surface_id, boundary_type=boundary_type, name=name) self.z0 = z0 @property @@ -909,24 +906,24 @@ class Cylinder(Surface): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional + r : float, optional + Radius of the cylinder. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. - r : float, optional - Radius of the cylinder. Defaults to 1. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- r : float Radius of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -939,8 +936,8 @@ class Cylinder(Surface): Type of the surface """ - def __init__(self, surface_id=None, boundary_type='transmission', - r=1., name=''): + def __init__(self, r=1., boundary_type='transmission', + name='', surface_id=None): super().__init__(surface_id, boundary_type, name=name) self.r = r @@ -960,22 +957,22 @@ class XCylinder(Cylinder): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. y0 : float, optional y-coordinate of the center of the cylinder. Defaults to 0. z0 : float, optional z-coordinate of the center of the cylinder. Defaults to 0. r : float, optional Radius of the cylinder. Defaults to 0. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -983,7 +980,7 @@ class XCylinder(Cylinder): y-coordinate of the center of the cylinder z0 : float z-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1000,12 +997,12 @@ class XCylinder(Cylinder): _type = 'x-cylinder' _coeff_keys = ('y0', 'z0', 'r') - def __init__(self, surface_id=None, boundary_type='transmission', - y0=0., z0=0., r=1., name='', *, R=None): + def __init__(self, y0=0., z0=0., r=1., boundary_type='transmission', + name='', surface_id=None, *, R=None): if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(surface_id, boundary_type, r, name=name) + super().__init__(r, boundary_type, name, surface_id) self.y0 = y0 self.z0 = z0 @@ -1107,22 +1104,22 @@ class YCylinder(Cylinder): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. x0 : float, optional x-coordinate of the center of the cylinder. Defaults to 0. z0 : float, optional z-coordinate of the center of the cylinder. Defaults to 0. r : float, optional Radius of the cylinder. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the cylinder. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -1130,7 +1127,7 @@ class YCylinder(Cylinder): x-coordinate of the center of the cylinder z0 : float z-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1147,12 +1144,12 @@ class YCylinder(Cylinder): _type = 'y-cylinder' _coeff_keys = ('x0', 'z0', 'r') - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., z0=0., r=1., name='', *, R=None): + def __init__(self, x0=0., z0=0., r=1., boundary_type='transmission', + name='', surface_id=None, *, R=None): if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(surface_id, boundary_type, r, name=name) + super().__init__(r, boundary_type, name, surface_id) self.x0 = x0 self.z0 = z0 @@ -1257,7 +1254,7 @@ class ZCylinder(Cylinder): surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. @@ -1277,7 +1274,7 @@ class ZCylinder(Cylinder): x-coordinate of the center of the cylinder y0 : float y-coordinate of the center of the cylinder - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1294,12 +1291,12 @@ class ZCylinder(Cylinder): _type = 'z-cylinder' _coeff_keys = ('x0', 'y0', 'r') - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., r=1., name='', *, R=None): + def __init__(self, x0=0., y0=0., r=1., boundary_type='transmission', + name='', surface_id=None, *, R=None): if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R - super().__init__(surface_id, boundary_type, r, name=name) + super().__init__(r, boundary_type, name, surface_id) self.x0 = x0 self.y0 = y0 @@ -1400,13 +1397,6 @@ class Sphere(Surface): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. x0 : float, optional x-coordinate of the center of the sphere. Defaults to 0. y0 : float, optional @@ -1415,8 +1405,15 @@ class Sphere(Surface): z-coordinate of the center of the sphere. Defaults to 0. r : float, optional Radius of the sphere. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the sphere. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -1428,7 +1425,7 @@ class Sphere(Surface): z-coordinate of the center of the sphere r : float Radius of the sphere - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1445,8 +1442,8 @@ class Sphere(Surface): _type = 'sphere' _coeff_keys = ('x0', 'y0', 'z0', 'r') - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., r=1., name='', *, R=None): + def __init__(self, x0=0., y0=0., z0=0., r=1., boundary_type='transmission', + name='', surface_id=None, *, R=None): if R is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r', 'R'), FutureWarning) r = R @@ -1574,21 +1571,21 @@ class Cone(Surface): Parameters ---------- + x0 : float, optional + x-coordinate of the apex. Defaults to 0. + y0 : float, optional + y-coordinate of the apex. Defaults to 0. + z0 : float, optional + z-coordinate of the apex. Defaults to 0. + r2 : float, optional + Parameter related to the aperature. Defaults to 1. surface_id : int, optional Unique identifier for the surface. If not specified, an identifier will automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. - x0 : float, optional - x-coordinate of the apex. Defaults to 0. - y0 : float - y-coordinate of the apex. Defaults to 0. - z0 : float - z-coordinate of the apex. Defaults to 0. - r2 : float - Parameter related to the aperature. Defaults to 1. name : str Name of the cone. If not specified, the name will be the empty string. @@ -1602,7 +1599,7 @@ class Cone(Surface): z-coordinate of the apex r2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1618,8 +1615,8 @@ class Cone(Surface): _coeff_keys = ('x0', 'y0', 'z0', 'r2') - def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., r2=1., name='', *, R2=None): + def __init__(self, x0=0., y0=0., z0=0., r2=1., boundary_type='transmission', + name='', surface_id=None, *, R2=None): if R2 is not None: warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), FutureWarning) r2 = R2 @@ -1695,13 +1692,6 @@ class XCone(Cone): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. x0 : float, optional x-coordinate of the apex. Defaults to 0. y0 : float, optional @@ -1710,8 +1700,15 @@ class XCone(Cone): z-coordinate of the apex. Defaults to 0. r2 : float, optional Parameter related to the aperature. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the cone. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -1723,7 +1720,7 @@ class XCone(Cone): z-coordinate of the apex r2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1766,13 +1763,6 @@ class YCone(Cone): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. x0 : float, optional x-coordinate of the apex. Defaults to 0. y0 : float, optional @@ -1781,8 +1771,15 @@ class YCone(Cone): z-coordinate of the apex. Defaults to 0. r2 : float, optional Parameter related to the aperature. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the cone. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -1794,7 +1791,7 @@ class YCone(Cone): z-coordinate of the apex r2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1837,13 +1834,6 @@ class ZCone(Cone): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective'}, optional - Boundary condition that defines the behavior for particles hitting the - surface. Defaults to transmissive boundary condition where particles - freely pass through the surface. x0 : float, optional x-coordinate of the apex. Defaults to 0. y0 : float, optional @@ -1852,8 +1842,15 @@ class ZCone(Cone): z-coordinate of the apex. Defaults to 0. r2 : float, optional Parameter related to the aperature. Defaults to 1. + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'}, optional + Boundary condition that defines the behavior for particles hitting the + surface. Defaults to transmissive boundary condition where particles + freely pass through the surface. name : str, optional Name of the cone. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- @@ -1865,7 +1862,7 @@ class ZCone(Cone): z-coordinate of the apex r2 : float Parameter related to the aperature - boundary_type : {'transmission, 'vacuum', 'reflective'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1908,23 +1905,23 @@ class Quadric(Surface): Parameters ---------- - surface_id : int, optional - Unique identifier for the surface. If not specified, an identifier will - automatically be assigned. - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}, optional + a, b, c, d, e, f, g, h, j, k : float, optional + coefficients for the surface. All default to 0. + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'}, optional Boundary condition that defines the behavior for particles hitting the surface. Defaults to transmissive boundary condition where particles freely pass through the surface. - a, b, c, d, e, f, g, h, j, k : float, optional - coefficients for the surface. All default to 0. name : str, optional Name of the surface. If not specified, the name will be the empty string. + surface_id : int, optional + Unique identifier for the surface. If not specified, an identifier will + automatically be assigned. Attributes ---------- a, b, c, d, e, f, g, h, j, k : float coefficients for the surface - boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'} + boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic', 'white'} Boundary condition that defines the behavior for particles hitting the surface. coefficients : dict @@ -1941,9 +1938,8 @@ class Quadric(Surface): _type = 'quadric' _coeff_keys = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k') - 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=''): + def __init__(self, a=0., b=0., c=0., d=0., e=0., f=0., g=0., h=0., j=0., + k=0., boundary_type='transmission', name='', surface_id=None): super().__init__(surface_id, boundary_type, name=name) self.a = a self.b = b diff --git a/openmc/volume.py b/openmc/volume.py index 7ac4fdd0c6..a59fb2acf6 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -45,6 +45,8 @@ class VolumeCalculation(object): Lower-left coordinates of bounding box used to sample points upper_right : Iterable of float Upper-right coordinates of bounding box used to sample points + threshold : float + Threshold for the maximum standard deviation of volume in the calculation atoms : dict Dictionary mapping unique IDs of domains to a mapping of nuclides to total number of atoms for each nuclide present in the domain. For @@ -54,12 +56,20 @@ class VolumeCalculation(object): in each domain specified. volumes : dict Dictionary mapping unique IDs of domains to estimated volumes in cm^3. + threshold : float + Threshold for the maxmimum standard deviation of volumes. + trigger_type : {'variance', 'std_dev', 'rel_err'} + Value type used to halt volume calculation + iterations : int + Number of iterations over samples (for calculations with a trigger). """ - def __init__(self, domains, samples, lower_left=None, - upper_right=None): + def __init__(self, domains, samples, lower_left=None, upper_right=None): self._atoms = {} self._volumes = {} + self._threshold = None + self._trigger_type = None + self._iterations = None cv.check_type('domains', domains, Iterable, (openmc.Cell, openmc.Material, openmc.Universe)) @@ -123,6 +133,18 @@ class VolumeCalculation(object): def upper_right(self): return self._upper_right + @property + def threshold(self): + return self._threshold + + @property + def trigger_type(self): + return self._trigger_type + + @property + def iterations(self): + return self._iterations + @property def domain_type(self): return self._domain_type @@ -170,6 +192,26 @@ class VolumeCalculation(object): cv.check_length(name, upper_right, 3) self._upper_right = upper_right + @threshold.setter + def threshold(self, threshold): + name = 'volume std. dev. threshold' + cv.check_type(name, threshold, Real) + cv.check_greater_than(name, threshold, 0.0) + self._threshold = threshold + + @trigger_type.setter + def trigger_type(self, trigger_type): + cv.check_value('tally trigger type', trigger_type, + ('variance', 'std_dev', 'rel_err')) + self._trigger_type = trigger_type + + @iterations.setter + def iterations(self, iterations): + name = 'volume calculation iterations' + cv.check_type(name, iterations, Integral) + cv.check_greater_than(name, iterations, 0) + self._iterations = iterations + @volumes.setter def volumes(self, volumes): cv.check_type('volumes', volumes, Mapping) @@ -180,6 +222,19 @@ class VolumeCalculation(object): cv.check_type('atoms', atoms, Mapping) self._atoms = atoms + def set_trigger(self, threshold, trigger_type): + """Set a trigger on the voulme calculation + + Parameters + ---------- + threshold : float + Threshold for the maxmimum standard deviation of volumes + trigger_type : {'variance', 'std_dev', 'rel_err'} + Value type used to halt volume calculation + """ + self.trigger_type = trigger_type + self.threshold = threshold + @classmethod def from_hdf5(cls, filename): """Load stochastic volume calculation results from HDF5 file. @@ -203,6 +258,10 @@ class VolumeCalculation(object): lower_left = f.attrs['lower_left'] upper_right = f.attrs['upper_right'] + threshold = f.attrs.get('threshold') + trigger_type = f.attrs.get('trigger_type') + iterations = f.attrs.get('iterations', 1) + volumes = {} atoms = {} ids = [] @@ -212,13 +271,12 @@ class VolumeCalculation(object): ids.append(domain_id) group = f[obj_name] volume = ufloat(*group['volume'][()]) + volumes[domain_id] = volume nucnames = group['nuclides'][()] atoms_ = group['atoms'][()] - atom_dict = OrderedDict() for name_i, atoms_i in zip(nucnames, atoms_): atom_dict[name_i.decode()] = ufloat(*atoms_i) - volumes[domain_id] = volume atoms[domain_id] = atom_dict # Instantiate some throw-away domains that are used by the constructor @@ -234,6 +292,11 @@ class VolumeCalculation(object): # Instantiate the class and assign results vol = cls(domains, samples, lower_left, upper_right) + + if trigger_type is not None: + vol.set_trigger(threshold, trigger_type.decode()) + + vol.iterations = iterations vol.volumes = volumes vol.atoms = atoms return vol @@ -278,4 +341,8 @@ class VolumeCalculation(object): ll_elem.text = ' '.join(str(x) for x in self.lower_left) ur_elem = ET.SubElement(element, "upper_right") ur_elem.text = ' '.join(str(x) for x in self.upper_right) + if self.threshold: + trigger_elem = ET.SubElement(element, "threshold") + trigger_elem.set("type", self.trigger_type) + trigger_elem.set("threshold", str(self.threshold)) return element diff --git a/setup.py b/setup.py index 712244efb4..aff7834d7b 100755 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ kwargs = { # Data files and librarries 'package_data': { - 'openmc.capi': ['libopenmc.{}'.format(suffix)], + 'openmc.lib': ['libopenmc.{}'.format(suffix)], 'openmc.data': ['mass16.txt', 'BREMX.DAT', '*.h5'] }, diff --git a/src/cell.cpp b/src/cell.cpp index cfb717c85e..3a7d0cdd46 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,8 +1,10 @@ #include "openmc/cell.h" +#include #include #include +#include #include #include #include @@ -438,35 +440,39 @@ CSGCell::CSGCell(pugi::xml_node cell_node) } auto rot {get_node_array(cell_node, "rotation")}; - if (rot.size() != 3) { + if (rot.size() != 3 && rot.size() != 9) { std::stringstream err_msg; err_msg << "Non-3D rotation vector applied to cell " << id_; fatal_error(err_msg); } - // Store the rotation angles. - rotation_.reserve(12); - rotation_.push_back(rot[0]); - rotation_.push_back(rot[1]); - rotation_.push_back(rot[2]); - // Compute and store the rotation matrix. - auto phi = -rot[0] * PI / 180.0; - auto theta = -rot[1] * PI / 180.0; - auto psi = -rot[2] * PI / 180.0; - rotation_.push_back(std::cos(theta) * std::cos(psi)); - rotation_.push_back(-std::cos(phi) * std::sin(psi) - + std::sin(phi) * std::sin(theta) * std::cos(psi)); - rotation_.push_back(std::sin(phi) * std::sin(psi) - + std::cos(phi) * std::sin(theta) * std::cos(psi)); - rotation_.push_back(std::cos(theta) * std::sin(psi)); - rotation_.push_back(std::cos(phi) * std::cos(psi) - + std::sin(phi) * std::sin(theta) * std::sin(psi)); - rotation_.push_back(-std::sin(phi) * std::cos(psi) - + std::cos(phi) * std::sin(theta) * std::sin(psi)); - rotation_.push_back(-std::sin(theta)); - rotation_.push_back(std::sin(phi) * std::cos(theta)); - rotation_.push_back(std::cos(phi) * std::cos(theta)); + rotation_.reserve(rot.size() == 9 ? 9 : 12); + if (rot.size() == 3) { + double phi = -rot[0] * PI / 180.0; + double theta = -rot[1] * PI / 180.0; + double psi = -rot[2] * PI / 180.0; + rotation_.push_back(std::cos(theta) * std::cos(psi)); + rotation_.push_back(-std::cos(phi) * std::sin(psi) + + std::sin(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::sin(phi) * std::sin(psi) + + std::cos(phi) * std::sin(theta) * std::cos(psi)); + rotation_.push_back(std::cos(theta) * std::sin(psi)); + rotation_.push_back(std::cos(phi) * std::cos(psi) + + std::sin(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(phi) * std::cos(psi) + + std::cos(phi) * std::sin(theta) * std::sin(psi)); + rotation_.push_back(-std::sin(theta)); + rotation_.push_back(std::sin(phi) * std::cos(theta)); + rotation_.push_back(std::cos(phi) * std::cos(theta)); + + // When user specifies angles, write them at end of vector + rotation_.push_back(rot[0]); + rotation_.push_back(rot[1]); + rotation_.push_back(rot[2]); + } else { + std::copy(rot.begin(), rot.end(), std::back_inserter(rotation_)); + } } } @@ -578,8 +584,12 @@ CSGCell::to_hdf5(hid_t cell_group) const write_dataset(group, "translation", translation_); } if (!rotation_.empty()) { - std::array rot {rotation_[0], rotation_[1], rotation_[2]}; - write_dataset(group, "rotation", rot); + if (rotation_.size() == 12) { + std::array rot {rotation_[9], rotation_[10], rotation_[11]}; + write_dataset(group, "rotation", rot); + } else { + write_dataset(group, "rotation", rotation_); + } } } else if (type_ == FILL_LATTICE) { @@ -598,81 +608,89 @@ BoundingBox CSGCell::bounding_box_simple() const { return bbox; } +void CSGCell::apply_demorgan(std::vector::iterator start, + std::vector::iterator stop) +{ + while (start < stop) { + if (*start < OP_UNION) { *start *= -1; } + else if (*start == OP_UNION) { *start = OP_INTERSECTION; } + else if (*start == OP_INTERSECTION) { *start = OP_UNION; } + start++; + } +} -void CSGCell::apply_demorgan(std::vector& rpn) { - for (auto& token : rpn) { - if (token < OP_UNION) { token *= -1; } - else if (token == OP_UNION) { token = OP_INTERSECTION; } - else if (token == OP_INTERSECTION) { token = OP_UNION; } +std::vector::iterator +CSGCell::find_left_parenthesis(std::vector::iterator start, + const std::vector& rpn) { + // start search at zero + int parenthesis_level = 0; + auto it = start; + while (it != rpn.begin()) { + // look at two tokens at a time + int32_t one = *it; + int32_t two = *(it - 1); + + // decrement parenthesis level if there are two adjacent surfaces + if (one < OP_UNION && two < OP_UNION) { + parenthesis_level--; + // increment if there are two adjacent operators + } else if (one >= OP_UNION && two >= OP_UNION) { + parenthesis_level++; + } + + // if the level gets to zero, return the position + if (parenthesis_level == 0) { + // move the iterator back one before leaving the loop + // so that all tokens in the parenthesis block are included + it--; + break; + } + + // continue loop, one token at a time + it--; + } + return it; +} + +void CSGCell::remove_complement_ops(std::vector& rpn) { + auto it = std::find(rpn.begin(), rpn.end(), OP_COMPLEMENT); + while (it != rpn.end()) { + // find the opening parenthesis (if any) + auto left = find_left_parenthesis(it, rpn); + std::vector tmp(left, it+1); + + // apply DeMorgan's law to any surfaces/operators between these + // positions in the RPN + apply_demorgan(left, it); + // remove complement operator + rpn.erase(it); + // update iterator position + it = std::find(rpn.begin(), rpn.end(), OP_COMPLEMENT); } } BoundingBox CSGCell::bounding_box_complex(std::vector rpn) { + // remove complements by adjusting surface signs and operators + remove_complement_ops(rpn); - // if the last operator is a complement op, there is no - // sub-region that the complement connects to. This indicates - // that the entire region is a complement and we can apply - // De Morgan's laws immediately - if (rpn.back() == OP_COMPLEMENT) { - rpn.pop_back(); - apply_demorgan(rpn); - } + std::vector stack(rpn.size()); + int i_stack = -1; - // reverse the rpn to make popping easier - std::reverse(rpn.begin(), rpn.end()); - - BoundingBox current = model::surfaces[abs(rpn.back()) - 1]->bounding_box(rpn.back() > 0); - rpn.pop_back(); - - while (rpn.size()) { - // move through the rpn in twos - int32_t one = rpn.back(); rpn.pop_back(); - int32_t two = rpn.back(); rpn.pop_back(); - - // the first token should always be a surface - Expects(one < OP_UNION); - - if (two >= OP_UNION) { - if (two == OP_UNION) { - current |= model::surfaces[abs(one)-1]->bounding_box(one > 0); - } else if (two == OP_INTERSECTION) { - current &= model::surfaces[abs(one)-1]->bounding_box(one > 0); - } + for (auto& token : rpn) { + if (token == OP_UNION) { + stack[i_stack - 1] = stack[i_stack - 1] | stack[i_stack]; + i_stack--; + } else if (token == OP_INTERSECTION) { + stack[i_stack - 1] = stack[i_stack - 1] & stack[i_stack]; + i_stack--; } else { - // two surfaces in a row (left parenthesis), - // create sub-rpn for region in parenthesis - std::vector subrpn; - subrpn.push_back(one); - subrpn.push_back(two); - // add until last two tokens in the sub-rpn are operators - // (indicates a right parenthesis) - while (!((subrpn.back() >= OP_UNION) && (*(subrpn.rbegin() + 1) >= OP_UNION))) { - subrpn.push_back(rpn.back()); - rpn.pop_back(); - } - - // handle complement case using De Morgan's laws - if (subrpn.back() == OP_COMPLEMENT) { - subrpn.pop_back(); - apply_demorgan(subrpn); - subrpn.push_back(rpn.back()); - rpn.pop_back(); - } - // save the last operator, tells us how to combine this region - // with our current bounding box - int32_t op = subrpn.back(); subrpn.pop_back(); - // get bounding box for the subrpn - BoundingBox sub_box = bounding_box_complex(subrpn); - // combine the sub-rpn bounding box with our current cell box - if (op == OP_UNION) { - current |= sub_box; - } else if (op == OP_INTERSECTION) { - current &= sub_box; - } + i_stack++; + stack[i_stack] = model::surfaces[abs(token) - 1]->bounding_box(token > 0); } } - return current; + Ensures(i_stack == 0); + return stack.front(); } BoundingBox CSGCell::bounding_box() const { diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 04d4f08c12..9d14c1b864 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -268,14 +268,12 @@ void load_dagmc_geometry() std::string cmp_str = mat_value; to_lower(cmp_str); - if (cmp_str.find("graveyard") != std::string::npos) { + if (cmp_str == "graveyard") { graveyard = vol_handle; } // material void checks - if (cmp_str.find("void") != std::string::npos || - cmp_str.find("vacuum") != std::string::npos || - cmp_str.find("graveyard") != std::string::npos) { + if (cmp_str == "void" || cmp_str == "vacuum" || cmp_str == "graveyard") { c->material_.push_back(MATERIAL_VOID); } else { if (using_uwuw) { diff --git a/src/distribution_multi.cpp b/src/distribution_multi.cpp index d4a728c71f..c388254b6e 100644 --- a/src/distribution_multi.cpp +++ b/src/distribution_multi.cpp @@ -61,6 +61,12 @@ Direction PolarAzimuthal::sample() const // Sample azimuthal angle double phi = phi_->sample(); + + // If the reference direction is along the z-axis, rotate the aziumthal angle + // to match spherical coordinate conventions. + // TODO: apply this change directly to rotate_angle + if (u_ref_.x == 0 && u_ref_.y == 0) phi += 0.5*PI; + return rotate_angle(u_ref_, mu, &phi); } diff --git a/src/finalize.cpp b/src/finalize.cpp index efd3828306..a7012ae197 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -129,9 +129,7 @@ int openmc_finalize() // Free all MPI types #ifdef OPENMC_MPI - int init_called; - MPI_Initialized(&init_called); - if (init_called) MPI_Type_free(&mpi::bank); + if (mpi::bank != MPI_DATATYPE_NULL) MPI_Type_free(&mpi::bank); #endif return 0; diff --git a/src/material.cpp b/src/material.cpp index 685fd1da2e..9a1e206ac8 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -873,8 +873,8 @@ void Material::set_id(int32_t id) // If no ID specified, auto-assign next ID in sequence if (id == -1) { id = 0; - for (const auto& f : model::materials) { - id = std::max(id, f->id_); + for (const auto& m : model::materials) { + id = std::max(id, m->id_); } ++id; } diff --git a/src/math_functions.cpp b/src/math_functions.cpp index ec5d64eac7..7525a7ddda 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -665,6 +665,12 @@ Direction rotate_angle(Direction u, double mu, const double* phi) return {mu*u.x + a*(u.x*u.y*cosphi + u.z*sinphi) / b, mu*u.y - a*b*cosphi, mu*u.z + a*(u.y*u.z*cosphi - u.x*sinphi) / b}; + // TODO: use the following code to make PolarAzimuthal distributions match + // spherical coordinate conventions. Remove the related fixup code in + // PolarAzimuthal::sample. + //return {mu*u.x + a*(-u.x*u.y*sinphi + u.z*cosphi) / b, + // mu*u.y + a*b*sinphi, + // mu*u.z - a*(u.y*u.z*sinphi + u.x*cosphi) / b}; } } diff --git a/src/mesh.cpp b/src/mesh.cpp index ac02b679e8..9e27fc4e6a 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -155,7 +155,8 @@ RegularMesh::RegularMesh(pugi::xml_node node) fatal_error("Must specify either and on a mesh."); } - if (shape_.dimension() > 0) { + // TODO: Change to zero when xtensor is updated + if (shape_.size() > 1) { if (shape_.size() != lower_left_.size()) { fatal_error("Number of entries on must be the same " "as the number of entries on ."); @@ -523,6 +524,14 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, } r1 = r; + // The TINY_BIT offsets above mean that the preceding logic cannot always find + // the correct ijk0 and ijk1 indices. For tracks shorter than 2*TINY_BIT, just + // assume the track lies in only one mesh bin. These tracks are very short so + // any error caused by this assumption will be small. + if (total_distance < 2*TINY_BIT) { + for (int i = 0; i < n; ++i) ijk0[i] = ijk1[i]; + } + // ======================================================================== // Find which mesh cells are traversed and the length of each traversal. @@ -898,6 +907,14 @@ void RectilinearMesh::bins_crossed(const Particle* p, std::vector& bins, } r1 = r; + // The TINY_BIT offsets above mean that the preceding logic cannot always find + // the correct ijk0 and ijk1 indices. For tracks shorter than 2*TINY_BIT, just + // assume the track lies in only one mesh bin. These tracks are very short so + // any error caused by this assumption will be small. + if (total_distance < 2*TINY_BIT) { + for (int i = 0; i < 3; ++i) ijk0[i] = ijk1[i]; + } + // ======================================================================== // Find which mesh cells are traversed and the length of each traversal. diff --git a/src/output.cpp b/src/output.cpp index 6516b48b61..d3262cc4d5 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -620,7 +620,6 @@ const std::unordered_map score_names = { {SCORE_FISS_Q_PROMPT, "Prompt fission power"}, {SCORE_FISS_Q_RECOV, "Recoverable fission power"}, {SCORE_CURRENT, "Current"}, - {SCORE_HEATING, "Heating"}, }; //! Create an ASCII output file showing all tally results. @@ -639,6 +638,16 @@ write_tallies() for (auto i_tally = 0; i_tally < model::tallies.size(); ++i_tally) { const auto& tally {*model::tallies[i_tally]}; + // Write header block. + std::string tally_header("TALLY " + std::to_string(tally.id_)); + if (!tally.name_.empty()) tally_header += ": " + tally.name_; + tallies_out << header(tally_header) << "\n\n"; + + if (!tally.writable_) { + tallies_out << " Internal\n\n"; + continue; + } + // Calculate t-value for confidence intervals double t_value = 1; if (settings::confidence_intervals) { @@ -646,11 +655,6 @@ write_tallies() t_value = t_percentile(1 - alpha*0.5, tally.n_realizations_ - 1); } - // Write header block. - std::string tally_header("TALLY " + std::to_string(tally.id_)); - if (!tally.name_.empty()) tally_header += ": " + tally.name_; - tallies_out << header(tally_header) << "\n\n"; - // Write derivative information. if (tally.deriv_ != C_NONE) { const auto& deriv {model::tally_derivs[tally.deriv_]}; diff --git a/src/particle.cpp b/src/particle.cpp index 1fb1eadb09..bf107a741c 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -428,7 +428,8 @@ Particle::cross_surface() } return; - } else if (surf->bc_ == BC_REFLECT && (settings::run_mode != RUN_MODE_PLOTTING)) { + } else if ((surf->bc_ == BC_REFLECT || surf->bc_ == BC_WHITE) + && (settings::run_mode != RUN_MODE_PLOTTING)) { // ======================================================================= // PARTICLE REFLECTS FROM SURFACE @@ -457,10 +458,11 @@ Particle::cross_surface() score_surface_tally(this, model::active_meshsurf_tallies); this->r() = r; } - - // Reflect particle off surface - Direction u = surf->reflect(this->r(), this->u()); - + + Direction u = (surf->bc_ == BC_REFLECT) ? + surf->reflect(this->r(), this->u()) : + surf->diffuse_reflect(this->r(), this->u()); + // Make sure new particle direction is normalized this->u() = u / u.norm(); diff --git a/src/plot.cpp b/src/plot.cpp index 98a3645c76..ba63da16b1 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -908,11 +908,13 @@ void create_voxel(Plot pl) // generate ids using plotbase IdData ids = pltbase.get_map(); - // select only cell ID data and flip the y-axis - xt::xtensor data1 = xt::flip(xt::view(ids.data_, xt::all(), xt::all(), 0), 0); + // select only cell/material ID data and flip the y-axis + int idx = pl.color_by_ == PlotColorBy::cells ? 0 : 1; + xt::xtensor data_slice = xt::view(ids.data_, xt::all(), xt::all(), idx); + xt::xtensor data_flipped = xt::flip(data_slice, 0); // Write to HDF5 dataset - voxel_write_slice(z, dspace, dset, memspace, &(data1(0,0))); + voxel_write_slice(z, dspace, dset, memspace, data_flipped.data()); } voxel_finalize(dspace, dset, memspace); diff --git a/src/position.cpp b/src/position.cpp index 1215bfb12e..d29e8d81ca 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -88,9 +88,9 @@ Position Position::rotate(const std::vector& rotation) const { return { + x*rotation[0] + y*rotation[1] + z*rotation[2], x*rotation[3] + y*rotation[4] + z*rotation[5], - x*rotation[6] + y*rotation[7] + z*rotation[8], - x*rotation[9] + y*rotation[10] + z*rotation[11] + x*rotation[6] + y*rotation[7] + z*rotation[8] }; } diff --git a/src/reaction.cpp b/src/reaction.cpp index 31bf8efd33..f7f1e87acb 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -88,193 +88,189 @@ Reaction::Reaction(hid_t group, const std::vector& temperatures) std::string reaction_name(int mt) { if (mt == SCORE_FLUX) { - return "flux"; + return "flux"; } else if (mt == SCORE_TOTAL) { - return "total"; + return "total"; } else if (mt == SCORE_SCATTER) { - return "scatter"; + return "scatter"; } else if (mt == SCORE_NU_SCATTER) { - return "nu-scatter"; + return "nu-scatter"; } else if (mt == SCORE_ABSORPTION) { - return "absorption"; + return "absorption"; } else if (mt == SCORE_FISSION) { - return "fission"; + return "fission"; } else if (mt == SCORE_NU_FISSION) { - return "nu-fission"; + return "nu-fission"; } else if (mt == SCORE_DECAY_RATE) { - return "decay-rate"; + return "decay-rate"; } else if (mt == SCORE_DELAYED_NU_FISSION) { - return "delayed-nu-fission"; + return "delayed-nu-fission"; } else if (mt == SCORE_PROMPT_NU_FISSION) { - return "prompt-nu-fission"; + return "prompt-nu-fission"; } else if (mt == SCORE_KAPPA_FISSION) { - return "kappa-fission"; + return "kappa-fission"; } else if (mt == SCORE_CURRENT) { - return "current"; + return "current"; } else if (mt == SCORE_EVENTS) { - return "events"; + return "events"; } else if (mt == SCORE_INVERSE_VELOCITY) { - return "inverse-velocity"; + return "inverse-velocity"; } else if (mt == SCORE_FISS_Q_PROMPT) { - return "fission-q-prompt"; + return "fission-q-prompt"; } else if (mt == SCORE_FISS_Q_RECOV) { - return "fission-q-recoverable"; - } else if (mt == SCORE_HEATING) { - return "heating"; + return "fission-q-recoverable"; // Normal ENDF-based reactions } else if (mt == TOTAL_XS) { - return "(n,total)"; + return "(n,total)"; } else if (mt == ELASTIC) { - return "(n,elastic)"; + return "(n,elastic)"; } else if (mt == N_LEVEL) { - return "(n,level)"; + return "(n,level)"; } else if (mt == N_2ND) { - return "(n,2nd)"; + return "(n,2nd)"; } else if (mt == N_2N) { - return "(n,2n)"; + return "(n,2n)"; } else if (mt == N_3N) { - return "(n,3n)"; + return "(n,3n)"; } else if (mt == N_FISSION) { - return "(n,fission)"; + return "(n,fission)"; } else if (mt == N_F) { - return "(n,f)"; + return "(n,f)"; } else if (mt == N_NF) { - return "(n,nf)"; + return "(n,nf)"; } else if (mt == N_2NF) { - return "(n,2nf)"; + return "(n,2nf)"; } else if (mt == N_NA) { - return "(n,na)"; + return "(n,na)"; } else if (mt == N_N3A) { - return "(n,n3a)"; + return "(n,n3a)"; } else if (mt == N_2NA) { - return "(n,2na)"; + return "(n,2na)"; } else if (mt == N_3NA) { - return "(n,3na)"; + return "(n,3na)"; } else if (mt == N_NP) { - return "(n,np)"; + return "(n,np)"; } else if (mt == N_N2A) { - return "(n,n2a)"; + return "(n,n2a)"; } else if (mt == N_2N2A) { - return "(n,2n2a)"; + return "(n,2n2a)"; } else if (mt == N_ND) { - return "(n,nd)"; + return "(n,nd)"; } else if (mt == N_NT) { - return "(n,nt)"; + return "(n,nt)"; } else if (mt == N_N3HE) { - return "(n,nHe-3)"; + return "(n,nHe-3)"; } else if (mt == N_ND2A) { - return "(n,nd2a)"; + return "(n,nd2a)"; } else if (mt == N_NT2A) { - return "(n,nt2a)"; + return "(n,nt2a)"; } else if (mt == N_4N) { - return "(n,4n)"; + return "(n,4n)"; } else if (mt == N_3NF) { - return "(n,3nf)"; + return "(n,3nf)"; } else if (mt == N_2NP) { - return "(n,2np)"; + return "(n,2np)"; } else if (mt == N_3NP) { - return "(n,3np)"; + return "(n,3np)"; } else if (mt == N_N2P) { - return "(n,n2p)"; + return "(n,n2p)"; } else if (mt == N_NPA) { - return "(n,npa)"; + return "(n,npa)"; } else if (N_N1 <= mt && mt <= N_N40) { - return "(n,n" + std::to_string(mt-50) + ")"; + return "(n,n" + std::to_string(mt-50) + ")"; } else if (mt == N_NC) { - return "(n,nc)"; + return "(n,nc)"; } else if (mt == N_DISAPPEAR) { - return "(n,disappear)"; + return "(n,disappear)"; } else if (mt == N_GAMMA) { - return "(n,gamma)"; + return "(n,gamma)"; } else if (mt == N_P) { - return "(n,p)"; + return "(n,p)"; } else if (mt == N_D) { - return "(n,d)"; + return "(n,d)"; } else if (mt == N_T) { - return "(n,t)"; + return "(n,t)"; } else if (mt == N_3HE) { - return "(n,3He)"; + return "(n,3He)"; } else if (mt == N_A) { - return "(n,a)"; + return "(n,a)"; } else if (mt == N_2A) { - return "(n,2a)"; + return "(n,2a)"; } else if (mt == N_3A) { - return "(n,3a)"; + return "(n,3a)"; } else if (mt == N_2P) { - return "(n,2p)"; + return "(n,2p)"; } else if (mt == N_PA) { - return "(n,pa)"; + return "(n,pa)"; } else if (mt == N_T2A) { - return "(n,t2a)"; + return "(n,t2a)"; } else if (mt == N_D2A) { - return "(n,d2a)"; + return "(n,d2a)"; } else if (mt == N_PD) { - return "(n,pd)"; + return "(n,pd)"; } else if (mt == N_PT) { - return "(n,pt)"; + return "(n,pt)"; } else if (mt == N_DA) { - return "(n,da)"; + return "(n,da)"; } else if (mt == 201) { - return "(n,Xn)"; + return "(n,Xn)"; } else if (mt == 202) { - return "(n,Xgamma)"; - } else if (mt == 203) { - return "(n,Xp)"; - } else if (mt == 204) { - return "(n,Xd)"; - } else if (mt == 205) { - return "(n,Xt)"; - } else if (mt == 206) { - return "(n,X3He)"; - } else if (mt == 207) { - return "(n,Xa)"; - } else if (mt == 301) { + return "(n,Xgamma)"; + } else if (mt == N_XP) { + return "(n,Xp)"; + } else if (mt == N_XD) { + return "(n,Xd)"; + } else if (mt == N_XT) { + return "(n,Xt)"; + } else if (mt == N_X3HE) { + return "(n,X3He)"; + } else if (mt == N_XA) { + return "(n,Xa)"; + } else if (mt == HEATING) { return "heating"; - } else if (mt == 318) { - return "fission-heating"; - } else if (mt == 999) { - return "non-fission-heating"; - } else if (mt == 444) { - return "damage-energy"; + } else if (mt == DAMAGE_ENERGY) { + return "damage-energy"; } else if (mt == COHERENT) { - return "coherent scatter"; + return "coherent scatter"; } else if (mt == INCOHERENT) { - return "incoherent scatter"; + return "incoherent scatter"; } else if (mt == PAIR_PROD_ELEC) { - return "pair production, electron"; + return "pair production, electron"; } else if (mt == PAIR_PROD) { - return "pair production"; + return "pair production"; } else if (mt == PAIR_PROD_NUC) { - return "pair production, nuclear"; + return "pair production, nuclear"; } else if (mt == PHOTOELECTRIC) { - return "photoelectric"; + return "photoelectric"; } else if (534 <= mt && mt <= 572) { std::stringstream name; name << "photoelectric, " << SUBSHELLS[mt - 534] << " subshell"; return name.str(); } else if (600 <= mt && mt <= 648) { - return "(n,p" + std::to_string(mt-600) + ")"; + return "(n,p" + std::to_string(mt-600) + ")"; } else if (mt == 649) { - return "(n,pc)"; + return "(n,pc)"; } else if (650 <= mt && mt <= 698) { - return "(n,d" + std::to_string(mt-650) + ")"; + return "(n,d" + std::to_string(mt-650) + ")"; } else if (mt == 699) { - return "(n,dc)"; + return "(n,dc)"; } else if (700 <= mt && mt <= 748) { - return "(n,t" + std::to_string(mt-700) + ")"; + return "(n,t" + std::to_string(mt-700) + ")"; } else if (mt == 749) { - return "(n,tc)"; + return "(n,tc)"; } else if (750 <= mt && mt <= 798) { - return "(n,3He" + std::to_string(mt-750) + ")"; + return "(n,3He" + std::to_string(mt-750) + ")"; } else if (mt == 799) { - return "(n,3Hec)"; + return "(n,3Hec)"; } else if (800 <= mt && mt <= 848) { - return "(n,a" + std::to_string(mt-800) + ")"; + return "(n,a" + std::to_string(mt-800) + ")"; } else if (mt == 849) { - return "(n,ac)"; + return "(n,ac)"; + } else if (mt == HEATING_LOCAL) { + return "heating-local"; } else { - return "MT=" + std::to_string(mt); + return "MT=" + std::to_string(mt); } } diff --git a/src/secondary_thermal.cpp b/src/secondary_thermal.cpp index 696907a17e..8c9cc69676 100644 --- a/src/secondary_thermal.cpp +++ b/src/secondary_thermal.cpp @@ -61,7 +61,7 @@ CoherentElasticAE::sample(double E_in, double& E_out, double& mu) const IncoherentElasticAE::IncoherentElasticAE(hid_t group) { - read_attribute(group, "debye_waller", debye_waller_); + read_dataset(group, "debye_waller", debye_waller_); } void diff --git a/src/settings.cpp b/src/settings.cpp index 1c9d1161a0..796a2862ad 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -512,7 +512,8 @@ void read_settings_xml() if (!m) fatal_error("Only regular meshes can be used as an entropy mesh"); simulation::entropy_mesh = m; - if (m->shape_.dimension() == 0) { + // TODO: Change to zero when xtensor is updated + if (m->shape_.size() == 1) { // If the user did not specify how many mesh cells are to be used in // each direction, we automatically determine an appropriate number of // cells diff --git a/src/state_point.cpp b/src/state_point.cpp index 7239f3284f..cd2c475d86 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -165,36 +165,43 @@ openmc_statepoint_write(const char* filename, bool* write_source) write_attribute(tallies_group, "ids", tally_ids); // Write all tally information except results - for (const auto& tally_ptr : model::tallies) { - const auto& tally {*tally_ptr}; + for (const auto& tally : model::tallies) { hid_t tally_group = create_group(tallies_group, - "tally " + std::to_string(tally.id_)); + "tally " + std::to_string(tally->id_)); - write_dataset(tally_group, "name", tally.name_); + write_dataset(tally_group, "name", tally->name_); - if (tally.estimator_ == ESTIMATOR_ANALOG) { + if (tally->writable_) { + write_attribute(tally_group, "internal", 0); + } else { + write_attribute(tally_group, "internal", 1); + close_group(tally_group); + continue; + } + + if (tally->estimator_ == ESTIMATOR_ANALOG) { write_dataset(tally_group, "estimator", "analog"); - } else if (tally.estimator_ == ESTIMATOR_TRACKLENGTH) { + } else if (tally->estimator_ == ESTIMATOR_TRACKLENGTH) { write_dataset(tally_group, "estimator", "tracklength"); - } else if (tally.estimator_ == ESTIMATOR_COLLISION) { + } else if (tally->estimator_ == ESTIMATOR_COLLISION) { write_dataset(tally_group, "estimator", "collision"); } - write_dataset(tally_group, "n_realizations", tally.n_realizations_); + write_dataset(tally_group, "n_realizations", tally->n_realizations_); // Write the ID of each filter attached to this tally - write_dataset(tally_group, "n_filters", tally.filters().size()); - if (!tally.filters().empty()) { + write_dataset(tally_group, "n_filters", tally->filters().size()); + if (!tally->filters().empty()) { std::vector filter_ids; - filter_ids.reserve(tally.filters().size()); - for (auto i_filt : tally.filters()) + filter_ids.reserve(tally->filters().size()); + for (auto i_filt : tally->filters()) filter_ids.push_back(model::tally_filters[i_filt]->id()); write_dataset(tally_group, "filters", filter_ids); } // Write the nuclides this tally scores std::vector nuclides; - for (auto i_nuclide : tally.nuclides_) { + for (auto i_nuclide : tally->nuclides_) { if (i_nuclide == -1) { nuclides.push_back("total"); } else { @@ -207,12 +214,12 @@ openmc_statepoint_write(const char* filename, bool* write_source) } write_dataset(tally_group, "nuclides", nuclides); - if (tally.deriv_ != C_NONE) write_dataset(tally_group, "derivative", - model::tally_derivs[tally.deriv_].id); + if (tally->deriv_ != C_NONE) write_dataset(tally_group, "derivative", + model::tally_derivs[tally->deriv_].id); // Write the tally score bins std::vector scores; - for (auto sc : tally.scores_) scores.push_back(reaction_name(sc)); + for (auto sc : tally->scores_) scores.push_back(reaction_name(sc)); write_dataset(tally_group, "n_score_bins", scores.size()); write_dataset(tally_group, "score_bins", scores); @@ -232,6 +239,7 @@ openmc_statepoint_write(const char* filename, bool* write_source) // Write all tally results for (const auto& tally : model::tallies) { + if (!tally->writable_) continue; // Write sum and sum_sq for each bin std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); @@ -425,11 +433,21 @@ void load_state_point() // Read sum, sum_sq, and N for each bin std::string name = "tally " + std::to_string(tally->id_); hid_t tally_group = open_group(tallies_group, name.c_str()); - auto& results = tally->results_; - read_tally_results(tally_group, results.shape()[0], - results.shape()[1], results.data()); - read_dataset(tally_group, "n_realizations", tally->n_realizations_); - close_group(tally_group); + + int internal=0; + if (attribute_exists(tally_group, "internal")) { + read_attribute(tally_group, "internal", internal); + } + if (internal) { + tally->writable_ = false; + } else { + + auto& results = tally->results_; + read_tally_results(tally_group, results.shape()[0], + results.shape()[1], results.data()); + read_dataset(tally_group, "n_realizations", tally->n_realizations_); + close_group(tally_group); + } } close_group(tallies_group); @@ -697,6 +715,7 @@ void write_tally_results_nr(hid_t file_id) for (const auto& t : model::tallies) { // Skip any tallies that are not active if (!t->active_) continue; + if (!t->writable_) continue; if (mpi::master && !object_exists(file_id, "tallies_present")) { write_attribute(file_id, "tallies_present", 1); diff --git a/src/surface.cpp b/src/surface.cpp index 14d077cfff..2354659520 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -11,6 +11,8 @@ #include "openmc/settings.h" #include "openmc/string_utils.h" #include "openmc/xml_interface.h" +#include "openmc/random_lcg.h" +#include "openmc/math_functions.h" namespace openmc { @@ -22,7 +24,7 @@ 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}; - +extern "C" const int BC_WHITE {4}; //============================================================================== // Global variables //============================================================================== @@ -147,6 +149,8 @@ Surface::Surface(pugi::xml_node surf_node) } else if (surf_bc == "reflective" || surf_bc == "reflect" || surf_bc == "reflecting") { bc_ = BC_REFLECT; + } else if (surf_bc == "white") { + bc_ = BC_WHITE; } else if (surf_bc == "periodic") { bc_ = BC_PERIODIC; } else { @@ -192,6 +196,27 @@ Surface::reflect(Position r, Direction u) const return u -= (2.0 * projection / magnitude) * n; } +Direction +Surface::diffuse_reflect(Position r, Direction u) const +{ + // Diffuse reflect direction according to the normal. + // cosine distribution + + Direction n = this->normal(r); + n /= n.norm(); + const double projection = n.dot(u); + + // sample from inverse function, u=sqrt(rand) since p(u)=2u, so F(u)=u^2 + const double mu = (projection>=0.0) ? + -std::sqrt(prn()) : std::sqrt(prn()); + + // sample azimuthal distribution uniformly + u = rotate_angle(n, mu, nullptr); + + // normalize the direction + return u/u.norm(); +} + CSGSurface::CSGSurface() : Surface{} {}; CSGSurface::CSGSurface(pugi::xml_node surf_node) : Surface{surf_node} {}; @@ -213,6 +238,9 @@ CSGSurface::to_hdf5(hid_t group_id) const case BC_REFLECT : write_string(surf_group, "boundary_type", "reflective", false); break; + case BC_WHITE : + write_string(surf_group, "boundary_type", "white", false); + break; case BC_PERIODIC : write_string(surf_group, "boundary_type", "periodic", false); break; diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 27d6cc58c0..f843874118 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -111,7 +111,10 @@ score_str_to_int(std::string score_str) return SCORE_FISS_Q_RECOV; if (score_str == "heating") - return SCORE_HEATING; + return HEATING; + + if (score_str == "heating-local") + return HEATING_LOCAL; if (score_str == "current") return SCORE_CURRENT; @@ -1217,6 +1220,30 @@ openmc_tally_set_active(int32_t index, bool active) return 0; } +extern "C" int +openmc_tally_get_writable(int32_t index, bool* writable) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + *writable = model::tallies[index]->writable(); + + return 0; +} + +extern "C" int +openmc_tally_set_writable(int32_t index, bool writable) +{ + if (index < 0 || index >= model::tallies.size()) { + set_errmsg("Index in tallies array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + model::tallies[index]->set_writable(writable); + + return 0; +} + extern "C" int openmc_tally_get_scores(int32_t index, int** scores, int* n) { diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index 1f6a8010a5..37310070fe 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -192,7 +192,7 @@ double get_nuc_fission_q(const Nuclide& nuc, const Particle* p, int score_bin) //! Pulled out to support both the fission_q scores and energy deposition //! score -double score_fission_q(const Particle* p, int score_bin, const Tally& tally, +double score_fission_q(const Particle* p, int score_bin, const Tally& tally, double flux, int i_nuclide, double atom_density) { if (tally.estimator_ == ESTIMATOR_ANALOG) { @@ -231,7 +231,7 @@ double score_fission_q(const Particle* p, int score_bin, const Tally& tally, auto j_nuclide = material.nuclide_[i]; auto atom_density = material.atom_density_(i); const Nuclide& nuc {*data::nuclides[j_nuclide]}; - score += get_nuc_fission_q(nuc, p, score_bin) * atom_density + score += get_nuc_fission_q(nuc, p, score_bin) * atom_density * p->neutron_xs_[j_nuclide].fission; } return score * flux; @@ -268,7 +268,7 @@ double score_neutron_heating(const Particle* p, const Tally& tally, double flux, // Get heating macroscopic "cross section" double heating_xs; if (i_nuclide >= 0) { - const Nuclide& nuc {*data::nuclides[i_nuclide]}; + const Nuclide& nuc {*data::nuclides[i_nuclide]}; heating_xs = get_nuclide_neutron_heating(p, nuc, rxn_bin, i_nuclide); if (tally.estimator_ == ESTIMATOR_ANALOG) { heating_xs /= p->neutron_xs_[i_nuclide].total; @@ -1210,10 +1210,10 @@ score_general_ce(Particle* p, int i_tally, int start_index, break; - case SCORE_HEATING: + case HEATING: score = 0.; if (p->type_ == Particle::Type::neutron) { - score = score_neutron_heating(p, tally, flux, NEUTRON_HEATING, + score = score_neutron_heating(p, tally, flux, HEATING, i_nuclide, atom_density); } else if (p->type_ == Particle::Type::photon) { if (tally.estimator_ == ESTIMATOR_ANALOG) { diff --git a/src/thermal.cpp b/src/thermal.cpp index 5cc395e13c..49b5223763 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -210,22 +210,15 @@ ThermalData::ThermalData(hid_t group) if (temp == "coherent_elastic") { auto xs = dynamic_cast(elastic_.xs.get()); elastic_.distribution = std::make_unique(*xs); - - // Set threshold energy - threshold_elastic_ = xs->bragg_edges().back(); - } else { - auto xs = dynamic_cast(elastic_.xs.get()); if (temp == "incoherent_elastic") { elastic_.distribution = std::make_unique(dgroup); } else if (temp == "incoherent_elastic_discrete") { + auto xs = dynamic_cast(elastic_.xs.get()); elastic_.distribution = std::make_unique( dgroup, xs->x() ); } - - // Set threshold energy - threshold_elastic_ = xs->x().back(); } close_group(elastic_group); @@ -239,10 +232,6 @@ ThermalData::ThermalData(hid_t group) // Read inelastic cross section inelastic_.xs = read_function(inelastic_group, "xs"); - // Set inelastic threshold - auto xs = dynamic_cast(inelastic_.xs.get()); - threshold_inelastic_ = xs->x().back(); - // Read angle-energy distribution hid_t dgroup = open_group(inelastic_group, "distribution"); std::string temp; @@ -250,6 +239,7 @@ ThermalData::ThermalData(hid_t group) if (temp == "incoherent_inelastic") { inelastic_.distribution = std::make_unique(dgroup); } else if (temp == "incoherent_inelastic_discrete") { + auto xs = dynamic_cast(inelastic_.xs.get()); inelastic_.distribution = std::make_unique( dgroup, xs->x() ); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 93b729bd66..419bf0ea59 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -59,7 +59,32 @@ VolumeCalculation::VolumeCalculation(pugi::xml_node node) domain_ids_ = get_node_array(node, "domain_ids"); lower_left_ = get_node_array(node, "lower_left"); upper_right_ = get_node_array(node, "upper_right"); - n_samples_ = std::stoi(get_node_value(node, "samples")); + n_samples_ = std::stoull(get_node_value(node, "samples")); + + if (check_for_node(node, "threshold")) { + pugi::xml_node threshold_node = node.child("threshold"); + + threshold_ = std::stod(get_node_value(threshold_node, "threshold")); + if (threshold_ <= 0.0) { + std::stringstream msg; + msg << "Invalid error threshold " << threshold_ << " provided for a volume calculation."; + fatal_error(msg); + } + + std::string tmp = get_node_value(threshold_node, "type"); + if (tmp == "variance") { + trigger_type_ = TriggerMetric::variance; + } else if (tmp == "std_dev") { + trigger_type_ = TriggerMetric::standard_deviation; + } else if ( tmp == "rel_err") { + trigger_type_ = TriggerMetric::relative_error; + } else { + std::stringstream msg; + msg << "Invalid volume calculation trigger type '" << tmp << "' provided."; + fatal_error(msg); + } + + } // Ensure there are no duplicates by copying elements to a set and then // comparing the length with the original vector @@ -77,11 +102,12 @@ std::vector VolumeCalculation::execute() const int n = domain_ids_.size(); std::vector> master_indices(n); // List of material indices for each domain std::vector> master_hits(n); // Number of hits for each material in each domain + int iterations = 0; // Divide work over MPI processes - int min_samples = n_samples_ / mpi::n_procs; - int remainder = n_samples_ % mpi::n_procs; - int i_start, i_end; + size_t min_samples = n_samples_ / mpi::n_procs; + size_t remainder = n_samples_ % mpi::n_procs; + size_t i_start, i_end; if (mpi::rank < remainder) { i_start = (min_samples + 1)*mpi::rank; i_end = i_start + min_samples + 1; @@ -90,182 +116,230 @@ std::vector VolumeCalculation::execute() const i_end = i_start + min_samples; } - #pragma omp parallel - { - // Variables that are private to each thread - std::vector> indices(n); - std::vector> hits(n); - Particle p; + while (true) { - prn_set_stream(STREAM_VOLUME); + #pragma omp parallel + { + // Variables that are private to each thread + std::vector> indices(n); + std::vector> hits(n); + Particle p; - // Sample locations and count hits - #pragma omp for - for (int i = i_start; i < i_end; i++) { - set_particle_seed(i); + prn_set_stream(STREAM_VOLUME); - p.n_coord_ = 1; - Position xi {prn(), prn(), prn()}; - p.r() = lower_left_ + xi*(upper_right_ - lower_left_); - p.u() = {0.5, 0.5, 0.5}; + // Sample locations and count hits + #pragma omp for + for (size_t i = i_start; i < i_end; i++) { + set_particle_seed(iterations * n_samples_ + i); - // If this location is not in the geometry at all, move on to next block - if (!find_cell(&p, false)) continue; + p.n_coord_ = 1; + Position xi {prn(), prn(), prn()}; + p.r() = lower_left_ + xi*(upper_right_ - lower_left_); + p.u() = {0.5, 0.5, 0.5}; - if (domain_type_ == FILTER_MATERIAL) { - if (p.material_ != MATERIAL_VOID) { - for (int i_domain = 0; i_domain < n; i_domain++) { - if (model::materials[p.material_]->id_ == domain_ids_[i_domain]) { - this->check_hit(p.material_, indices[i_domain], hits[i_domain]); - break; + // If this location is not in the geometry at all, move on to next block + if (!find_cell(&p, false)) continue; + + if (domain_type_ == FILTER_MATERIAL) { + if (p.material_ != MATERIAL_VOID) { + for (int i_domain = 0; i_domain < n; i_domain++) { + if (model::materials[p.material_]->id_ == domain_ids_[i_domain]) { + this->check_hit(p.material_, indices[i_domain], hits[i_domain]); + break; + } } } - } - } else if (domain_type_ == FILTER_CELL) { - for (int level = 0; level < p.n_coord_; ++level) { - for (int i_domain=0; i_domain < n; i_domain++) { - if (model::cells[p.coord_[level].cell]->id_ == domain_ids_[i_domain]) { - this->check_hit(p.material_, indices[i_domain], hits[i_domain]); - break; + } else if (domain_type_ == FILTER_CELL) { + for (int level = 0; level < p.n_coord_; ++level) { + for (int i_domain=0; i_domain < n; i_domain++) { + if (model::cells[p.coord_[level].cell]->id_ == domain_ids_[i_domain]) { + this->check_hit(p.material_, indices[i_domain], hits[i_domain]); + break; + } } } - } - } else if (domain_type_ == FILTER_UNIVERSE) { - for (int level = 0; level < p.n_coord_; ++level) { - for (int i_domain = 0; i_domain < n; ++i_domain) { - if (model::universes[p.coord_[level].universe]->id_ == domain_ids_[i_domain]) { - check_hit(p.material_, indices[i_domain], hits[i_domain]); - break; + } else if (domain_type_ == FILTER_UNIVERSE) { + for (int level = 0; level < p.n_coord_; ++level) { + for (int i_domain = 0; i_domain < n; ++i_domain) { + if (model::universes[p.coord_[level].universe]->id_ == domain_ids_[i_domain]) { + check_hit(p.material_, indices[i_domain], hits[i_domain]); + break; + } } } } } - } - // At this point, each thread has its own pair of index/hits lists and we now - // need to reduce them. OpenMP is not nearly smart enough to do this on its own, - // so we have to manually reduce them + // At this point, each thread has its own pair of index/hits lists and we now + // need to reduce them. OpenMP is not nearly smart enough to do this on its own, + // so we have to manually reduce them -#ifdef _OPENMP - #pragma omp for ordered schedule(static) - for (int i = 0; i < omp_get_num_threads(); ++i) { - #pragma omp ordered - for (int i_domain = 0; i_domain < n; ++i_domain) { - for (int j = 0; j < indices[i_domain].size(); ++j) { - // Check if this material has been added to the master list and if so, - // accumulate the number of hits - bool already_added = false; - for (int k = 0; k < master_indices[i_domain].size(); k++) { - if (indices[i_domain][j] == master_indices[i_domain][k]) { - master_hits[i_domain][k] += hits[i_domain][j]; - already_added = true; + #ifdef _OPENMP + int n_threads = omp_get_num_threads(); + #else + int n_threads = 1; + #endif + + #pragma omp for ordered schedule(static) + for (int i = 0; i < n_threads; ++i) { + #pragma omp ordered + for (int i_domain = 0; i_domain < n; ++i_domain) { + for (int j = 0; j < indices[i_domain].size(); ++j) { + // Check if this material has been added to the master list and if so, + // accumulate the number of hits + bool already_added = false; + for (int k = 0; k < master_indices[i_domain].size(); k++) { + if (indices[i_domain][j] == master_indices[i_domain][k]) { + master_hits[i_domain][k] += hits[i_domain][j]; + already_added = true; + } + } + if (!already_added) { + // If we made it here, the material hasn't yet been added to the master + // list, so add entries to the master indices and master hits lists + master_indices[i_domain].push_back(indices[i_domain][j]); + master_hits[i_domain].push_back(hits[i_domain][j]); } - } - if (!already_added) { - // If we made it here, the material hasn't yet been added to the master - // list, so add entries to the master indices and master hits lists - master_indices[i_domain].push_back(indices[i_domain][j]); - master_hits[i_domain].push_back(hits[i_domain][j]); } } } - } -#else - master_indices = indices; - master_hits = hits; -#endif + prn_set_stream(STREAM_TRACKING); + } // omp parallel - prn_set_stream(STREAM_TRACKING); - } // omp parallel + // Reduce hits onto master process - // Reduce hits onto master process + // Determine volume of bounding box + Position d {upper_right_ - lower_left_}; + double volume_sample = d.x*d.y*d.z; - // Determine volume of bounding box - Position d {upper_right_ - lower_left_}; - double volume_sample = d.x*d.y*d.z; + // bump iteration counter and get total number + // of samples at this point + iterations++; + size_t total_samples = iterations * n_samples_; - // Set size for members of the Result struct - std::vector results(n); + // reset + double trigger_val = -INFTY; - for (int i_domain = 0; i_domain < n; ++i_domain) { - // Get reference to result for this domain - auto& result {results[i_domain]}; + // Set size for members of the Result struct + std::vector results(n); - // Create 2D array to store atoms/uncertainty for each nuclide. Later this - // is compressed into vectors storing only those nuclides that are non-zero - auto n_nuc = data::nuclides.size(); - xt::xtensor atoms({n_nuc, 2}, 0.0); + for (int i_domain = 0; i_domain < n; ++i_domain) { + // Get reference to result for this domain + auto& result {results[i_domain]}; + + // Create 2D array to store atoms/uncertainty for each nuclide. Later this + // is compressed into vectors storing only those nuclides that are non-zero + auto n_nuc = data::nuclides.size(); + xt::xtensor atoms({n_nuc, 2}, 0.0); #ifdef OPENMC_MPI - if (mpi::master) { - for (int j = 1; j < mpi::n_procs; j++) { - int q; - MPI_Recv(&q, 1, MPI_INTEGER, j, 0, mpi::intracomm, MPI_STATUS_IGNORE); - int buffer[2*q]; - MPI_Recv(&buffer[0], 2*q, MPI_INTEGER, j, 1, mpi::intracomm, MPI_STATUS_IGNORE); - for (int k = 0; k < q; ++k) { - for (int m = 0; m < master_indices[i_domain].size(); ++m) { - if (buffer[2*k] == master_indices[i_domain][m]) { - master_hits[i_domain][m] += buffer[2*k + 1]; - break; + if (mpi::master) { + for (int j = 1; j < mpi::n_procs; j++) { + int q; + MPI_Recv(&q, 1, MPI_INTEGER, j, 0, mpi::intracomm, MPI_STATUS_IGNORE); + int buffer[2*q]; + MPI_Recv(&buffer[0], 2*q, MPI_INTEGER, j, 1, mpi::intracomm, MPI_STATUS_IGNORE); + for (int k = 0; k < q; ++k) { + for (int m = 0; m < master_indices[i_domain].size(); ++m) { + if (buffer[2*k] == master_indices[i_domain][m]) { + master_hits[i_domain][m] += buffer[2*k + 1]; + break; + } } } } - } - } else { - int q = master_indices[i_domain].size(); - int buffer[2*q]; - for (int k = 0; k < q; ++k) { - buffer[2*k] = master_indices[i_domain][k]; - buffer[2*k + 1] = master_hits[i_domain][k]; - } + } else { + int q = master_indices[i_domain].size(); + int buffer[2*q]; + for (int k = 0; k < q; ++k) { + buffer[2*k] = master_indices[i_domain][k]; + buffer[2*k + 1] = master_hits[i_domain][k]; + } - MPI_Send(&q, 1, MPI_INTEGER, 0, 0, mpi::intracomm); - MPI_Send(&buffer[0], 2*q, MPI_INTEGER, 0, 1, mpi::intracomm); + MPI_Send(&q, 1, MPI_INTEGER, 0, 0, mpi::intracomm); + MPI_Send(&buffer[0], 2*q, MPI_INTEGER, 0, 1, mpi::intracomm); + } +#endif + + if (mpi::master) { + int total_hits = 0; + for (int j = 0; j < master_indices[i_domain].size(); ++j) { + total_hits += master_hits[i_domain][j]; + double f = static_cast(master_hits[i_domain][j]) / total_samples; + double var_f = f*(1.0 - f) / total_samples; + + int i_material = master_indices[i_domain][j]; + if (i_material == MATERIAL_VOID) continue; + + const auto& mat = model::materials[i_material]; + for (int k = 0; k < mat->nuclide_.size(); ++k) { + // Accumulate nuclide density + int i_nuclide = mat->nuclide_[k]; + atoms(i_nuclide, 0) += mat->atom_density_[k] * f; + atoms(i_nuclide, 1) += std::pow(mat->atom_density_[k], 2) * var_f; + } + } + + // Determine volume + result.volume[0] = static_cast(total_hits) / total_samples * volume_sample; + result.volume[1] = std::sqrt(result.volume[0] + * (volume_sample - result.volume[0]) / total_samples); + result.iterations = iterations; + + // update threshold value if needed + if (trigger_type_ != TriggerMetric::not_active) { + double val = 0.0; + switch (trigger_type_) { + case TriggerMetric::standard_deviation: + val = result.volume[1]; + break; + case TriggerMetric::relative_error: + val = result.volume[0] == 0.0 ? INFTY : result.volume[1] / result.volume[0]; + break; + case TriggerMetric::variance: + val = result.volume[1] * result.volume[1]; + break; + } + // update max if entry is valid + if (val > 0.0) { trigger_val = std::max(trigger_val, val); } + } + + for (int j = 0; j < n_nuc; ++j) { + // Determine total number of atoms. At this point, we have values in + // atoms/b-cm. To get to atoms we multiply by 10^24 V. + double mean = 1.0e24 * volume_sample * atoms(j, 0); + double stdev = 1.0e24 * volume_sample * std::sqrt(atoms(j, 1)); + + // Convert full arrays to vectors + if (mean > 0.0) { + result.nuclides.push_back(j); + result.atoms.push_back(mean); + result.uncertainty.push_back(stdev); + } + } + } + } // end domain loop + + // if no trigger is applied, we're done + if (trigger_type_ == TriggerMetric::not_active) { return results; } + +#ifdef OPENMC_MPI + // update maximum error value on all processes + MPI_Bcast(&trigger_val, 1, MPI_DOUBLE, 0, mpi::intracomm); +#endif + + // return results of the calculation + if (trigger_val < threshold_) { return results; } + +#ifdef OPENMC_MPI + // if iterating in an MPI run, need to zero indices and hits so they aren't counted twice + if (!mpi::master) { + for (auto& v : master_indices) { std::fill(v.begin(), v.end(), 0); } + for (auto& v : master_hits) { std::fill(v.begin(), v.end(), 0); } } #endif - if (mpi::master) { - int total_hits = 0; - for (int j = 0; j < master_indices[i_domain].size(); ++j) { - total_hits += master_hits[i_domain][j]; - double f = static_cast(master_hits[i_domain][j]) / n_samples_; - double var_f = f*(1.0 - f)/n_samples_; - - int i_material = master_indices[i_domain][j]; - if (i_material == MATERIAL_VOID) continue; - - const auto& mat = model::materials[i_material]; - for (int k = 0; k < mat->nuclide_.size(); ++k) { - // Accumulate nuclide density - int i_nuclide = mat->nuclide_[k]; - atoms(i_nuclide, 0) += mat->atom_density_[k] * f; - atoms(i_nuclide, 1) += std::pow(mat->atom_density_[k], 2) * var_f; - } - } - - // Determine volume - result.volume[0] = static_cast(total_hits) / n_samples_ * volume_sample; - result.volume[1] = std::sqrt(result.volume[0] - * (volume_sample - result.volume[0]) / n_samples_); - - for (int j = 0; j < n_nuc; ++j) { - // Determine total number of atoms. At this point, we have values in - // atoms/b-cm. To get to atoms we multiply by 10^24 V. - double mean = 1.0e24 * volume_sample * atoms(j, 0); - double stdev = 1.0e24 * volume_sample * std::sqrt(atoms(j, 1)); - - // Convert full arrays to vectors - if (mean > 0.0) { - result.nuclides.push_back(j); - result.atoms.push_back(mean); - result.uncertainty.push_back(stdev); - } - } - } - } - - return results; + } // end while } void VolumeCalculation::to_hdf5(const std::string& filename, @@ -289,6 +363,27 @@ void VolumeCalculation::to_hdf5(const std::string& filename, write_attribute(file_id, "samples", n_samples_); write_attribute(file_id, "lower_left", lower_left_); write_attribute(file_id, "upper_right", upper_right_); + // Write trigger info + if (trigger_type_ != TriggerMetric::not_active) { + write_attribute(file_id, "iterations", results[0].iterations); + write_attribute(file_id, "threshold", threshold_); + std::string trigger_str; + switch(trigger_type_) { + case TriggerMetric::variance: + trigger_str = "variance"; + break; + case TriggerMetric::standard_deviation: + trigger_str = "std_dev"; + break; + case TriggerMetric::relative_error: + trigger_str = "rel_err"; + break; + } + write_attribute(file_id, "trigger_type", trigger_str); + } else { + write_attribute(file_id, "iterations", 1); + } + if (domain_type_ == FILTER_CELL) { write_attribute(file_id, "domain_type", "cell"); } @@ -311,22 +406,20 @@ void VolumeCalculation::to_hdf5(const std::string& filename, // Create array of nuclide names from the vector auto n_nuc = result.nuclides.size(); - if (!result.nuclides.empty()) { - std::vector nucnames; - for (int i_nuc : result.nuclides) { - nucnames.push_back(data::nuclides[i_nuc]->name_); - } - - // Create array of total # of atoms with uncertainty for each nuclide - xt::xtensor atom_data({n_nuc, 2}); - xt::view(atom_data, xt::all(), 0) = xt::adapt(result.atoms); - xt::view(atom_data, xt::all(), 1) = xt::adapt(result.uncertainty); - - // Write results - write_dataset(group_id, "nuclides", nucnames); - write_dataset(group_id, "atoms", atom_data); + std::vector nucnames; + for (int i_nuc : result.nuclides) { + nucnames.push_back(data::nuclides[i_nuc]->name_); } + // Create array of total # of atoms with uncertainty for each nuclide + xt::xtensor atom_data({n_nuc, 2}); + xt::view(atom_data, xt::all(), 0) = xt::adapt(result.atoms); + xt::view(atom_data, xt::all(), 1) = xt::adapt(result.uncertainty); + + // Write results + write_dataset(group_id, "nuclides", nucnames); + write_dataset(group_id, "atoms", atom_data); + close_group(group_id); } diff --git a/tests/dummy_operator.py b/tests/dummy_operator.py index 85c689743a..35a526c30d 100644 --- a/tests/dummy_operator.py +++ b/tests/dummy_operator.py @@ -83,7 +83,7 @@ class TestChain(object): fission_yields = [None] @staticmethod - def get_thermal_fission_yields(): + def get_default_fission_yields(): return None def form_matrix(self, rates, _fission_yields=None): diff --git a/tests/regression_tests/asymmetric_lattice/test.py b/tests/regression_tests/asymmetric_lattice/test.py index d6a0ab9b73..7ed0293ce9 100644 --- a/tests/regression_tests/asymmetric_lattice/test.py +++ b/tests/regression_tests/asymmetric_lattice/test.py @@ -25,12 +25,12 @@ class AsymmetricLatticeTestHarness(PyAPITestHarness): [water, water, water]] # Create bounding surfaces - min_x = openmc.XPlane(x0=-32.13, boundary_type='reflective') - max_x = openmc.XPlane(x0=+32.13, boundary_type='reflective') - min_y = openmc.YPlane(y0=-32.13, boundary_type='reflective') - max_y = openmc.YPlane(y0=+32.13, boundary_type='reflective') - min_z = openmc.ZPlane(z0=0, boundary_type='reflective') - max_z = openmc.ZPlane(z0=+32.13, boundary_type='reflective') + min_x = openmc.XPlane(-32.13, 'reflective') + max_x = openmc.XPlane(+32.13, 'reflective') + min_y = openmc.YPlane(-32.13, 'reflective') + max_y = openmc.YPlane(+32.13, 'reflective') + min_z = openmc.ZPlane(0, 'reflective') + max_z = openmc.ZPlane(+32.13, 'reflective') # Define root universe root_univ = openmc.Universe(universe_id=0, name='root universe') diff --git a/tests/regression_tests/complex_cell/test.py b/tests/regression_tests/complex_cell/test.py index b43ccd72f9..77cbd6cb7d 100755 --- a/tests/regression_tests/complex_cell/test.py +++ b/tests/regression_tests/complex_cell/test.py @@ -1,8 +1,5 @@ from tests.testing_harness import TestHarness -import sys - -import openmc.capi def test_complex_cell(): harness = TestHarness('statepoint.10.h5') diff --git a/tests/regression_tests/dagmc/legacy/dagmc.h5m b/tests/regression_tests/dagmc/legacy/dagmc.h5m index fbbe9a34a8..bd50a96914 100644 Binary files a/tests/regression_tests/dagmc/legacy/dagmc.h5m and b/tests/regression_tests/dagmc/legacy/dagmc.h5m differ diff --git a/tests/regression_tests/dagmc/legacy/inputs_true.dat b/tests/regression_tests/dagmc/legacy/inputs_true.dat index 8ca49c3246..769a3384c3 100644 --- a/tests/regression_tests/dagmc/legacy/inputs_true.dat +++ b/tests/regression_tests/dagmc/legacy/inputs_true.dat @@ -1,6 +1,6 @@ - + diff --git a/tests/regression_tests/dagmc/legacy/test.py b/tests/regression_tests/dagmc/legacy/test.py index db062d36eb..5af18e5c8c 100644 --- a/tests/regression_tests/dagmc/legacy/test.py +++ b/tests/regression_tests/dagmc/legacy/test.py @@ -1,14 +1,16 @@ import openmc -import openmc.capi +import openmc.lib import pytest from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.capi._dagmc_enabled(), + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") -def test_dagmc(): +@pytest.fixture +def model(): + model = openmc.model.Model() # settings @@ -31,7 +33,7 @@ def test_dagmc(): model.tallies = [tally] # materials - u235 = openmc.Material(name="fuel") + u235 = openmc.Material(name="no-void fuel") u235.add_nuclide('U235', 1.0, 'ao') u235.set_density('g/cc', 11) u235.id = 40 @@ -46,4 +48,8 @@ def test_dagmc(): mats = openmc.Materials([u235, water]) model.materials = mats - model.export_to_xml() + return model + +def test_dagmc(model): + harness = PyAPITestHarness('statepoint.5.h5', model) + harness.main() diff --git a/tests/regression_tests/dagmc/refl/test.py b/tests/regression_tests/dagmc/refl/test.py index 93104f1d0d..c451b61250 100644 --- a/tests/regression_tests/dagmc/refl/test.py +++ b/tests/regression_tests/dagmc/refl/test.py @@ -1,12 +1,12 @@ import openmc -import openmc.capi +import openmc.lib from openmc.stats import Box import pytest from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.capi._dagmc_enabled(), + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") class UWUWTest(PyAPITestHarness): diff --git a/tests/regression_tests/dagmc/uwuw/test.py b/tests/regression_tests/dagmc/uwuw/test.py index 885b83766f..b4391d8e77 100644 --- a/tests/regression_tests/dagmc/uwuw/test.py +++ b/tests/regression_tests/dagmc/uwuw/test.py @@ -1,12 +1,12 @@ import openmc -import openmc.capi +import openmc.lib from openmc.stats import Box import pytest from tests.testing_harness import PyAPITestHarness pytestmark = pytest.mark.skipif( - not openmc.capi._dagmc_enabled(), + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") class UWUWTest(PyAPITestHarness): diff --git a/tests/regression_tests/deplete/example_geometry.py b/tests/regression_tests/deplete/example_geometry.py index d4fdf585df..f79044558c 100644 --- a/tests/regression_tests/deplete/example_geometry.py +++ b/tests/regression_tests/deplete/example_geometry.py @@ -158,6 +158,7 @@ def generate_initial_number_density(): return temperature, sab, initial_density, burn + def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): """ Calculates a segmented pin. @@ -248,6 +249,7 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): return fuel_u, v_segment, v_gap, v_clad + def generate_geometry(n_rings, n_wedges): """ Generates example geometry. @@ -296,12 +298,12 @@ def generate_geometry(n_rings, n_wedges): lattice.outer = all_water_u # Bound universe - x_low = openmc.XPlane(x0=-pitch*n_pin/2, boundary_type='reflective') - x_high = openmc.XPlane(x0=pitch*n_pin/2, boundary_type='reflective') - y_low = openmc.YPlane(y0=-pitch*n_pin/2, boundary_type='reflective') - y_high = openmc.YPlane(y0=pitch*n_pin/2, boundary_type='reflective') - z_low = openmc.ZPlane(z0=-10, boundary_type='reflective') - z_high = openmc.ZPlane(z0=10, boundary_type='reflective') + x_low = openmc.XPlane(-pitch*n_pin/2, 'reflective') + x_high = openmc.XPlane(pitch*n_pin/2, 'reflective') + y_low = openmc.YPlane(-pitch*n_pin/2, 'reflective') + y_high = openmc.YPlane(pitch*n_pin/2, 'reflective') + z_low = openmc.ZPlane(-10, 'reflective') + z_high = openmc.ZPlane(10, 'reflective') # Compute bounding box lower_left = [-pitch*n_pin/2, -pitch*n_pin/2, -10] @@ -317,10 +319,11 @@ def generate_geometry(n_rings, n_wedges): v_cool = pitch**2 - (v_gap + v_clad + n_rings * n_wedges * v_segment) # Store volumes for later usage - volume = {'fuel': v_segment, 'gap':v_gap, 'clad':v_clad, 'cool':v_cool} + volume = {'fuel': v_segment, 'gap': v_gap, 'clad': v_clad, 'cool': v_cool} return geometry, volume, mapping, lower_left, upper_right + def generate_problem(n_rings=5, n_wedges=8): """ Merges geometry and materials. diff --git a/tests/regression_tests/deplete/test.py b/tests/regression_tests/deplete/test.py index c7a9c162e0..01ce21c010 100644 --- a/tests/regression_tests/deplete/test.py +++ b/tests/regression_tests/deplete/test.py @@ -108,11 +108,17 @@ def test_full(run_in_tmpdir): t_ref, k_ref = res_ref.get_eigenvalue() k_state = np.empty_like(k_ref) + n_tallies = np.empty(N + 1, dtype=int) + # Get statepoint files for all BOS points and EOL for n in range(N + 1): statepoint = openmc.StatePoint("openmc_simulation_n{}.h5".format(n)) k_n = statepoint.k_combined k_state[n] = [k_n.nominal_value, k_n.std_dev] + n_tallies[n] = len(statepoint.tallies) # Look for exact match pulling from statepoint and depletion_results assert np.all(k_state == k_test) assert np.allclose(k_test, k_ref) + + # Check that no additional tallies are loaded from the files + assert np.all(n_tallies == 0) diff --git a/tests/regression_tests/periodic/test.py b/tests/regression_tests/periodic/test.py index 5df935b22e..879bd84809 100644 --- a/tests/regression_tests/periodic/test.py +++ b/tests/regression_tests/periodic/test.py @@ -21,16 +21,16 @@ class PeriodicTest(PyAPITestHarness): materials.export_to_xml() # Define geometry - x_min = openmc.XPlane(1, x0=-5., boundary_type='periodic') - x_max = openmc.XPlane(2, x0=5., boundary_type='periodic') + x_min = openmc.XPlane(surface_id=1, x0=-5., boundary_type='periodic') + x_max = openmc.XPlane(surface_id=2, x0=5., boundary_type='periodic') x_max.periodic_surface = x_min - y_min = openmc.YPlane(3, y0=-5., boundary_type='periodic') - y_max = openmc.YPlane(4, y0=5., boundary_type='periodic') + y_min = openmc.YPlane(surface_id=3, y0=-5., boundary_type='periodic') + y_max = openmc.YPlane(surface_id=4, y0=5., boundary_type='periodic') - z_min = openmc.ZPlane(5, z0=-5., boundary_type='reflective') - z_max = openmc.ZPlane(6, z0=5., boundary_type='reflective') - z_cyl = openmc.ZCylinder(7, x0=-2.5, y0=2.5, r=2.0) + z_min = openmc.ZPlane(surface_id=5, z0=-5., boundary_type='reflective') + z_max = openmc.ZPlane(surface_id=6, z0=5., boundary_type='reflective') + z_cyl = openmc.ZCylinder(surface_id=7, x0=-2.5, y0=2.5, r=2.0) outside_cyl = openmc.Cell(1, fill=water, region=( +x_min & -x_max & +y_min & -y_max & +z_min & -z_max & +z_cyl)) diff --git a/tests/regression_tests/photon_production/test.py b/tests/regression_tests/photon_production/test.py index c6a1fc3908..1f645ebb94 100644 --- a/tests/regression_tests/photon_production/test.py +++ b/tests/regression_tests/photon_production/test.py @@ -14,10 +14,10 @@ class SourceTestHarness(PyAPITestHarness): materials = openmc.Materials([mat]) materials.export_to_xml() - cyl = openmc.XCylinder(boundary_type='vacuum', r=1.0) - x_plane_left = openmc.XPlane(boundary_type='vacuum', x0=-1.0) - x_plane_center = openmc.XPlane(boundary_type='transmission', x0=1.0) - x_plane_right = openmc.XPlane(boundary_type='vacuum', x0=1.0e9) + cyl = openmc.XCylinder(r=1.0, boundary_type='vacuum') + x_plane_left = openmc.XPlane(-1.0, 'vacuum') + x_plane_center = openmc.XPlane(1.0) + x_plane_right = openmc.XPlane(1.0e9, 'vacuum') inner_cyl_left = openmc.Cell() inner_cyl_right = openmc.Cell() @@ -31,7 +31,7 @@ class SourceTestHarness(PyAPITestHarness): geometry.export_to_xml() source = openmc.Source() - source.space = openmc.stats.Point((0,0,0)) + source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Monodirectional() source.energy = openmc.stats.Discrete([14.0e6], [1.0]) source.particle = 'neutron' diff --git a/tests/regression_tests/resonance_scattering/test.py b/tests/regression_tests/resonance_scattering/test.py index c83005ff2c..588ba7c977 100644 --- a/tests/regression_tests/resonance_scattering/test.py +++ b/tests/regression_tests/resonance_scattering/test.py @@ -17,7 +17,7 @@ class ResonanceScatteringTestHarness(PyAPITestHarness): mats_file.export_to_xml() # Geometry - dumb_surface = openmc.XPlane(x0=100, boundary_type='reflective') + dumb_surface = openmc.XPlane(100, 'reflective') c1 = openmc.Cell(cell_id=1, fill=mat, region=-dumb_surface) root_univ = openmc.Universe(universe_id=0, cells=[c1]) geometry = openmc.Geometry(root_univ) diff --git a/tests/regression_tests/salphabeta/test.py b/tests/regression_tests/salphabeta/test.py index ab5305126e..f487723dee 100644 --- a/tests/regression_tests/salphabeta/test.py +++ b/tests/regression_tests/salphabeta/test.py @@ -44,11 +44,11 @@ def make_model(): model.materials += [m1, m2, m3, m4] # Geometry - x0 = openmc.XPlane(x0=-10, boundary_type='vacuum') - x1 = openmc.XPlane(x0=-5) - x2 = openmc.XPlane(x0=0) - x3 = openmc.XPlane(x0=5) - x4 = openmc.XPlane(x0=10, boundary_type='vacuum') + x0 = openmc.XPlane(-10, 'vacuum') + x1 = openmc.XPlane(-5) + x2 = openmc.XPlane(0) + x3 = openmc.XPlane(5) + x4 = openmc.XPlane(10, 'vacuum') root_univ = openmc.Universe() diff --git a/tests/regression_tests/source/results_true.dat b/tests/regression_tests/source/results_true.dat index 90cc3ec1f6..6fe76c8cd5 100644 --- a/tests/regression_tests/source/results_true.dat +++ b/tests/regression_tests/source/results_true.dat @@ -1,2 +1,2 @@ k-combined: -3.026614E-01 3.952008E-03 +2.800827E-01 7.360163E-03 diff --git a/tests/regression_tests/triso/test.py b/tests/regression_tests/triso/test.py index aa1bf08289..7bef80ea87 100644 --- a/tests/regression_tests/triso/test.py +++ b/tests/regression_tests/triso/test.py @@ -54,12 +54,12 @@ class TRISOTestHarness(PyAPITestHarness): inner_univ = openmc.Universe(cells=[c1, c2, c3, c4, c5]) # Define box to contain lattice and to pack TRISO particles in - min_x = openmc.XPlane(x0=-0.5, boundary_type='reflective') - max_x = openmc.XPlane(x0=0.5, boundary_type='reflective') - min_y = openmc.YPlane(y0=-0.5, boundary_type='reflective') - max_y = openmc.YPlane(y0=0.5, boundary_type='reflective') - min_z = openmc.ZPlane(z0=-0.5, boundary_type='reflective') - max_z = openmc.ZPlane(z0=0.5, boundary_type='reflective') + min_x = openmc.XPlane(-0.5, 'reflective') + max_x = openmc.XPlane(0.5, 'reflective') + min_y = openmc.YPlane(-0.5, 'reflective') + max_y = openmc.YPlane(0.5, 'reflective') + min_z = openmc.ZPlane(-0.5, 'reflective') + max_z = openmc.ZPlane(0.5, 'reflective') box_region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z box = openmc.Cell(region=box_region) diff --git a/tests/regression_tests/volume_calc/inputs_true.dat b/tests/regression_tests/volume_calc/inputs_true.dat index 607921af26..aaf6d8b005 100644 --- a/tests/regression_tests/volume_calc/inputs_true.dat +++ b/tests/regression_tests/volume_calc/inputs_true.dat @@ -48,4 +48,28 @@ -1.0 -1.0 -6.0 1.0 1.0 6.0 + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + material + 1 2 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + + + cell + 1 2 3 + 100 + -1.0 -1.0 -6.0 + 1.0 1.0 6.0 + + diff --git a/tests/regression_tests/volume_calc/results_true.dat b/tests/regression_tests/volume_calc/results_true.dat index 466139cd68..800356e35b 100644 --- a/tests/regression_tests/volume_calc/results_true.dat +++ b/tests/regression_tests/volume_calc/results_true.dat @@ -1,4 +1,7 @@ Volume calculation 0 +Trigger Type: None +Trigger threshold: None +Iterations: 1 Domain 1: 31.47+/-0.07 cm^3 Domain 2: 2.093+/-0.031 cm^3 Domain 3: 2.049+/-0.031 cm^3 @@ -12,6 +15,9 @@ Domain 3: 2.049+/-0.031 cm^3 6 3 O16 (6.85+/-0.10)e+22 7 3 B10 (6.85+/-0.10)e+18 Volume calculation 1 +Trigger Type: None +Trigger threshold: None +Iterations: 1 Domain 1: 4.14+/-0.04 cm^3 Domain 2: 31.47+/-0.07 cm^3 Material Nuclide Atoms @@ -21,6 +27,9 @@ Domain 2: 31.47+/-0.07 cm^3 3 2 U235 (3.482+/-0.008)e+23 4 2 Mo99 (3.482+/-0.008)e+22 Volume calculation 2 +Trigger Type: None +Trigger threshold: None +Iterations: 1 Domain 0: 35.61+/-0.07 cm^3 Universe Nuclide Atoms 0 0 H1 (2.770+/-0.029)e+23 @@ -28,3 +37,47 @@ Domain 0: 35.61+/-0.07 cm^3 2 0 B10 (1.385+/-0.014)e+19 3 0 U235 (3.482+/-0.008)e+23 4 0 Mo99 (3.482+/-0.008)e+22 +Volume calculation 3 +Trigger Type: std_dev +Trigger threshold: 0.1 +Iterations: 521 +Domain 1: 31.47+/-0.10 cm^3 +Domain 2: 2.10+/-0.04 cm^3 +Domain 3: 2.11+/-0.04 cm^3 + Cell Nuclide Atoms +0 1 U235 (3.481+/-0.011)e+23 +1 1 Mo99 (3.481+/-0.011)e+22 +2 2 H1 (1.403+/-0.029)e+23 +3 2 O16 (7.01+/-0.14)e+22 +4 2 B10 (7.01+/-0.14)e+18 +5 3 H1 (1.412+/-0.029)e+23 +6 3 O16 (7.06+/-0.14)e+22 +7 3 B10 (7.06+/-0.14)e+18 +Volume calculation 4 +Trigger Type: rel_err +Trigger threshold: 0.1 +Iterations: 10 +Domain 1: 4.5+/-0.4 cm^3 +Domain 2: 30.5+/-0.7 cm^3 + Material Nuclide Atoms +0 1 H1 (3.02+/-0.30)e+23 +1 1 O16 (1.51+/-0.15)e+23 +2 1 B10 (1.51+/-0.15)e+19 +3 2 U235 (3.38+/-0.08)e+23 +4 2 Mo99 (3.38+/-0.08)e+22 +Volume calculation 5 +Trigger Type: variance +Trigger threshold: 0.05 +Iterations: 105 +Domain 1: 31.51+/-0.22 cm^3 +Domain 2: 2.13+/-0.10 cm^3 +Domain 3: 2.11+/-0.10 cm^3 + Cell Nuclide Atoms +0 1 U235 (3.486+/-0.025)e+23 +1 1 Mo99 (3.486+/-0.025)e+22 +2 2 H1 (1.42+/-0.06)e+23 +3 2 O16 (7.11+/-0.32)e+22 +4 2 B10 (7.11+/-0.32)e+18 +5 3 H1 (1.41+/-0.06)e+23 +6 3 O16 (7.05+/-0.32)e+22 +7 3 B10 (7.05+/-0.32)e+18 diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index da8399c1fd..f7b9a27a01 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -8,6 +8,14 @@ from tests.testing_harness import PyAPITestHarness class VolumeTest(PyAPITestHarness): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self.exp_std_dev = 1e-01 + self.exp_rel_err = 1e-01 + self.exp_variance = 5e-02 + def _build_inputs(self): # Define materials water = openmc.Material(1) @@ -25,11 +33,11 @@ class VolumeTest(PyAPITestHarness): materials = openmc.Materials((water, fuel)) materials.export_to_xml() - cyl = openmc.ZCylinder(1, r=1.0, boundary_type='vacuum') - top_sphere = openmc.Sphere(2, z0=5., r=1., boundary_type='vacuum') - top_plane = openmc.ZPlane(3, z0=5.) - bottom_sphere = openmc.Sphere(4, z0=-5., r=1., boundary_type='vacuum') - bottom_plane = openmc.ZPlane(5, z0=-5.) + cyl = openmc.ZCylinder(surface_id=1, r=1.0, boundary_type='vacuum') + top_sphere = openmc.Sphere(surface_id=2, z0=5., r=1., boundary_type='vacuum') + top_plane = openmc.ZPlane(surface_id=3, z0=5.) + bottom_sphere = openmc.Sphere(surface_id=4, z0=-5., r=1., boundary_type='vacuum') + bottom_plane = openmc.ZPlane(surface_id=5, z0=-5.) # Define geometry inside_cyl = openmc.Cell(1, fill=fuel, region=-cyl & -top_plane & +bottom_plane) @@ -45,9 +53,18 @@ class VolumeTest(PyAPITestHarness): vol_calcs = [ openmc.VolumeCalculation(list(root.cells.values()), 100000), openmc.VolumeCalculation([water, fuel], 100000, ll, ur), - openmc.VolumeCalculation([root], 100000, ll, ur) + openmc.VolumeCalculation([root], 100000, ll, ur), + openmc.VolumeCalculation(list(root.cells.values()), 100), + openmc.VolumeCalculation([water, fuel], 100, ll, ur), + openmc.VolumeCalculation(list(root.cells.values()), 100) ] + vol_calcs[3].set_trigger(self.exp_std_dev, 'std_dev') + + vol_calcs[4].set_trigger(self.exp_rel_err, 'rel_err') + + vol_calcs[5].set_trigger(self.exp_variance, 'variance') + # Define settings settings = openmc.Settings() settings.run_mode = 'volume' @@ -62,6 +79,33 @@ class VolumeTest(PyAPITestHarness): # Read volume calculation results volume_calc = openmc.VolumeCalculation.from_hdf5(filename) + outstr += 'Trigger Type: {}\n'.format(volume_calc.trigger_type) + outstr += 'Trigger threshold: {}\n'.format(volume_calc.threshold) + outstr += 'Iterations: {}\n'.format(volume_calc.iterations) + + if i == 3: + assert(volume_calc.trigger_type == 'std_dev') + assert(volume_calc.threshold == self.exp_std_dev) + elif i == 4: + assert(volume_calc.trigger_type == 'rel_err') + assert(volume_calc.threshold == self.exp_rel_err) + elif i == 5: + assert(volume_calc.trigger_type == 'variance') + assert(volume_calc.threshold == self.exp_variance) + else: + assert(volume_calc.trigger_type == None) + assert(volume_calc.threshold == None) + assert(volume_calc.iterations == 1) + + # if a trigger is applied, make sure the calculation satisfies the trigger + for vol in volume_calc.volumes.values(): + if volume_calc.trigger_type == 'std_dev': + assert(vol.std_dev <= self.exp_std_dev) + if volume_calc.trigger_type == 'rel_err': + assert(vol.std_dev/vol.nominal_value <= self.exp_rel_err) + if volume_calc.trigger_type == 'variance': + assert(vol.std_dev * vol.std_dev <= self.exp_variance) + # Write cell volumes and total # of atoms for each nuclide for uid, volume in sorted(volume_calc.volumes.items()): outstr += 'Domain {}: {} cm^3\n'.format(uid, volume) diff --git a/tests/regression_tests/white_plane/__init__.py b/tests/regression_tests/white_plane/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/white_plane/geometry.xml b/tests/regression_tests/white_plane/geometry.xml new file mode 100644 index 0000000000..b8be4b59dc --- /dev/null +++ b/tests/regression_tests/white_plane/geometry.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/regression_tests/white_plane/materials.xml b/tests/regression_tests/white_plane/materials.xml new file mode 100644 index 0000000000..2472a74717 --- /dev/null +++ b/tests/regression_tests/white_plane/materials.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tests/regression_tests/white_plane/results_true.dat b/tests/regression_tests/white_plane/results_true.dat new file mode 100644 index 0000000000..b80361ced1 --- /dev/null +++ b/tests/regression_tests/white_plane/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +2.268730E+00 4.470768E-03 diff --git a/tests/regression_tests/white_plane/settings.xml b/tests/regression_tests/white_plane/settings.xml new file mode 100644 index 0000000000..70b4e802f8 --- /dev/null +++ b/tests/regression_tests/white_plane/settings.xml @@ -0,0 +1,15 @@ + + + + eigenvalue + 10 + 5 + 1000 + + + + -4 -4 -4 4 4 4 + + + + diff --git a/tests/regression_tests/white_plane/test.py b/tests/regression_tests/white_plane/test.py new file mode 100644 index 0000000000..223f4e5e6a --- /dev/null +++ b/tests/regression_tests/white_plane/test.py @@ -0,0 +1,6 @@ +from tests.testing_harness import TestHarness + + +def test_white_plane(): + harness = TestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py index 59a520c12a..e97ab13e2a 100644 --- a/tests/unit_tests/__init__.py +++ b/tests/unit_tests/__init__.py @@ -1,7 +1,14 @@ +import shutil + import numpy as np import pytest +# Check if NJOY is available +needs_njoy = pytest.mark.skipif(shutil.which('njoy') is None, + reason="NJOY not installed") + + def assert_unbounded(obj): """Assert that a region/cell is unbounded.""" ll, ur = obj.bounding_box diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index b69ef0b138..926535a9c8 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -108,11 +108,11 @@ def mixed_lattice_model(uo2, water): [empty_univ, u] ] - xmin = openmc.XPlane(x0=-d, boundary_type='periodic') - xmax = openmc.XPlane(x0=d, boundary_type='periodic') + xmin = openmc.XPlane(-d, 'periodic') + xmax = openmc.XPlane(d, 'periodic') xmin.periodic_surface = xmax - ymin = openmc.YPlane(y0=-d, boundary_type='periodic') - ymax = openmc.YPlane(y0=d, boundary_type='periodic') + ymin = openmc.YPlane(-d, 'periodic') + ymax = openmc.YPlane(d, 'periodic') main_cell = openmc.Cell(fill=rect_lattice, region=+xmin & -xmax & +ymin & -ymax) diff --git a/tests/unit_tests/dagmc/test.py b/tests/unit_tests/dagmc/test.py index bfe054dd82..e5d7725551 100644 --- a/tests/unit_tests/dagmc/test.py +++ b/tests/unit_tests/dagmc/test.py @@ -4,12 +4,12 @@ import numpy as np import pytest import openmc -import openmc.capi +import openmc.lib from tests import cdtemp pytestmark = pytest.mark.skipif( - not openmc.capi._dagmc_enabled(), + not openmc.lib._dagmc_enabled(), reason="DAGMC CAD geometry is not enabled.") @@ -38,7 +38,7 @@ def dagmc_model(request): model.tallies = [tally] # materials - u235 = openmc.Material(name="fuel") + u235 = openmc.Material(name="no-void fuel") u235.add_nuclide('U235', 1.0, 'ao') u235.set_density('g/cc', 11) u235.id = 40 @@ -60,15 +60,15 @@ def dagmc_model(request): with cdtemp(): shutil.copyfile(dagmc_file, "./dagmc.h5m") model.export_to_xml() - openmc.capi.init() + openmc.lib.init() yield - openmc.capi.finalize() + openmc.lib.finalize() @pytest.mark.parametrize("cell_id,exp_temp", ((1, 320.0), # assigned by material (2, 300.0), # assigned in dagmc file (3, 293.6))) # assigned by default def test_dagmc_temperatures(cell_id, exp_temp): - cell = openmc.capi.cells[cell_id] + cell = openmc.lib.cells[cell_id] assert np.isclose(cell.get_temperature(), exp_temp) diff --git a/tests/unit_tests/test_complex_cell_capi.py b/tests/unit_tests/test_complex_cell_bb.py similarity index 73% rename from tests/unit_tests/test_complex_cell_capi.py rename to tests/unit_tests/test_complex_cell_bb.py index a76fa63076..8db20f05c1 100644 --- a/tests/unit_tests/test_complex_cell_capi.py +++ b/tests/unit_tests/test_complex_cell_bb.py @@ -1,5 +1,5 @@ import numpy as np -import openmc.capi +import openmc.lib import pytest @pytest.fixture(autouse=True) @@ -27,27 +27,27 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): model.materials = (u235, u238, zr90, n14) - s1 = openmc.XPlane(x0=-10.0, boundary_type='vacuum') - s2 = openmc.XPlane(x0=-7.0) - s3 = openmc.XPlane(x0=-4.0) - s4 = openmc.XPlane(x0=4.0) - s5 = openmc.XPlane(x0=7.0) - s6 = openmc.XPlane(x0=10.0, boundary_type='vacuum') - s7 = openmc.XPlane(x0=0.0) + s1 = openmc.XPlane(-10.0, 'vacuum') + s2 = openmc.XPlane(-7.0) + s3 = openmc.XPlane(-4.0) + s4 = openmc.XPlane(4.0) + s5 = openmc.XPlane(7.0) + s6 = openmc.XPlane(10.0, 'vacuum') + s7 = openmc.XPlane(0.0) - s11 = openmc.YPlane(y0=-10.0, boundary_type='vacuum') - s12 = openmc.YPlane(y0=-7.0) - s13 = openmc.YPlane(y0=-4.0) - s14 = openmc.YPlane(y0=4.0) - s15 = openmc.YPlane(y0=7.0) - s16 = openmc.YPlane(y0=10.0, boundary_type='vacuum') - s17 = openmc.YPlane(y0=0.0) + s11 = openmc.YPlane(-10.0, 'vacuum') + s12 = openmc.YPlane(-7.0) + s13 = openmc.YPlane(-4.0) + s14 = openmc.YPlane(4.0) + s15 = openmc.YPlane(7.0) + s16 = openmc.YPlane(10.0, 'vacuum') + s17 = openmc.YPlane(0.0) c1 = openmc.Cell(fill=u235) c1.region = ~(-s3 | +s4 | ~(+s13 & -s14)) c2 = openmc.Cell(fill=u238) - c2.region = +s2 & -s5 & +s12 & -s15 & ~(+s3 & -s4 & +s13 & -s14) + c2.region = ~(+s3 & -s4 & +s13 & -s14) & +s2 & -s5 & +s12 & -s15 c3 = openmc.Cell(fill=zr90) c3.region = ((+s1 & -s7 & +s17 & -s16) | (+s7 & -s6 & +s11 & -s17)) \ @@ -73,12 +73,12 @@ def complex_cell(run_in_tmpdir, mpi_intracomm): model.export_to_xml() - openmc.capi.finalize() - openmc.capi.init(intracomm=mpi_intracomm) + openmc.lib.finalize() + openmc.lib.init(intracomm=mpi_intracomm) yield - openmc.capi.finalize() + openmc.lib.finalize() expected_results = ( (1, (( -4., -4., -np.inf), @@ -93,6 +93,6 @@ expected_results = ( (1, (( -4., -4., -np.inf), ( np.inf, np.inf, np.inf))) ) @pytest.mark.parametrize("cell_id,expected_box", expected_results) def test_cell_box(cell_id, expected_box): - cell_box = openmc.capi.cells[cell_id].bounding_box + cell_box = openmc.lib.cells[cell_id].bounding_box assert tuple(cell_box[0]) == expected_box[0] assert tuple(cell_box[1]) == expected_box[1] diff --git a/tests/unit_tests/test_data_decay.py b/tests/unit_tests/test_data_decay.py index b925506dfb..0cc7939904 100644 --- a/tests/unit_tests/test_data_decay.py +++ b/tests/unit_tests/test_data_decay.py @@ -10,9 +10,6 @@ from uncertainties import ufloat import openmc.data -_ENDF_DATA = os.environ['OPENMC_ENDF_DATA'] - - def ufloat_close(a, b): assert a.nominal_value == pytest.approx(b.nominal_value) assert a.std_dev == pytest.approx(b.std_dev) @@ -21,14 +18,16 @@ def ufloat_close(a, b): @pytest.fixture(scope='module') def nb90(): """Nb90 decay data.""" - filename = os.path.join(_ENDF_DATA, 'decay', 'dec-041_Nb_090.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'decay', 'dec-041_Nb_090.endf') return openmc.data.Decay.from_endf(filename) @pytest.fixture(scope='module') def u235_yields(): """U235 fission product yield data.""" - filename = os.path.join(_ENDF_DATA, 'nfy', 'nfy-092_U_235.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'nfy', 'nfy-092_U_235.endf') return openmc.data.FissionProductYields.from_endf(filename) diff --git a/tests/unit_tests/test_data_neutron.py b/tests/unit_tests/test_data_neutron.py index 40a286331e..ac6164e5b5 100644 --- a/tests/unit_tests/test_data_neutron.py +++ b/tests/unit_tests/test_data_neutron.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from collections.abc import Mapping, Callable import os @@ -8,9 +6,9 @@ import pandas as pd import pytest import openmc.data +from . import needs_njoy _TEMPERATURES = [300., 600., 900.] -_ENDF_DATA = os.environ['OPENMC_ENDF_DATA'] @pytest.fixture(scope='module') @@ -24,14 +22,16 @@ def pu239(): @pytest.fixture(scope='module') def xe135(): """Xe135 ENDF data (contains SLBW resonance range)""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-054_Xe_135.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-054_Xe_135.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def sm150(): """Sm150 ENDF data (contains MLBW resonance range)""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-062_Sm_150.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-062_Sm_150.endf') return openmc.data.IncidentNeutron.from_endf(filename) @@ -39,69 +39,79 @@ def sm150(): def gd154(): """Gd154 ENDF data (contains Reich Moore resonance range and reosnance covariance with LCOMP=1).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-064_Gd_154.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-064_Gd_154.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') def cl35(): """Cl35 ENDF data (contains RML resonance range)""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-017_Cl_035.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-017_Cl_035.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def am241(): """Am241 ENDF data (contains Madland-Nix fission energy distribution).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-095_Am_241.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-095_Am_241.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def u233(): """U233 ENDF data (contains Watt fission energy distribution).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-092_U_233.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-092_U_233.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def u236(): """U236 ENDF data (contains Watt fission energy distribution).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-092_U_236.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-092_U_236.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def na22(): """Na22 ENDF data (contains evaporation spectrum).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-011_Na_022.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_022.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def na23(): """Na23 ENDF data (contains MLBW resonance covariance with LCOMP=0).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-011_Na_023.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-011_Na_023.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') def be9(): """Be9 ENDF data (contains laboratory angle-energy distribution).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-004_Be_009.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-004_Be_009.endf') return openmc.data.IncidentNeutron.from_endf(filename) @pytest.fixture(scope='module') def h2(): - endf_file = os.path.join(_ENDF_DATA, 'neutrons', 'n-001_H_002.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + endf_file = os.path.join(endf_data, 'neutrons', 'n-001_H_002.endf') return openmc.data.IncidentNeutron.from_njoy( endf_file, temperatures=_TEMPERATURES) @pytest.fixture(scope='module') def am244(): - endf_file = os.path.join(_ENDF_DATA, 'neutrons', 'n-095_Am_244.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + endf_file = os.path.join(endf_data, 'neutrons', 'n-095_Am_244.endf') return openmc.data.IncidentNeutron.from_njoy(endf_file) @@ -109,21 +119,24 @@ def am244(): def ti50(): """Ti50 ENDF data (contains Multi-level Breit-Wigner resonance range and resonance covariance with LCOMP=1).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-022_Ti_050.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-022_Ti_050.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') def cf252(): """Cf252 ENDF data (contains RM resonance covariance with LCOMP=0).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-098_Cf_252.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-098_Cf_252.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @pytest.fixture(scope='module') def th232(): """Th232 ENDF data (contains RM resonance covariance with LCOMP=2).""" - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-090_Th_232.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-090_Th_232.endf') return openmc.data.IncidentNeutron.from_endf(filename, covariance=True) @@ -198,6 +211,7 @@ def test_fission(pu239): assert photon.particle == 'photon' +@needs_njoy def test_derived_products(am244): fission = am244.reactions[18] total_neutron = fission.derived_products[0] @@ -205,20 +219,22 @@ def test_derived_products(am244): assert total_neutron.yield_(6e6) == pytest.approx(4.2558) -def test_heating(run_in_tmpdir, am244): - assert 318 in am244.reactions - assert 999 in am244.reactions - # compare values in 999 reaction - total_heating = am244.reactions[301].xs["294K"] - fission_heating = am244.reactions[318].xs["294K"] - expected_y = total_heating.y - fission_heating(total_heating.x) - assert am244.reactions[999].xs["294K"].y == pytest.approx(expected_y) +def test_kerma(run_in_tmpdir, am244, h2): + # Make sure kerma w/ local photon is >= regular kerma + for nuc in (am244, h2): + assert 301 in nuc + assert 901 in nuc + for T in nuc.temperatures: + k, k_local = nuc[301].xs[T], nuc[901].xs[T] + assert np.all(k.x == k_local.x) + assert np.all(k_local.y >= k.y) - am244.export_to_hdf5("am244.h5") - read_in = openmc.data.IncidentNeutron.from_hdf5("am244.h5") - assert 318 in read_in.reactions - assert 999 in read_in.reactions - assert read_in.reactions[999].xs["294K"].y == pytest.approx(expected_y) + # Make sure 301/901 get exported/imported correctly + h2.export_to_hdf5("H2.h5") + read_in = openmc.data.IncidentNeutron.from_hdf5("H2.h5") + assert 301 in read_in + assert 901 in read_in + assert np.all(read_in[901].xs['300K'].y == h2[901].xs['300K'].y) def test_urr(pu239): @@ -236,6 +252,7 @@ def test_urr(pu239): assert ptable.table.shape[0] == ptable.energy.size +@needs_njoy def test_get_reaction_components(h2): assert h2.get_reaction_components(1) == [2, 16, 102] assert h2.get_reaction_components(101) == [102] @@ -453,8 +470,10 @@ def test_laboratory(be9): assert np.all((-1. <= mu.x) & (mu.x <= 1.)) +@needs_njoy def test_correlated(tmpdir): - endf_file = os.path.join(_ENDF_DATA, 'neutrons', 'n-014_Si_030.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + endf_file = os.path.join(endf_data, 'neutrons', 'n-014_Si_030.endf') si30 = openmc.data.IncidentNeutron.from_njoy(endf_file, heatr=False) # Convert to HDF5 and read back @@ -463,6 +482,7 @@ def test_correlated(tmpdir): si30_copy = openmc.data.IncidentNeutron.from_hdf5(filename) +@needs_njoy def test_nbody(tmpdir, h2): # Convert to HDF5 and read back filename = str(tmpdir.join('h2.h5')) @@ -477,8 +497,10 @@ def test_nbody(tmpdir, h2): assert nbody1.q_value == nbody2.q_value +@needs_njoy def test_ace_convert(run_in_tmpdir): - filename = os.path.join(_ENDF_DATA, 'neutrons', 'n-001_H_001.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') ace_ascii = 'ace_ascii' ace_binary = 'ace_binary' openmc.data.njoy.make_ace(filename, acer=ace_ascii) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index 9bbbcc8ecb..c767d19e6c 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -9,19 +9,17 @@ import pytest import openmc.data -_ENDF_DATA = os.environ['OPENMC_ENDF_DATA'] - - @pytest.fixture(scope='module') def elements_endf(): """Dictionary of element ENDF data indexed by atomic symbol.""" + endf_data = os.environ['OPENMC_ENDF_DATA'] elements = {'H': 1, 'O': 8, 'Al': 13, 'Cu': 29, 'Ag': 47, 'U': 92, 'Pu': 94} data = {} for symbol, Z in elements.items(): p_file = 'photoat-{:03}_{}_000.endf'.format(Z, symbol) - p_path = os.path.join(_ENDF_DATA, 'photoat', p_file) + p_path = os.path.join(endf_data, 'photoat', p_file) a_file = 'atom-{:03}_{}_000.endf'.format(Z, symbol) - a_path = os.path.join(_ENDF_DATA, 'atomic_relax', a_file) + a_path = os.path.join(endf_data, 'atomic_relax', a_file) data[symbol] = openmc.data.IncidentPhoton.from_endf(p_path, a_path) return data diff --git a/tests/unit_tests/test_data_thermal.py b/tests/unit_tests/test_data_thermal.py index 47cbe27b97..dc4d24628b 100644 --- a/tests/unit_tests/test_data_thermal.py +++ b/tests/unit_tests/test_data_thermal.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - from collections.abc import Callable from math import exp import os @@ -9,8 +7,7 @@ import numpy as np import pytest import openmc.data - -_ENDF_DATA = os.environ['OPENMC_ENDF_DATA'] +from . import needs_njoy @pytest.fixture(scope='module') @@ -32,8 +29,9 @@ def graphite(): @pytest.fixture(scope='module') def h2o_njoy(): """H in H2O generated using NJOY.""" - path_h1 = os.path.join(_ENDF_DATA, 'neutrons', 'n-001_H_001.endf') - path_h2o = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-HinH2O.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') + path_h2o = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') return openmc.data.ThermalScattering.from_njoy( path_h1, path_h2o, temperatures=[293.6, 500.0]) @@ -41,15 +39,17 @@ def h2o_njoy(): @pytest.fixture(scope='module') def hzrh(): """H in ZrH thermal scattering data.""" - filename = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-HinZrH.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') return openmc.data.ThermalScattering.from_endf(filename) @pytest.fixture(scope='module') def hzrh_njoy(): """H in ZrH generated using NJOY.""" - path_h1 = os.path.join(_ENDF_DATA, 'neutrons', 'n-001_H_001.endf') - path_hzrh = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-HinZrH.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + path_h1 = os.path.join(endf_data, 'neutrons', 'n-001_H_001.endf') + path_hzrh = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinZrH.endf') with_endf_data = openmc.data.ThermalScattering.from_njoy( path_h1, path_hzrh, temperatures=[296.0], iwt=0 ) @@ -62,7 +62,8 @@ def hzrh_njoy(): @pytest.fixture(scope='module') def sio2(): """SiO2 thermal scattering data.""" - filename = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-SiO2.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-SiO2.endf') return openmc.data.ThermalScattering.from_endf(filename) @@ -100,10 +101,11 @@ def test_graphite_xs(graphite): elastic = graphite.elastic.xs['296K'] assert elastic([1e-3, 1.0]) == pytest.approx([0.0, 0.62586153]) - +@needs_njoy def test_graphite_njoy(): - path_c0 = os.path.join(_ENDF_DATA, 'neutrons', 'n-006_C_000.endf') - path_gr = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-graphite.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + path_c0 = os.path.join(endf_data, 'neutrons', 'n-006_C_000.endf') + path_gr = os.path.join(endf_data, 'thermal_scatt', 'tsl-graphite.endf') graphite = openmc.data.ThermalScattering.from_njoy( path_c0, path_gr, temperatures=[296.0]) assert graphite.nuclides == ['C0', 'C12', 'C13'] @@ -112,6 +114,7 @@ def test_graphite_njoy(): assert graphite.temperatures == ['296K'] +@needs_njoy def test_export_to_hdf5(tmpdir, h2o_njoy, hzrh_njoy, graphite): filename = str(tmpdir.join('water.h5')) h2o_njoy.export_to_hdf5(filename) @@ -131,6 +134,7 @@ def test_export_to_hdf5(tmpdir, h2o_njoy, hzrh_njoy, graphite): assert os.path.exists(filename) +@needs_njoy def test_continuous_dist(h2o_njoy): for temperature, dist in h2o_njoy.inelastic.distribution.items(): assert temperature.endswith('K') @@ -138,7 +142,8 @@ def test_continuous_dist(h2o_njoy): def test_h2o_endf(): - filename = os.path.join(_ENDF_DATA, 'thermal_scatt', 'tsl-HinH2O.endf') + endf_data = os.environ['OPENMC_ENDF_DATA'] + filename = os.path.join(endf_data, 'thermal_scatt', 'tsl-HinH2O.endf') h2o = openmc.data.ThermalScattering.from_endf(filename) assert not h2o.elastic assert h2o.atomic_weight_ratio == pytest.approx(0.99917) @@ -173,6 +178,7 @@ def test_hzrh_elastic(hzrh): assert dist.debye_waller > 0.0 +@needs_njoy def test_hzrh_njoy(hzrh_njoy): endf, ace = hzrh_njoy diff --git a/tests/unit_tests/test_deplete_chain.py b/tests/unit_tests/test_deplete_chain.py index 3dcd3ad750..08c60d3ed1 100644 --- a/tests/unit_tests/test_deplete_chain.py +++ b/tests/unit_tests/test_deplete_chain.py @@ -12,8 +12,6 @@ import pytest from tests import cdtemp -_ENDF_DATA = Path(os.environ['OPENMC_ENDF_DATA']) - _TEST_CHAIN = """\ @@ -67,9 +65,10 @@ def test_len(): def test_from_endf(): """Test depletion chain building from ENDF files""" - decay_data = (_ENDF_DATA / 'decay').glob('*.endf') - fpy_data = (_ENDF_DATA / 'nfy').glob('*.endf') - neutron_data = (_ENDF_DATA / 'neutrons').glob('*.endf') + endf_data = Path(os.environ['OPENMC_ENDF_DATA']) + decay_data = (endf_data / 'decay').glob('*.endf') + fpy_data = (endf_data / 'nfy').glob('*.endf') + neutron_data = (endf_data / 'neutrons').glob('*.endf') chain = Chain.from_endf(decay_data, fpy_data, neutron_data) assert len(chain) == len(chain.nuclides) == len(chain.nuclide_dict) == 3820 @@ -385,13 +384,13 @@ def test_set_alpha_branches(): def test_simple_fission_yields(simple_chain): """Check the default fission yields that can be used to form the matrix """ - fission_yields = simple_chain.get_thermal_fission_yields() + fission_yields = simple_chain.get_default_fission_yields() assert fission_yields == {"C": {"A": 0.0292737, "B": 0.002566345}} def test_fission_yield_attribute(simple_chain): """Test the fission_yields property""" - thermal_yields = simple_chain.get_thermal_fission_yields() + thermal_yields = simple_chain.get_default_fission_yields() # generate default with property assert simple_chain.fission_yields[0] == thermal_yields empty_chain = Chain() diff --git a/tests/unit_tests/test_deplete_fission_yields.py b/tests/unit_tests/test_deplete_fission_yields.py index bedd702a90..854c530f95 100644 --- a/tests/unit_tests/test_deplete_fission_yields.py +++ b/tests/unit_tests/test_deplete_fission_yields.py @@ -8,7 +8,7 @@ import bisect import pytest import numpy as np import openmc -from openmc import capi +from openmc import lib from openmc.deplete.nuclide import Nuclide, FissionYieldDistribution from openmc.deplete.helpers import ( FissionYieldCutoffHelper, ConstantFissionYieldHelper, @@ -18,7 +18,7 @@ from openmc.deplete.helpers import ( @pytest.fixture(scope="module") def materials(tmpdir_factory): """Use C API to construct realistic materials for testing tallies""" - tmpdir = tmpdir_factory.mktemp("capi") + tmpdir = tmpdir_factory.mktemp("lib") orig = tmpdir.chdir() # Create proxy problem to please openmc mfuel = openmc.Material(name="test_fuel") @@ -40,8 +40,8 @@ def materials(tmpdir_factory): settings.export_to_xml() try: - with capi.run_in_memory(): - yield [capi.Material(), capi.Material()] + with lib.run_in_memory(): + yield [lib.Material(), lib.Material()] finally: # Convert to strings as os.remove in py 3.5 doesn't support Paths for file_path in ("settings.xml", "geometry.xml", "materials.xml", @@ -64,7 +64,7 @@ def proxy_tally_data(tally, fill=None): if not hasattr(tfilter, "bins"): continue this_bins = len(tfilter.bins) - if isinstance(tfilter, capi.EnergyFilter): + if isinstance(tfilter, lib.EnergyFilter): this_bins -= 1 n_bins *= max(this_bins, 1) data = np.empty((n_bins, n_nucs * n_scores, 3)) @@ -192,9 +192,9 @@ def test_cutoff_helper(materials, nuclide_bundle, therm_frac): assert fission_tally is not None filters = fission_tally.filters assert len(filters) == 2 - assert isinstance(filters[0], capi.MaterialFilter) + assert isinstance(filters[0], lib.MaterialFilter) assert len(filters[0].bins) == len(materials) - assert isinstance(filters[1], capi.EnergyFilter) + assert isinstance(filters[1], lib.EnergyFilter) # lower, cutoff, and upper energy assert len(filters[1].bins) == 3 @@ -235,9 +235,9 @@ def test_averaged_helper(materials, nuclide_bundle, avg_energy): assert fission_tally is not None fission_filters = fission_tally.filters assert len(fission_filters) == 2 - assert isinstance(fission_filters[0], capi.MaterialFilter) + assert isinstance(fission_filters[0], lib.MaterialFilter) assert len(fission_filters[0].bins) == len(materials) - assert isinstance(fission_filters[1], capi.EnergyFilter) + assert isinstance(fission_filters[1], lib.EnergyFilter) assert len(fission_filters[1].bins) == 2 assert fission_tally.scores == ["fission"] assert fission_tally.nuclides == list(tallied_nucs) @@ -246,9 +246,9 @@ def test_averaged_helper(materials, nuclide_bundle, avg_energy): assert weighted_tally is not None weighted_filters = weighted_tally.filters assert len(weighted_filters) == 2 - assert isinstance(weighted_filters[0], capi.MaterialFilter) + assert isinstance(weighted_filters[0], lib.MaterialFilter) assert len(weighted_filters[0].bins) == len(materials) - assert isinstance(weighted_filters[1], capi.EnergyFunctionFilter) + assert isinstance(weighted_filters[1], lib.EnergyFunctionFilter) assert len(weighted_filters[1].energy) == 2 assert len(weighted_filters[1].y) == 2 assert weighted_tally.scores == ["fission"] diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index 69211f72e2..70d90b6157 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -280,3 +280,24 @@ def test_from_xml(run_in_tmpdir, mixed_lattice_model): ll, ur = geom.bounding_box assert ll == pytest.approx((-6.0, -6.0, -np.inf)) assert ur == pytest.approx((6.0, 6.0, np.inf)) + + +def test_rotation_matrix(): + """Test ability to set a rotation matrix directly""" + y = openmc.YPlane() + cyl1 = openmc.ZCylinder(r=1.0) + cyl2 = openmc.ZCylinder(r=2.0, boundary_type='vacuum') + + # Create a universe and then reflect in the y-direction + c1 = openmc.Cell(region=-cyl1 & +y) + c2 = openmc.Cell(region=+cyl1 & +y) + c3 = openmc.Cell(region=-y) + univ = openmc.Universe(cells=[c1, c2, c3]) + c = openmc.Cell(fill=univ, region=-cyl2) + c.rotation = [[1, 0, 0], [0, -1, 0], [0, 0, 1]] + assert np.all(c.rotation_matrix == c.rotation) + geom = openmc.Geometry([c]) + + assert geom.find((0.0, 0.5, 0.0))[-1] == c3 + assert geom.find((0.0, -0.5, 0.0))[-1] == c1 + assert geom.find((0.0, -1.5, 0.0))[-1] == c2 diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_lib.py similarity index 59% rename from tests/unit_tests/test_capi.py rename to tests/unit_tests/test_lib.py index c8ad0907c5..ad198f255c 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_lib.py @@ -5,7 +5,7 @@ import numpy as np import pytest import openmc import openmc.exceptions as exc -import openmc.capi +import openmc.lib from tests import cdtemp @@ -50,68 +50,68 @@ def pincell_model(): @pytest.fixture(scope='module') -def capi_init(pincell_model, mpi_intracomm): - openmc.capi.init(intracomm=mpi_intracomm) +def lib_init(pincell_model, mpi_intracomm): + openmc.lib.init(intracomm=mpi_intracomm) yield - openmc.capi.finalize() + openmc.lib.finalize() @pytest.fixture(scope='module') -def capi_simulation_init(capi_init): - openmc.capi.simulation_init() +def lib_simulation_init(lib_init): + openmc.lib.simulation_init() yield @pytest.fixture(scope='module') -def capi_run(capi_simulation_init): - openmc.capi.run() +def lib_run(lib_simulation_init): + openmc.lib.run() -def test_cell_mapping(capi_init): - cells = openmc.capi.cells +def test_cell_mapping(lib_init): + cells = openmc.lib.cells assert isinstance(cells, Mapping) assert len(cells) == 3 for cell_id, cell in cells.items(): - assert isinstance(cell, openmc.capi.Cell) + assert isinstance(cell, openmc.lib.Cell) assert cell_id == cell.id -def test_cell(capi_init): - cell = openmc.capi.cells[1] - assert isinstance(cell.fill, openmc.capi.Material) - cell.fill = openmc.capi.materials[1] +def test_cell(lib_init): + cell = openmc.lib.cells[1] + assert isinstance(cell.fill, openmc.lib.Material) + cell.fill = openmc.lib.materials[1] assert str(cell) == 'Cell[0]' assert cell.name == "Fuel" cell.name = "Not fuel" assert cell.name == "Not fuel" -def test_cell_temperature(capi_init): - cell = openmc.capi.cells[1] +def test_cell_temperature(lib_init): + cell = openmc.lib.cells[1] cell.set_temperature(100.0, 0) assert cell.get_temperature(0) == 100.0 cell.set_temperature(200) assert cell.get_temperature() == 200.0 -def test_new_cell(capi_init): +def test_new_cell(lib_init): with pytest.raises(exc.AllocationError): - openmc.capi.Cell(1) - new_cell = openmc.capi.Cell() - new_cell_with_id = openmc.capi.Cell(10) - assert len(openmc.capi.cells) == 5 + openmc.lib.Cell(1) + new_cell = openmc.lib.Cell() + new_cell_with_id = openmc.lib.Cell(10) + assert len(openmc.lib.cells) == 5 -def test_material_mapping(capi_init): - mats = openmc.capi.materials +def test_material_mapping(lib_init): + mats = openmc.lib.materials assert isinstance(mats, Mapping) assert len(mats) == 3 for mat_id, mat in mats.items(): - assert isinstance(mat, openmc.capi.Material) + assert isinstance(mat, openmc.lib.Material) assert mat_id == mat.id -def test_material(capi_init): - m = openmc.capi.materials[3] +def test_material(lib_init): + m = openmc.lib.materials[3] assert m.nuclides == ['H1', 'O16', 'B10', 'B11'] old_dens = m.densities @@ -136,32 +136,32 @@ def test_material(capi_init): m.name = "Not hot borated water" assert m.name == "Not hot borated water" -def test_material_add_nuclide(capi_init): - m = openmc.capi.materials[3] +def test_material_add_nuclide(lib_init): + m = openmc.lib.materials[3] m.add_nuclide('Xe135', 1e-12) assert m.nuclides[-1] == 'Xe135' assert m.densities[-1] == 1e-12 -def test_new_material(capi_init): +def test_new_material(lib_init): with pytest.raises(exc.AllocationError): - openmc.capi.Material(1) - new_mat = openmc.capi.Material() - new_mat_with_id = openmc.capi.Material(10) - assert len(openmc.capi.materials) == 5 + openmc.lib.Material(1) + new_mat = openmc.lib.Material() + new_mat_with_id = openmc.lib.Material(10) + assert len(openmc.lib.materials) == 5 -def test_nuclide_mapping(capi_init): - nucs = openmc.capi.nuclides +def test_nuclide_mapping(lib_init): + nucs = openmc.lib.nuclides assert isinstance(nucs, Mapping) assert len(nucs) == 13 for name, nuc in nucs.items(): - assert isinstance(nuc, openmc.capi.Nuclide) + assert isinstance(nuc, openmc.lib.Nuclide) assert name == nuc.name -def test_settings(capi_init): - settings = openmc.capi.settings +def test_settings(lib_init): + settings = openmc.lib.settings assert settings.batches == 10 settings.batches = 10 assert settings.inactive == 5 @@ -175,36 +175,36 @@ def test_settings(capi_init): settings.run_mode = 'eigenvalue' -def test_tally_mapping(capi_init): - tallies = openmc.capi.tallies +def test_tally_mapping(lib_init): + tallies = openmc.lib.tallies assert isinstance(tallies, Mapping) assert len(tallies) == 3 for tally_id, tally in tallies.items(): - assert isinstance(tally, openmc.capi.Tally) + assert isinstance(tally, openmc.lib.Tally) assert tally_id == tally.id -def test_energy_function_filter(capi_init): +def test_energy_function_filter(lib_init): """Test special __new__ and __init__ for EnergyFunctionFilter""" - efunc = openmc.capi.EnergyFunctionFilter([0.0, 1.0], [0.0, 2.0]) + efunc = openmc.lib.EnergyFunctionFilter([0.0, 1.0], [0.0, 2.0]) assert len(efunc.energy) == 2 assert (efunc.energy == [0.0, 1.0]).all() assert len(efunc.y) == 2 assert (efunc.y == [0.0, 2.0]).all() -def test_tally(capi_init): - t = openmc.capi.tallies[1] +def test_tally(lib_init): + t = openmc.lib.tallies[1] assert t.type == 'volume' assert len(t.filters) == 2 - assert isinstance(t.filters[0], openmc.capi.MaterialFilter) - assert isinstance(t.filters[1], openmc.capi.EnergyFilter) + assert isinstance(t.filters[0], openmc.lib.MaterialFilter) + assert isinstance(t.filters[1], openmc.lib.EnergyFilter) # Create new filter and replace existing with pytest.raises(exc.AllocationError): - openmc.capi.MaterialFilter(uid=1) - mats = openmc.capi.materials - f = openmc.capi.MaterialFilter([mats[2], mats[1]]) + openmc.lib.MaterialFilter(uid=1) + mats = openmc.lib.materials + f = openmc.lib.MaterialFilter([mats[2], mats[1]]) assert f.bins[0] == mats[2] assert f.bins[1] == mats[1] t.filters = [f] @@ -221,17 +221,17 @@ def test_tally(capi_init): t.scores = new_scores assert t.scores == new_scores - t2 = openmc.capi.tallies[2] + t2 = openmc.lib.tallies[2] assert len(t2.filters) == 2 - assert isinstance(t2.filters[0], openmc.capi.ZernikeFilter) - assert isinstance(t2.filters[1], openmc.capi.CellFilter) + assert isinstance(t2.filters[0], openmc.lib.ZernikeFilter) + assert isinstance(t2.filters[1], openmc.lib.CellFilter) assert len(t2.filters[1].bins) == 3 assert t2.filters[0].order == 5 - t3 = openmc.capi.tallies[3] + t3 = openmc.lib.tallies[3] assert len(t3.filters) == 1 t3_f = t3.filters[0] - assert isinstance(t3_f, openmc.capi.EnergyFunctionFilter) + assert isinstance(t3_f, openmc.lib.EnergyFunctionFilter) assert len(t3_f.energy) == 2 assert len(t3_f.y) == 2 t3_f.set_data([0.0, 1.0, 2.0], [0.0, 1.0, 4.0]) @@ -239,146 +239,155 @@ def test_tally(capi_init): assert len(t3_f.y) == 3 -def test_new_tally(capi_init): +def test_new_tally(lib_init): with pytest.raises(exc.AllocationError): - openmc.capi.Material(1) - new_tally = openmc.capi.Tally() + openmc.lib.Material(1) + new_tally = openmc.lib.Tally() new_tally.scores = ['flux'] - new_tally_with_id = openmc.capi.Tally(10) + new_tally_with_id = openmc.lib.Tally(10) new_tally_with_id.scores = ['flux'] - assert len(openmc.capi.tallies) == 5 + assert len(openmc.lib.tallies) == 5 -def test_tally_activate(capi_simulation_init): - t = openmc.capi.tallies[1] +def test_tally_activate(lib_simulation_init): + t = openmc.lib.tallies[1] assert not t.active t.active = True assert t.active -def test_tally_results(capi_run): - t = openmc.capi.tallies[1] - assert t.num_realizations == 10 # t was made active in test_tally +def test_tally_writable(lib_simulation_init): + t = openmc.lib.tallies[1] + assert t.writable + t.writable = False + assert not t.writable + # Revert tally to writable state for lib_run fixtures + t.writable = True + + +def test_tally_results(lib_run): + t = openmc.lib.tallies[1] + assert t.num_realizations == 10 # t was made active in test_tally_active assert np.all(t.mean >= 0) nonzero = (t.mean > 0.0) assert np.all(t.std_dev[nonzero] >= 0) assert np.all(t.ci_width()[nonzero] >= 1.95*t.std_dev[nonzero]) - t2 = openmc.capi.tallies[2] + t2 = openmc.lib.tallies[2] n = 5 assert t2.mean.size == (n + 1) * (n + 2) // 2 * 3 # Number of Zernike coeffs * 3 cells -def test_global_tallies(capi_run): - assert openmc.capi.num_realizations() == 5 - gt = openmc.capi.global_tallies() +def test_global_tallies(lib_run): + assert openmc.lib.num_realizations() == 5 + gt = openmc.lib.global_tallies() for mean, std_dev in gt: assert mean >= 0 -def test_statepoint(capi_run): - openmc.capi.statepoint_write('test_sp.h5') +def test_statepoint(lib_run): + openmc.lib.statepoint_write('test_sp.h5') assert os.path.exists('test_sp.h5') -def test_source_bank(capi_run): - source = openmc.capi.source_bank() +def test_source_bank(lib_run): + source = openmc.lib.source_bank() assert np.all(source['E'] > 0.0) assert np.all(source['wgt'] == 1.0) assert np.allclose(np.linalg.norm(source['u'], axis=1), 1.0) -def test_by_batch(capi_run): - openmc.capi.hard_reset() +def test_by_batch(lib_run): + openmc.lib.hard_reset() # Running next batch before simulation is initialized should raise an # exception with pytest.raises(exc.AllocationError): - openmc.capi.next_batch() + openmc.lib.next_batch() - openmc.capi.simulation_init() + openmc.lib.simulation_init() try: - for _ in openmc.capi.iter_batches(): + for _ in openmc.lib.iter_batches(): # Make sure we can get k-effective during inactive/active batches - mean, std_dev = openmc.capi.keff() + mean, std_dev = openmc.lib.keff() assert 0.0 < mean < 2.5 assert std_dev > 0.0 - assert openmc.capi.num_realizations() == 5 + assert openmc.lib.num_realizations() == 5 for i in range(3): - openmc.capi.next_batch() - assert openmc.capi.num_realizations() == 8 + openmc.lib.next_batch() + assert openmc.lib.num_realizations() == 8 finally: - openmc.capi.simulation_finalize() + openmc.lib.simulation_finalize() -def test_reset(capi_run): +def test_reset(lib_run): # Init and run 10 batches. - openmc.capi.hard_reset() - openmc.capi.simulation_init() + openmc.lib.hard_reset() + openmc.lib.simulation_init() try: for i in range(10): - openmc.capi.next_batch() + openmc.lib.next_batch() # Make sure there are 5 realizations for the 5 active batches. - assert openmc.capi.num_realizations() == 5 - assert openmc.capi.tallies[2].num_realizations == 5 - _, keff_sd1 = openmc.capi.keff() - tally_sd1 = openmc.capi.tallies[2].std_dev[0] + assert openmc.lib.num_realizations() == 5 + assert openmc.lib.tallies[2].num_realizations == 5 + _, keff_sd1 = openmc.lib.keff() + tally_sd1 = openmc.lib.tallies[2].std_dev[0] # Reset and run 3 more batches. Check the number of realizations. - openmc.capi.reset() + openmc.lib.reset() for i in range(3): - openmc.capi.next_batch() - assert openmc.capi.num_realizations() == 3 - assert openmc.capi.tallies[2].num_realizations == 3 + openmc.lib.next_batch() + assert openmc.lib.num_realizations() == 3 + assert openmc.lib.tallies[2].num_realizations == 3 # Check the tally std devs to make sure results were cleared. - _, keff_sd2 = openmc.capi.keff() - tally_sd2 = openmc.capi.tallies[2].std_dev[0] + _, keff_sd2 = openmc.lib.keff() + tally_sd2 = openmc.lib.tallies[2].std_dev[0] assert keff_sd2 > keff_sd1 assert tally_sd2 > tally_sd1 finally: - openmc.capi.simulation_finalize() + openmc.lib.simulation_finalize() -def test_reproduce_keff(capi_init): +def test_reproduce_keff(lib_init): # Get k-effective after run - openmc.capi.hard_reset() - openmc.capi.run() - keff0 = openmc.capi.keff() + openmc.lib.hard_reset() + openmc.lib.run() + keff0 = openmc.lib.keff() # Reset, run again, and get k-effective again. they should match - openmc.capi.hard_reset() - openmc.capi.run() - keff1 = openmc.capi.keff() + openmc.lib.hard_reset() + openmc.lib.run() + keff1 = openmc.lib.keff() assert keff0 == pytest.approx(keff1) -def test_find_cell(capi_init): - cell, instance = openmc.capi.find_cell((0., 0., 0.)) - assert cell is openmc.capi.cells[1] - cell, instance = openmc.capi.find_cell((0.4, 0., 0.)) - assert cell is openmc.capi.cells[2] +def test_find_cell(lib_init): + cell, instance = openmc.lib.find_cell((0., 0., 0.)) + assert cell is openmc.lib.cells[1] + cell, instance = openmc.lib.find_cell((0.4, 0., 0.)) + assert cell is openmc.lib.cells[2] with pytest.raises(exc.GeometryError): - openmc.capi.find_cell((100., 100., 100.)) + openmc.lib.find_cell((100., 100., 100.)) -def test_find_material(capi_init): - mat = openmc.capi.find_material((0., 0., 0.)) - assert mat is openmc.capi.materials[1] - mat = openmc.capi.find_material((0.4, 0., 0.)) - assert mat is openmc.capi.materials[2] +def test_find_material(lib_init): + mat = openmc.lib.find_material((0., 0., 0.)) + assert mat is openmc.lib.materials[1] + mat = openmc.lib.find_material((0.4, 0., 0.)) + assert mat is openmc.lib.materials[2] -def test_mesh(capi_init): - mesh = openmc.capi.RegularMesh() +def test_mesh(lib_init): + mesh = openmc.lib.RegularMesh() mesh.dimension = (2, 3, 4) assert mesh.dimension == (2, 3, 4) with pytest.raises(exc.AllocationError): - mesh2 = openmc.capi.RegularMesh(mesh.id) + mesh2 = openmc.lib.RegularMesh(mesh.id) # Make sure each combination of parameters works ll = (0., 0., 0.) @@ -394,70 +403,70 @@ def test_mesh(capi_init): assert mesh.upper_right == pytest.approx(ur) assert mesh.width == pytest.approx(width) - meshes = openmc.capi.meshes + meshes = openmc.lib.meshes assert isinstance(meshes, Mapping) assert len(meshes) == 1 for mesh_id, mesh in meshes.items(): - assert isinstance(mesh, openmc.capi.RegularMesh) + assert isinstance(mesh, openmc.lib.RegularMesh) assert mesh_id == mesh.id - mf = openmc.capi.MeshFilter(mesh) + mf = openmc.lib.MeshFilter(mesh) assert mf.mesh == mesh - msf = openmc.capi.MeshSurfaceFilter(mesh) + msf = openmc.lib.MeshSurfaceFilter(mesh) assert msf.mesh == mesh -def test_restart(capi_init, mpi_intracomm): +def test_restart(lib_init, mpi_intracomm): # Finalize and re-init to make internal state consistent with XML. - openmc.capi.hard_reset() - openmc.capi.finalize() - openmc.capi.init(intracomm=mpi_intracomm) - openmc.capi.simulation_init() + openmc.lib.hard_reset() + openmc.lib.finalize() + openmc.lib.init(intracomm=mpi_intracomm) + openmc.lib.simulation_init() # Run for 7 batches then write a statepoint. for i in range(7): - openmc.capi.next_batch() - openmc.capi.statepoint_write('restart_test.h5', True) + openmc.lib.next_batch() + openmc.lib.statepoint_write('restart_test.h5', True) # Run 3 more batches and copy the keff. for i in range(3): - openmc.capi.next_batch() - keff0 = openmc.capi.keff() + openmc.lib.next_batch() + keff0 = openmc.lib.keff() # Restart the simulation from the statepoint and the 3 remaining active batches. - openmc.capi.simulation_finalize() - openmc.capi.hard_reset() - openmc.capi.finalize() - openmc.capi.init(args=('-r', 'restart_test.h5')) - openmc.capi.simulation_init() + openmc.lib.simulation_finalize() + openmc.lib.hard_reset() + openmc.lib.finalize() + openmc.lib.init(args=('-r', 'restart_test.h5')) + openmc.lib.simulation_init() for i in range(3): - openmc.capi.next_batch() - keff1 = openmc.capi.keff() - openmc.capi.simulation_finalize() + openmc.lib.next_batch() + keff1 = openmc.lib.keff() + openmc.lib.simulation_finalize() # Compare the keff values. assert keff0 == pytest.approx(keff1) -def test_load_nuclide(capi_init): +def test_load_nuclide(lib_init): # load multiple nuclides - openmc.capi.load_nuclide('H3') - assert 'H3' in openmc.capi.nuclides - openmc.capi.load_nuclide('Pu239') - assert 'Pu239' in openmc.capi.nuclides + openmc.lib.load_nuclide('H3') + assert 'H3' in openmc.lib.nuclides + openmc.lib.load_nuclide('Pu239') + assert 'Pu239' in openmc.lib.nuclides # load non-existent nuclide with pytest.raises(exc.DataError): - openmc.capi.load_nuclide('Pu3') + openmc.lib.load_nuclide('Pu3') -def test_id_map(capi_init): +def test_id_map(lib_init): expected_ids = np.array([[(3, 3), (2, 2), (3, 3)], [(2, 2), (1, 1), (2, 2)], [(3, 3), (2, 2), (3, 3)]], dtype='int32') # create a plot object - s = openmc.capi.plot._PlotBase() + s = openmc.lib.plot._PlotBase() s.width = 1.26 s.height = 1.26 s.v_res = 3 @@ -466,17 +475,17 @@ def test_id_map(capi_init): s.basis = 'xy' s.level = -1 - ids = openmc.capi.plot.id_map(s) + ids = openmc.lib.plot.id_map(s) assert np.array_equal(expected_ids, ids) -def test_property_map(capi_init): +def test_property_map(lib_init): expected_properties = np.array( [[(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)], [ (293.6, 6.55), (293.6, 10.29769), (293.6, 6.55)], [(293.6, 0.740582), (293.6, 6.55), (293.6, 0.740582)]], dtype='float') # create a plot object - s = openmc.capi.plot._PlotBase() + s = openmc.lib.plot._PlotBase() s.width = 1.26 s.height = 1.26 s.v_res = 3 @@ -485,13 +494,13 @@ def test_property_map(capi_init): s.basis = 'xy' s.level = -1 - properties = openmc.capi.plot.property_map(s) + properties = openmc.lib.plot.property_map(s) assert np.allclose(expected_properties, properties, atol=1e-04) -def test_position(capi_init): +def test_position(lib_init): - pos = openmc.capi.plot._Position(1.0, 2.0, 3.0) + pos = openmc.lib.plot._Position(1.0, 2.0, 3.0) assert tuple(pos) == (1.0, 2.0, 3.0) @@ -502,11 +511,11 @@ def test_position(capi_init): assert tuple(pos) == (1.3, 2.3, 3.3) -def test_global_bounding_box(capi_init): +def test_global_bounding_box(lib_init): expected_llc = (-0.63, -0.63, -np.inf) expected_urc = (0.63, 0.63, np.inf) - llc, urc = openmc.capi.global_bounding_box() + llc, urc = openmc.lib.global_bounding_box() assert tuple(llc) == expected_llc assert tuple(urc) == expected_urc diff --git a/tests/unit_tests/test_math.py b/tests/unit_tests/test_math.py index e6b2ddecd6..7c0218fbd7 100644 --- a/tests/unit_tests/test_math.py +++ b/tests/unit_tests/test_math.py @@ -2,7 +2,7 @@ import numpy as np import scipy as sp import openmc -import openmc.capi +import openmc.lib import pytest @@ -15,10 +15,10 @@ def test_t_percentile(): # The reference solutions come from Scipy ref_ts = [[sp.stats.t.ppf(p, df) for p in test_ps] for df in test_dfs] - test_ts = [[openmc.capi.math.t_percentile(p, df) for p in test_ps] + test_ts = [[openmc.lib.math.t_percentile(p, df) for p in test_ps] for df in test_dfs] - # The 5 DoF approximation in openmc.capi.math.t_percentile is off by up to + # The 5 DoF approximation in openmc.lib.math.t_percentile is off by up to # 8e-3 from the scipy solution, so test that one separately with looser # tolerance assert np.allclose(ref_ts[:-1], test_ts[:-1]) @@ -35,7 +35,7 @@ def test_calc_pn(): test_vals = [] for x in test_xs: - test_vals.append(openmc.capi.math.calc_pn(max_order, x).tolist()) + test_vals.append(openmc.lib.math.calc_pn(max_order, x).tolist()) test_vals = np.swapaxes(np.array(test_vals), 0, 1) @@ -55,7 +55,7 @@ def test_evaluate_legendre(): # evaluate legendre incorporates the (2l+1)/2 term on its own test_coeffs = [1. for l in range(max_order + 1)] - test_vals = np.array([openmc.capi.math.evaluate_legendre(test_coeffs, x) + test_vals = np.array([openmc.lib.math.evaluate_legendre(test_coeffs, x) for x in test_xs]) assert np.allclose(ref_vals, test_vals) @@ -98,7 +98,7 @@ def test_calc_rn(): ref_vals.append(ylm) test_vals = [] - test_vals = openmc.capi.math.calc_rn(max_order, test_uvw) + test_vals = openmc.lib.math.calc_rn(max_order, test_uvw) assert np.allclose(ref_vals, test_vals) @@ -133,7 +133,7 @@ def test_calc_zn(): -8.98437500e-02, -1.08693628e-01, 1.78813094e-01, -1.98191857e-01, 1.65964201e-02, 2.77013853e-04]) - test_vals = openmc.capi.math.calc_zn(n, rho, phi) + test_vals = openmc.lib.math.calc_zn(n, rho, phi) assert np.allclose(ref_vals, test_vals) @@ -147,7 +147,7 @@ def test_calc_zn_rad(): 1.00000000e+00, -5.00000000e-01, -1.25000000e-01, 4.37500000e-01, -2.89062500e-01,-8.98437500e-02]) - test_vals = openmc.capi.math.calc_zn_rad(n, rho) + test_vals = openmc.lib.math.calc_zn_rad(n, rho) assert np.allclose(ref_vals, test_vals) @@ -160,7 +160,7 @@ def test_rotate_angle(): # reference: mu of 0 pulls the vector the bottom, so: ref_uvw = np.array([0., 0., -1.]) - test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) + test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi) assert np.array_equal(ref_uvw, test_uvw) @@ -168,51 +168,51 @@ def test_rotate_angle(): mu = 1. ref_uvw = np.array([1., 0., 0.]) - test_uvw = openmc.capi.math.rotate_angle(uvw0, mu, phi) + test_uvw = openmc.lib.math.rotate_angle(uvw0, mu, phi) assert np.array_equal(ref_uvw, test_uvw) # Now to test phi is None mu = 0.9 - settings = openmc.capi.settings + settings = openmc.lib.settings settings.seed = 1 # When seed = 1, phi will be sampled as 1.9116495709698769 # The resultant reference is from hand-calculations given the above ref_uvw = [0.9, 0.410813051297112, 0.1457142302040] - test_uvw = openmc.capi.math.rotate_angle(uvw0, mu) + test_uvw = openmc.lib.math.rotate_angle(uvw0, mu) assert np.allclose(ref_uvw, test_uvw) def test_maxwell_spectrum(): - settings = openmc.capi.settings + settings = openmc.lib.settings settings.seed = 1 T = 0.5 ref_val = 0.6129982175261098 - test_val = openmc.capi.math.maxwell_spectrum(T) + test_val = openmc.lib.math.maxwell_spectrum(T) assert ref_val == test_val def test_watt_spectrum(): - settings = openmc.capi.settings + settings = openmc.lib.settings settings.seed = 1 a = 0.5 b = 0.75 ref_val = 0.6247242713640233 - test_val = openmc.capi.math.watt_spectrum(a, b) + test_val = openmc.lib.math.watt_spectrum(a, b) assert ref_val == test_val def test_normal_dist(): - settings = openmc.capi.settings + settings = openmc.lib.settings settings.seed = 1 a = 14.08 b = 0.0 ref_val = 14.08 - test_val = openmc.capi.math.normal_variate(a, b) + test_val = openmc.lib.math.normal_variate(a, b) assert ref_val == pytest.approx(test_val) @@ -220,7 +220,7 @@ def test_normal_dist(): a = 14.08 b = 1.0 ref_val = 16.436645416691427 - test_val = openmc.capi.math.normal_variate(a, b) + test_val = openmc.lib.math.normal_variate(a, b) assert ref_val == pytest.approx(test_val) @@ -234,13 +234,13 @@ def test_broaden_wmp_polynomials(): n = 6 ref_val = [2., 1.41421356, 1.0001, 0.70731891, 0.50030001, 0.353907] - test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) + test_val = openmc.lib.math.broaden_wmp_polynomials(test_E, test_dopp, n) assert np.allclose(ref_val, test_val) # now beta < 6 test_dopp = 5. ref_val = [1.99999885, 1.41421356, 1.04, 0.79195959, 0.6224, 0.50346003] - test_val = openmc.capi.math.broaden_wmp_polynomials(test_E, test_dopp, n) + test_val = openmc.lib.math.broaden_wmp_polynomials(test_E, test_dopp, n) assert np.allclose(ref_val, test_val) diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py index 782373414c..c1f8c039e5 100644 --- a/tests/unit_tests/test_model_triso.py +++ b/tests/unit_tests/test_model_triso.py @@ -34,12 +34,12 @@ def centers(request, container): @pytest.fixture(scope='module') def centers_rectangular_prism(): - min_x = openmc.XPlane(x0=0) - max_x = openmc.XPlane(x0=1) - min_y = openmc.YPlane(y0=0) - max_y = openmc.YPlane(y0=1) - min_z = openmc.ZPlane(z0=0) - max_z = openmc.ZPlane(z0=1) + min_x = openmc.XPlane(0) + max_x = openmc.XPlane(1) + min_y = openmc.YPlane(0) + max_y = openmc.YPlane(1) + min_z = openmc.ZPlane(0) + max_z = openmc.ZPlane(1) region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) @@ -48,8 +48,8 @@ def centers_rectangular_prism(): @pytest.fixture(scope='module') def centers_x_cylinder(): cylinder = openmc.XCylinder(r=1, y0=1, z0=2) - min_x = openmc.XPlane(x0=0) - max_x = openmc.XPlane(x0=1) + min_x = openmc.XPlane(0) + max_x = openmc.XPlane(1) region = +min_x & -max_x & -cylinder return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) @@ -58,8 +58,8 @@ def centers_x_cylinder(): @pytest.fixture(scope='module') def centers_y_cylinder(): cylinder = openmc.YCylinder(r=1, x0=1, z0=2) - min_y = openmc.YPlane(y0=0) - max_y = openmc.YPlane(y0=1) + min_y = openmc.YPlane(0) + max_y = openmc.YPlane(1) region = +min_y & -max_y & -cylinder return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) @@ -68,8 +68,8 @@ def centers_y_cylinder(): @pytest.fixture(scope='module') def centers_z_cylinder(): cylinder = openmc.ZCylinder(r=1, x0=1, y0=2) - min_z = openmc.ZPlane(z0=0) - max_z = openmc.ZPlane(z0=1) + min_z = openmc.ZPlane(0) + max_z = openmc.ZPlane(1) region = +min_z & -max_z & -cylinder return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py index d1074fd7d9..f9656ebb4b 100644 --- a/tests/unit_tests/test_region.py +++ b/tests/unit_tests/test_region.py @@ -11,8 +11,8 @@ def reset(): def test_union(reset): - s1 = openmc.XPlane(surface_id=1, x0=5) - s2 = openmc.XPlane(surface_id=2, x0=-5) + s1 = openmc.XPlane(x0=5, surface_id=1) + s2 = openmc.XPlane(x0=-5, surface_id=2) region = +s1 | -s2 assert isinstance(region, openmc.Union) @@ -42,8 +42,8 @@ def test_union(reset): def test_intersection(reset): - s1 = openmc.XPlane(surface_id=1, x0=5) - s2 = openmc.XPlane(surface_id=2, x0=-5) + s1 = openmc.XPlane(x0=5, surface_id=1) + s2 = openmc.XPlane(x0=-5, surface_id=2) region = -s1 & +s2 assert isinstance(region, openmc.Intersection) @@ -75,9 +75,9 @@ def test_intersection(reset): 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.) + zcyl = openmc.ZCylinder(r=1., surface_id=1) + z0 = openmc.ZPlane(-5., surface_id=2) + z1 = openmc.ZPlane(5., surface_id=3) outside = +zcyl | -z0 | +z1 inside = ~outside outside_equiv = ~(-zcyl & +z0 & -z1) @@ -151,8 +151,8 @@ def test_extend_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.) + s2 = openmc.ZPlane(-10., surface_id=2) + s3 = openmc.ZPlane(10., surface_id=3) surfs = {1: s1, 2: s2, 3: s3} r = openmc.Region.from_expression('-1 2 -3', surfs) diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 8ce1040213..703010a059 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -55,7 +55,7 @@ def test_plane_from_points(): def test_xplane(): - s = openmc.XPlane(x0=3., boundary_type='reflective') + s = openmc.XPlane(3., 'reflective') assert s.x0 == 3. assert s.boundary_type == 'reflective'