diff --git a/CMakeLists.txt b/CMakeLists.txt index cb92be848..4033d2466 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) -project(openmc CXX) +project(openmc C CXX) # Setup output directories set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) @@ -20,16 +20,13 @@ option(optimize "Turn on all compiler optimization flags" OFF) option(coverage "Compile with coverage analysis flags" OFF) option(dagmc "Enable support for DAGMC (CAD) geometry" OFF) -# Maximum number of nested coordinates levels -set(maxcoord 10 CACHE STRING "Maximum number of nested coordinate levels") - #=============================================================================== # MPI for distributed-memory parallelism #=============================================================================== set(MPI_ENABLED FALSE) if($ENV{CXX} MATCHES "(mpi[^/]*|CC)$") - message("-- Detected MPI wrapper: $ENV{CXX}") + message(STATUS "Detected MPI wrapper: $ENV{CXX}") set(MPI_ENABLED TRUE) endif() @@ -38,9 +35,6 @@ endif() #=============================================================================== if(dagmc) find_package(DAGMC REQUIRED) - if(NOT DAGMC_FOUND) - message(FATAL_ERROR "Could not find DAGMC installation") - endif() link_directories(${DAGMC_LIBRARY_DIRS}) endif() @@ -66,15 +60,12 @@ if(NOT DEFINED HDF5_PREFER_PARALLEL) endif() endif() -find_package(HDF5 COMPONENTS HL) -if(NOT HDF5_FOUND) - message(FATAL_ERROR "Could not find HDF5") -endif() +find_package(HDF5 REQUIRED COMPONENTS C HL) if(HDF5_IS_PARALLEL) if(NOT MPI_ENABLED) message(FATAL_ERROR "Parallel HDF5 must be used with MPI.") endif() - message("-- Using parallel HDF5") + message(STATUS "Using parallel HDF5") endif() #=============================================================================== @@ -98,7 +89,7 @@ if(debug) list(APPEND cxxflags -g -O0) endif() if(profile) - list(APPEND cxxflags -pg) + list(APPEND cxxflags -g -fno-omit-frame-pointer) endif() if(optimize) list(REMOVE_ITEM cxxflags -O2) @@ -106,8 +97,8 @@ if(optimize) endif() # Show flags being used -message(STATUS "C++ flags: ${cxxflags}") -message(STATUS "Linker flags: ${ldflags}") +message(STATUS "OpenMC C++ flags: ${cxxflags}") +message(STATUS "OpenMC Linker flags: ${ldflags}") #=============================================================================== # pugixml library @@ -149,7 +140,7 @@ set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # the RPATH to be used when installing, but only if it's not a system directory list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) if("${isSystemDir}" STREQUAL "-1") - set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") endif() #=============================================================================== @@ -255,13 +246,11 @@ set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) target_include_directories(libopenmc - PUBLIC include - PRIVATE ${HDF5_INCLUDE_DIRS}) + PUBLIC include ${HDF5_INCLUDE_DIRS}) # Set compile flags target_compile_options(libopenmc PRIVATE ${cxxflags}) -target_compile_definitions(libopenmc PRIVATE -DMAX_COORD=${maxcoord}) if (HDF5_IS_PARALLEL) target_compile_definitions(libopenmc PRIVATE -DPHDF5) endif() @@ -281,8 +270,8 @@ endif() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. -target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml - faddeeva xtensor) +target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} + pugixml faddeeva xtensor) if(dagmc) target_compile_definitions(libopenmc PRIVATE DAGMC) diff --git a/cmake/Modules/FindHDF5.cmake b/cmake/Modules/FindHDF5.cmake deleted file mode 100644 index 7492ea750..000000000 --- a/cmake/Modules/FindHDF5.cmake +++ /dev/null @@ -1,399 +0,0 @@ -#.rst: -# FindHDF5 -# -------- -# -# Find HDF5, a library for reading and writing self describing array data. -# -# -# -# This module invokes the HDF5 wrapper compiler that should be installed -# alongside HDF5. Depending upon the HDF5 Configuration, the wrapper -# compiler is called either h5cc or h5pcc. If this succeeds, the module -# will then call the compiler with the -show argument to see what flags -# are used when compiling an HDF5 client application. -# -# The module will optionally accept the COMPONENTS argument. If no -# COMPONENTS are specified, then the find module will default to finding -# only the HDF5 C library. If one or more COMPONENTS are specified, the -# module will attempt to find the language bindings for the specified -# components. The only valid components are C, CXX, Fortran, HL, and -# Fortran_HL. If the COMPONENTS argument is not given, the module will -# attempt to find only the C bindings. -# -# On UNIX systems, this module will read the variable -# HDF5_USE_STATIC_LIBRARIES to determine whether or not to prefer a -# static link to a dynamic link for HDF5 and all of it's dependencies. -# To use this feature, make sure that the HDF5_USE_STATIC_LIBRARIES -# variable is set before the call to find_package. -# -# To provide the module with a hint about where to find your HDF5 -# installation, you can set the environment variable HDF5_ROOT. The -# Find module will then look in this path when searching for HDF5 -# executables, paths, and libraries. -# -# In addition to finding the includes and libraries required to compile -# an HDF5 client application, this module also makes an effort to find -# tools that come with the HDF5 distribution that may be useful for -# regression testing. -# -# This module will define the following variables: -# -# :: -# -# HDF5_INCLUDE_DIRS - Location of the hdf5 includes -# HDF5_INCLUDE_DIR - Location of the hdf5 includes (deprecated) -# HDF5_DEFINITIONS - Required compiler definitions for HDF5 -# HDF5_C_LIBRARIES - Required libraries for the HDF5 C bindings. -# HDF5_CXX_LIBRARIES - Required libraries for the HDF5 C++ bindings -# HDF5_Fortran_LIBRARIES - Required libraries for the HDF5 Fortran bindings -# HDF5_HL_LIBRARIES - Required libraries for the HDF5 high level API -# HDF5_Fortran_HL_LIBRARIES - Required libraries for the high level Fortran -# bindings. -# HDF5_LIBRARIES - Required libraries for all requested bindings -# HDF5_FOUND - true if HDF5 was found on the system -# HDF5_VERSION - HDF5 version in format Major.Minor.Release -# HDF5_LIBRARY_DIRS - the full set of library directories -# HDF5_IS_PARALLEL - Whether or not HDF5 was found with parallel IO support -# HDF5_C_COMPILER_EXECUTABLE - the path to the HDF5 C wrapper compiler -# HDF5_CXX_COMPILER_EXECUTABLE - the path to the HDF5 C++ wrapper compiler -# HDF5_Fortran_COMPILER_EXECUTABLE - the path to the HDF5 Fortran wrapper compiler -# HDF5_DIFF_EXECUTABLE - the path to the HDF5 dataset comparison tool - -#============================================================================= -# Copyright 2015 Axel Huebl, Helmholtz-Zentrum Dresden - Rossendorf -# Copyright 2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -# This module is maintained by Will Dicharry . - -include(SelectLibraryConfigurations) -include(FindPackageHandleStandardArgs) - -# List of the valid HDF5 components -set( HDF5_VALID_COMPONENTS - C - CXX - Fortran - HL - Fortran_HL -) - -# Validate the list of find components. -if( NOT HDF5_FIND_COMPONENTS ) - set( HDF5_LANGUAGE_BINDINGS "C" ) -else() - # add the extra specified components, ensuring that they are valid. - foreach( component ${HDF5_FIND_COMPONENTS} ) - list( FIND HDF5_VALID_COMPONENTS ${component} component_location ) - if( ${component_location} EQUAL -1 ) - message( FATAL_ERROR - "\"${component}\" is not a valid HDF5 component." ) - else() - list( APPEND HDF5_LANGUAGE_BINDINGS ${component} ) - endif() - endforeach() -endif() - -# Determine whether to search for serial or parallel executable first -if(HDF5_PREFER_PARALLEL) - set(HDF5_C_COMPILER_NAMES h5pcc h5cc) - set(HDF5_CXX_COMPILER_NAMES h5pc++ h5c++) - set(HDF5_Fortran_COMPILER_NAMES h5pfc h5fc) -else() - set(HDF5_C_COMPILER_NAMES h5cc h5pcc) - set(HDF5_CXX_COMPILER_NAMES h5c++ h5pc++) - set(HDF5_Fortran_COMPILER_NAMES h5fc h5pfc) -endif() - -# try to find the HDF5 wrapper compilers -find_program( HDF5_C_COMPILER_EXECUTABLE - NAMES ${HDF5_C_COMPILER_NAMES} - HINTS ENV HDF5_ROOT - PATH_SUFFIXES bin Bin - DOC "HDF5 Wrapper compiler. Used only to detect HDF5 compile flags." ) -mark_as_advanced( HDF5_C_COMPILER_EXECUTABLE ) - -find_program( HDF5_CXX_COMPILER_EXECUTABLE - NAMES ${HDF5_CXX_COMPILER_NAMES} - HINTS ENV HDF5_ROOT - PATH_SUFFIXES bin Bin - DOC "HDF5 C++ Wrapper compiler. Used only to detect HDF5 compile flags." ) -mark_as_advanced( HDF5_CXX_COMPILER_EXECUTABLE ) - -find_program( HDF5_Fortran_COMPILER_EXECUTABLE - NAMES ${HDF5_Fortran_COMPILER_NAMES} - HINTS ENV HDF5_ROOT - PATH_SUFFIXES bin Bin - DOC "HDF5 Fortran Wrapper compiler. Used only to detect HDF5 compile flags." ) -mark_as_advanced( HDF5_Fortran_COMPILER_EXECUTABLE ) - -unset(HDF5_C_COMPILER_NAMES) -unset(HDF5_CXX_COMPILER_NAMES) -unset(HDF5_Fortran_COMPILER_NAMES) - -find_program( HDF5_DIFF_EXECUTABLE - NAMES h5diff - HINTS ENV HDF5_ROOT - PATH_SUFFIXES bin Bin - DOC "HDF5 file differencing tool." ) -mark_as_advanced( HDF5_DIFF_EXECUTABLE ) - -# Invoke the HDF5 wrapper compiler. The compiler return value is stored to the -# return_value argument, the text output is stored to the output variable. -macro( _HDF5_invoke_compiler language output return_value ) - if( HDF5_${language}_COMPILER_EXECUTABLE ) - exec_program( ${HDF5_${language}_COMPILER_EXECUTABLE} - ARGS -show - OUTPUT_VARIABLE ${output} - RETURN_VALUE ${return_value} - ) - if( ${${return_value}} EQUAL 0 ) - # do nothing - else() - message( STATUS - "Unable to determine HDF5 ${language} flags from HDF5 wrapper." ) - endif() - endif() -endmacro() - -# Parse a compile line for definitions, includes, library paths, and libraries. -macro( _HDF5_parse_compile_line - compile_line_var - include_paths - definitions - library_paths - libraries ) - - # Match the include paths - string( REGEX MATCHALL "-I([^\" ]+)" include_path_flags - "${${compile_line_var}}" - ) - foreach( IPATH ${include_path_flags} ) - string( REGEX REPLACE "^-I" "" IPATH ${IPATH} ) - string( REPLACE "//" "/" IPATH ${IPATH} ) - list( APPEND ${include_paths} ${IPATH} ) - endforeach() - - # Match the definitions - string( REGEX MATCHALL "-D[^ ]*" definition_flags "${${compile_line_var}}" ) - foreach( DEF ${definition_flags} ) - list( APPEND ${definitions} ${DEF} ) - endforeach() - - # Match the library paths - string( REGEX MATCHALL "-L([^\" ]+|\"[^\"]+\")" library_path_flags - "${${compile_line_var}}" - ) - - foreach( LPATH ${library_path_flags} ) - string( REGEX REPLACE "^-L" "" LPATH ${LPATH} ) - string( REPLACE "//" "/" LPATH ${LPATH} ) - list( APPEND ${library_paths} ${LPATH} ) - endforeach() - - # now search for the library names specified in the compile line (match -l...) - # match only -l's preceded by a space or comma - # this is to exclude directory names like xxx-linux/ - string( REGEX MATCHALL "[, ]-l([^\", ]+)" library_name_flags - "${${compile_line_var}}" ) - # strip the -l from all of the library flags and add to the search list - foreach( LIB ${library_name_flags} ) - string( REGEX REPLACE "^[, ]-l" "" LIB ${LIB} ) - list( APPEND ${libraries} ${LIB} ) - endforeach() -endmacro() - -# Try to find HDF5 using an installed hdf5-config.cmake -if( NOT HDF5_FOUND ) - find_package( HDF5 QUIET NO_MODULE ) - if( HDF5_FOUND ) - set( HDF5_INCLUDE_DIRS ${HDF5_INCLUDE_DIR} ) - set( HDF5_LIBRARIES ) - set( HDF5_C_TARGET hdf5 ) - set( HDF5_CXX_TARGET hdf5_cpp ) - set( HDF5_HL_TARGET hdf5_hl ) - set( HDF5_Fortran_TARGET hdf5_fortran ) - set( HDF5_Fortran_HL_TARGET hdf5_hl_fortran ) - foreach( _component ${HDF5_LANGUAGE_BINDINGS} ) - list( FIND HDF5_VALID_COMPONENTS ${_component} _component_location ) - get_target_property( _comp_location ${HDF5_${_component}_TARGET} LOCATION ) - if( _comp_location ) - set( HDF5_${_component}_LIBRARY ${_comp_location} CACHE PATH - "HDF5 ${_component} library" ) - mark_as_advanced( HDF5_${_component}_LIBRARY ) - list( APPEND HDF5_LIBRARIES ${HDF5_${_component}_LIBRARY} ) - endif() - endforeach() - endif() -endif() - -if( NOT HDF5_FOUND ) - _HDF5_invoke_compiler( C HDF5_C_COMPILE_LINE HDF5_C_RETURN_VALUE ) - _HDF5_invoke_compiler( CXX HDF5_CXX_COMPILE_LINE HDF5_CXX_RETURN_VALUE ) - _HDF5_invoke_compiler( Fortran HDF5_Fortran_COMPILE_LINE HDF5_Fortran_RETURN_VALUE ) - set(HDF5_HL_COMPILE_LINE ${HDF5_C_COMPILE_LINE}) - set(HDF5_Fortran_HL_COMPILE_LINE ${HDF5_Fortran_COMPILE_LINE}) - - # seed the initial lists of libraries to find with items we know we need - set( HDF5_C_LIBRARY_NAMES_INIT hdf5 ) - set( HDF5_HL_LIBRARY_NAMES_INIT hdf5_hl ${HDF5_C_LIBRARY_NAMES_INIT} ) - set( HDF5_CXX_LIBRARY_NAMES_INIT hdf5_cpp ${HDF5_C_LIBRARY_NAMES_INIT} ) - set( HDF5_Fortran_LIBRARY_NAMES_INIT hdf5_fortran - ${HDF5_C_LIBRARY_NAMES_INIT} ) - set( HDF5_Fortran_HL_LIBRARY_NAMES_INIT hdf5hl_fortran hdf5_hl - ${HDF5_Fortran_LIBRARY_NAMES_INIT} ) - - foreach( LANGUAGE ${HDF5_LANGUAGE_BINDINGS} ) - if( HDF5_${LANGUAGE}_COMPILE_LINE ) - _HDF5_parse_compile_line( HDF5_${LANGUAGE}_COMPILE_LINE - HDF5_${LANGUAGE}_INCLUDE_FLAGS - HDF5_${LANGUAGE}_DEFINITIONS - HDF5_${LANGUAGE}_LIBRARY_DIRS - HDF5_${LANGUAGE}_LIBRARY_NAMES - ) - - # take a guess that the includes may be in the 'include' sibling - # directory of a library directory. - foreach( dir ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ) - list( APPEND HDF5_${LANGUAGE}_INCLUDE_FLAGS ${dir}/../include ) - endforeach() - endif() - - # set the definitions for the language bindings. - list( APPEND HDF5_DEFINITIONS ${HDF5_${LANGUAGE}_DEFINITIONS} ) - - # find the HDF5 include directories - if(${LANGUAGE} MATCHES "Fortran") - set(HDF5_INCLUDE_FILENAME hdf5.mod) - else() - set(HDF5_INCLUDE_FILENAME hdf5.h) - endif() - - find_path( HDF5_${LANGUAGE}_INCLUDE_DIR ${HDF5_INCLUDE_FILENAME} - HINTS - ${HDF5_${LANGUAGE}_INCLUDE_FLAGS} - ENV - HDF5_ROOT - PATHS - $ENV{HOME}/.local/include - PATH_SUFFIXES - include - Include - ) - mark_as_advanced( HDF5_${LANGUAGE}_INCLUDE_DIR ) - list( APPEND HDF5_INCLUDE_DIRS ${HDF5_${LANGUAGE}_INCLUDE_DIR} ) - - # find the HDF5 libraries - foreach( LIB ${HDF5_${LANGUAGE}_LIBRARY_NAMES_INIT} ) - if( UNIX AND HDF5_USE_STATIC_LIBRARIES ) - # According to bug 1643 on the CMake bug tracker, this is the - # preferred method for searching for a static library. - # See http://www.cmake.org/Bug/view.php?id=1643. We search - # first for the full static library name, but fall back to a - # generic search on the name if the static search fails. - set( THIS_LIBRARY_SEARCH_DEBUG lib${LIB}d.a ${LIB}d ) - set( THIS_LIBRARY_SEARCH_RELEASE lib${LIB}.a ${LIB} ) - else() - set( THIS_LIBRARY_SEARCH_DEBUG ${LIB}d ) - set( THIS_LIBRARY_SEARCH_RELEASE ${LIB} ) - endif() - find_library( HDF5_${LIB}_LIBRARY_DEBUG - NAMES ${THIS_LIBRARY_SEARCH_DEBUG} - HINTS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} - ENV HDF5_ROOT - PATH_SUFFIXES lib Lib ) - find_library( HDF5_${LIB}_LIBRARY_RELEASE - NAMES ${THIS_LIBRARY_SEARCH_RELEASE} - HINTS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} - ENV HDF5_ROOT - PATH_SUFFIXES lib Lib ) - select_library_configurations( HDF5_${LIB} ) - list(APPEND HDF5_${LANGUAGE}_LIBRARIES ${HDF5_${LIB}_LIBRARY}) - endforeach() - list( APPEND HDF5_LIBRARY_DIRS ${HDF5_${LANGUAGE}_LIBRARY_DIRS} ) - - # When the wrapper lists a library with -l, e.g. -lz, simply use it as - # is. If find_library is called for these libraries, you end up with - # local libraries that will not be suitable when cross-compiling for the - # Intel Xeon Phi. - foreach(LIBNAME ${HDF5_${LANGUAGE}_LIBRARY_NAMES}) - list(APPEND HDF5_${LANGUAGE}_LIBRARIES "-l${LIBNAME}") - endforeach() - - # Append the libraries for this language binding to the list of all - # required libraries. - list(APPEND HDF5_LIBRARIES ${HDF5_${LANGUAGE}_LIBRARIES}) - endforeach() - - # We may have picked up some duplicates in various lists during the above - # process for the language bindings (both the C and C++ bindings depend on - # libz for example). Remove the duplicates. It appears that the default - # CMake behavior is to remove duplicates from the end of a list. However, - # for link lines, this is incorrect since unresolved symbols are searched - # for down the link line. Therefore, we reverse the list, remove the - # duplicates, and then reverse it again to get the duplicates removed from - # the beginning. - macro( _remove_duplicates_from_beginning _list_name ) - list( REVERSE ${_list_name} ) - list( REMOVE_DUPLICATES ${_list_name} ) - list( REVERSE ${_list_name} ) - endmacro() - - if( HDF5_INCLUDE_DIRS ) - _remove_duplicates_from_beginning( HDF5_INCLUDE_DIRS ) - endif() - if( HDF5_LIBRARY_DIRS ) - _remove_duplicates_from_beginning( HDF5_LIBRARY_DIRS ) - endif() - - # If the HDF5 include directory was found, open H5pubconf.h to determine if - # HDF5 was compiled with parallel IO support - set( HDF5_IS_PARALLEL FALSE ) - set( HDF5_VERSION "" ) - foreach( _dir IN LISTS HDF5_INCLUDE_DIRS ) - foreach(_hdr "${_dir}/H5pubconf.h" "${_dir}/H5pubconf-64.h" "${_dir}/H5pubconf-32.h") - if( EXISTS "${_hdr}" ) - file( STRINGS "${_hdr}" - HDF5_HAVE_PARALLEL_DEFINE - REGEX "HAVE_PARALLEL 1" ) - if( HDF5_HAVE_PARALLEL_DEFINE ) - set( HDF5_IS_PARALLEL TRUE ) - endif() - unset(HDF5_HAVE_PARALLEL_DEFINE) - - file( STRINGS "${_hdr}" - HDF5_VERSION_DEFINE - REGEX "^[ \t]*#[ \t]*define[ \t]+H5_VERSION[ \t]+" ) - if( "${HDF5_VERSION_DEFINE}" MATCHES - "H5_VERSION[ \t]+\"([0-9]+\\.[0-9]+\\.[0-9]+).*\"" ) - set( HDF5_VERSION "${CMAKE_MATCH_1}" ) - endif() - unset(HDF5_VERSION_DEFINE) - endif() - endforeach() - endforeach() - set( HDF5_IS_PARALLEL ${HDF5_IS_PARALLEL} CACHE BOOL - "HDF5 library compiled with parallel IO support" ) - mark_as_advanced( HDF5_IS_PARALLEL ) - - # For backwards compatibility we set HDF5_INCLUDE_DIR to the value of - # HDF5_INCLUDE_DIRS - if( HDF5_INCLUDE_DIRS ) - set( HDF5_INCLUDE_DIR "${HDF5_INCLUDE_DIRS}" ) - endif() - -endif() - -find_package_handle_standard_args( HDF5 - REQUIRED_VARS HDF5_LIBRARIES HDF5_INCLUDE_DIRS - VERSION_VAR HDF5_VERSION -) diff --git a/docs/source/conf.py b/docs/source/conf.py index be7e3a8d1..22446213a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -53,9 +53,10 @@ extensions = ['sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', - 'sphinx.ext.imgconverter', 'sphinx_numfig', 'notebook_sphinxext'] +if not on_rtd: + extensions.append('sphinx.ext.imgconverter') # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index 0f7669a24..cbf665cc8 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -12,6 +12,11 @@ adding new code in OpenMC. C++ --- +Indentation +----------- + +Use two spaces per indentation level. + Miscellaneous ------------- @@ -126,6 +131,15 @@ single declaration to avoid confusion: Curly braces ------------ +For a class declaration, the opening brace should be on the same line that +lists the name of the class. + +.. code-block:: C++ + + class Matrix { + ... + }; + For a function definition, the opening and closing braces should each be on their own lines. This helps distinguish function code from the argument list. If the entire function fits on one or two lines, then the braces can be on the @@ -210,11 +224,18 @@ Use of third-party Python packages should be limited to numpy_, scipy_, matplotlib_, pandas_, and h5py_. Use of other third-party packages must be implemented as optional dependencies rather than required dependencies. +Prefer pathlib_ when working with filesystem paths over functions in the os_ +module or other standard-library modules. Functions that accept arguments that +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://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt +.. _numpydoc: https://numpydoc.readthedocs.io/en/latest/format.html .. _numpy: http://www.numpy.org/ .. _scipy: https://www.scipy.org/ .. _matplotlib: https://matplotlib.org/ .. _pandas: https://pandas.pydata.org/ -.. _h5py: http://www.h5py.org/ +.. _h5py: https://www.h5py.org/ +.. _pathlib: https://docs.python.org/3/library/pathlib.html +.. _os: https://docs.python.org/3/library/os.html +.. _Path: https://docs.python.org/3/library/pathlib.html#pathlib.Path diff --git a/docs/source/io_formats/nuclear_data.rst b/docs/source/io_formats/nuclear_data.rst index 5595f2061..81e4f4bcd 100644 --- a/docs/source/io_formats/nuclear_data.rst +++ b/docs/source/io_formats/nuclear_data.rst @@ -133,11 +133,17 @@ Incident Photon Data **//bremsstrahlung/** +:Attributes: - **I** (*double*) -- Mean excitation energy in [eV] + :Datasets: - **electron_energy** (*double[]*) -- Incident electron energy in [eV] - **photon_energy** (*double[]*) -- Outgoing photon energy as fraction of incident electron energy - **dcs** (*double[][]*) -- Bremsstrahlung differential cross section at each incident energy in [mb/eV] + - **ionization_energy** (*double[]*) -- Ionization potential of each + subshell in [eV] + - **num_electrons** (*int[]*) -- Number of electrons per subshell, + with conduction electrons indicated by a negative value **//coherent/** @@ -176,13 +182,6 @@ Incident Photon Data :Datasets: - **xs** (*double[]*) -- Total photoionization cross section in [b] -**//stopping_powers/** - -:Datasets: - **I** (*double*) -- Mean excitation energy in [eV] - - **energy** (*double[]*) -- Energies in [eV] - - **s_collision** (*double[]*) -- Collision stopping power in [eV-cm\ :sup:`2`\ /g] - - **s_radiative** (*double[]*) -- Radiative stopping power in [eV-cm\ :sup:`2`\ /g] - **//subshells/** :Attributes: - **designators** (*char[][]*) -- Designator for each shell, e.g. 'M2' diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index e5c0ca160..9c68a9730 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -730,15 +730,6 @@ sections. *Default*: 10 K ---------------------- -```` Element ---------------------- - -The ```` element indicates the number of OpenMP threads to be used for -a simulation. It has no attributes and accepts a positive integer value. - - *Default*: None (Determined by environment variable :envvar:`OMP_NUM_THREADS`) - .. _trace: ------------------- diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index 028a71de7..2d9a62ef3 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -5,7 +5,7 @@ Photon Physics ============== Photons, being neutral particles, behave much in the same manner as neutrons, -traveling in straight lines and experiencing occasional collisions which change +traveling in straight lines and experiencing occasional collisions that change their energy and direction. Photons undergo four basic interactions as they pass through matter: coherent (Rayleigh) scattering, incoherent (Compton) scattering, photoelectric effect, and pair/triplet production. Photons with energy in the @@ -728,10 +728,18 @@ the cross section differential in energy loss. The total stopping power power :math:`S_{\text{rad}}(T)`, which refers to energy loss due to bremsstrahlung, and the collision stopping power :math:`S_{\text{col}}(T)`, which refers to the energy loss due to inelastic collisions with bound -electrons in the material that result in ionization and excitation. To obtain -the radiative stopping power for positrons, the radiative stopping power for -electrons is multiplied by :eq:`positron-factor`. Currently, the collision -stopping power for electrons is also used for positrons. +electrons in the material that result in ionization and excitation. The +radiative stopping power for electrons is given by + +.. math:: + :label: radiative-stopping-power + + S_{\text{rad}}(T) = n \frac{Z^2}{\beta^2} T \int_0^1 \chi(Z,T,\kappa) + d\kappa. + + +To obtain the radiative stopping power for positrons, +:eq:`radiative-stopping-power` is multiplied by :eq:`positron-factor`. While the models for photon interactions with matter described above can safely assume interactions occur with free atoms, sampling the target atom based on @@ -754,14 +762,97 @@ power is calculated using Bragg's additivity rule as S_{\text{rad}}(T) = \sum_i w_i S_{\text{rad},i}(T), -where :math:`w_i` is the mass fraction of the :math:`i`-th element. The -collision stopping power, however, is a function of certain quantities such as -the mean excitation energy :math:`I` and the density effect correction -:math:`\delta_F` that depend on molecular properties. These quantities cannot -simply be summed over constituent elements in a compound, but should instead be -calculated for the material. Currently, we use Bragg's additivity rule to -calculate the collision stopping power as well, but this is not a good -approximation and should be fixed in the future. +where :math:`w_i` is the mass fraction of the :math:`i`-th element and +:math:`S_{\text{rad},i}(T)` is found for element :math:`i` using +:eq:`radiative-stopping-power`. The collision stopping power, however, is a +function of certain quantities such as the mean excitation energy :math:`I` and +the density effect correction :math:`\delta_F` that depend on molecular +properties. These quantities cannot simply be summed over constituent elements +in a compound, but should instead be calculated for the material. The Bethe +formula can be used to find the collision stopping power of the material: + +.. math:: + :label: material-collision-stopping-power + + S_{\text{col}}(T) = \frac{2 \pi r_e^2 m_e c^2}{\beta^2} N_A \frac{Z}{A_M} + [\ln(T^2/I^2) + \ln(1 + \tau/2) + F(\tau) - \delta_F(T)], + +where :math:`N_A` is Avogadro's number, :math:`A_M` is the molar mass, +:math:`\tau = T/m_e`, and :math:`F(\tau)` depends on the particle type. For +electrons, + +.. math:: + :label: F-electron + + F_{-}(\tau) = (1 - \beta^2)[1 + \tau^2/8 - (2\tau + 1) \ln2], + +while for positrons + +.. math:: + :label: F-positron + + F_{+}(\tau) = 2\ln2 - (\beta^2/12)[23 + 14/(\tau + 2) + 10/(\tau + 2)^2 + + 4/(\tau + 2)^3]. + +The density effect correction :math:`\delta_F` takes into account the reduction +of the collision stopping power due to the polarization of the material the +charged particle is passing through by the electric field of the particle. +It can be evaluated using the method described by Sternheimer_, where the +equation for :math:`\delta_F` is + +.. math:: + :label: density-effect-correction + + \delta_F(\beta) = \sum_{i=1}^n f_i \ln[(l_i^2 + l^2)/l_i^2] - + l^2(1-\beta^2). + +Here, :math:`f_i` is the oscillator strength of the :math:`i`-th transition, +given by :math:`f_i = n_i/Z`, where :math:`n_i` is the number of electrons in +the :math:`i`-th subshell. The frequency :math:`l` is the solution of the +equation + +.. math:: + :label: density-effect-l + + \frac{1}{\beta^2} - 1 = \sum_{i=1}^{n} \frac{f_i}{\bar{\nu}_i^2 + l^2}, + +where :math:`\bar{v}_i` is defined as + +.. math:: + :label: density-effect-nubar + + \bar{\nu}_i = h\nu_i \rho / h\nu_p. + +The plasma energy :math:`h\nu_p` of the medium is given by + +.. math:: + :label: plasma-frequency + + h\nu_p = \sqrt{\frac{(hc)^2 r_e \rho_m N_A Z}{\pi A}}, + +where :math:`A` is the atomic weight and :math:`\rho_m` is the density of the +material. In :eq:`density-effect-nubar`, :math:`h\nu_i` is the oscillator +energy, and :math:`\rho` is an adjustment factor introduced to give agreement +between the experimental values of the oscillator energies and the mean +excitation energy. The :math:`l_i` in :eq:`density-effect-correction` are +defined as + +.. math:: + :label: density-effect-li + + l_i &= (\bar{\nu}_i^2 + 2/3f_i)^{1/2} ~~~~&\text{for}~~ \bar{\nu}_i > 0 \\ + l_n &= f_n^{1/2} ~~~~&\text{for}~~ \bar{\nu}_n = 0, + +where the second case applies to conduction electrons. For a conductor, +:math:`f_n` is given by :math:`n_c/Z`, where :math:`n_c` is the effective +number of conduction electrons, and :math:`v_n = 0`. The adjustment factor +:math:`\rho` is determined using the equation for the mean excitation energy: + +.. math:: + :label: mean-excitation-energy + + \ln I = \sum_{i=1}^{n-1} f_i \ln[(h\nu_i\rho)^2 + 2/3f_i(h\nu_p)^2]^{1/2} + + f_n \ln (h\nu_pf_n^{1/2}). .. _ttb: @@ -891,6 +982,55 @@ direction of the incident charged particle, which is a reasonable approximation at higher energies when the bremsstrahlung radiation is emitted at small angles. +----------------- +Photon Production +----------------- + +In coupled neutron-photon transport, a source neutron is tracked, and photons +produced from neutron reactions are transported after the neutron's history has +terminated. Since these secondary photons form the photon source for the +problem, it is important to correctly describe their energy and angular +distributions as the accuracy of the calculation relies on the accuracy of this +source. The photon production cross section for a particular reaction :math:`i` +and incident neutron energy :math:`E` is defined as + +.. math:: + :label: photon-production-xs + + \sigma_{\gamma, i}(E) = y_i(E)\sigma_i(E), + +where :math:`y_i(E)` is the photon yield corresponding to an incident neutron +reaction having cross section :math:`\sigma_i(E)`. + +The yield of photons during neutron transport is determined as the sum of the +photon yields from each individual reaction. In OpenMC, production of photons +is treated in an average sense. That is, the total photon production cross +section is used at a collision site to determine how many photons to produce +rather than the photon production from the reaction that actually took place. +This is partly done for convenience but also because the use of variance +reduction techniques such as implicit capture make it difficult in practice to +directly sample photon production from individual reactions. + +In OpenMC, secondary photons are created after a nuclide has been sampled in a +neutron collision. The expected number of photons produced is + +.. math:: + :label: expected-number-photons + + n = w\frac{\sigma_{\gamma}(E)}{\sigma_T(E)}, + +where :math:`w` is the weight of the neutron, :math:`\sigma_{\gamma}` is the +photon production cross section for the sampled nuclide, and :math:`\sigma_T` +is the total cross section for the nuclide. :math:`\lfloor n \rfloor` photons +are created with an additional photon produced with probability :math:`n - +\lfloor n \rfloor`. Next, a reaction is sampled for each secondary photon. The +probability of sampling the :math:`i`-th reaction is given by +:math:`\sigma_{\gamma, i}(E)/\sum_j\sigma_{\gamma, j}(E)`, where +:math:`\sum_j\sigma_{\gamma, j} = \sigma_{\gamma}` is the total photon +production cross section. The secondary angle and energy distributions +associated with the reaction are used to sample the angle and energy of the +emitted photon. + .. _Koblinger: https://doi.org/10.13182/NSE75-A26663 .. _anomalous scattering: http://pd.chem.ucl.ac.uk/pdnn/diff1/anomscat.htm @@ -906,3 +1046,5 @@ angles. .. _Kaltiaisenaho: https://aaltodoc.aalto.fi/bitstream/handle/123456789/21004/master_Kaltiaisenaho_Toni_2016.pdf .. _Salvat: http://www.oecd-nea.org/globalsearch/download.php?doc=77434 + +.. _Sternheimer: https://doi.org/10.1103/PhysRevB.26.6067 diff --git a/docs/source/publications.rst b/docs/source/publications.rst index 990a8fc21..e1c0f6de0 100644 --- a/docs/source/publications.rst +++ b/docs/source/publications.rst @@ -59,7 +59,7 @@ Coupling and Multi-physics - Ze-Long Zhao, Yongwei Yang, and Shuang Hong, "`Application of FLUKA and OpenMC in coupled physics calculation of target and subcritical reactor for ADS - `_," *Nucl. Sci. Tech.*, **30** + `_," *Nucl. Sci. Tech.*, **30**: 10 (2019). - April Novak, Paul Romano, Brycen Wendt, Ron Rahaman, Elia Merzari, Leslie @@ -134,6 +134,11 @@ Geometry and Visualization Miscellaneous ------------- +- Faisal Qayyum, Muhammad R. Ali, Awais Zahur, and R. Khan, "`Improvements in + methodology to determine feedback reactivity coefficients + `_," *Nucl. Sci. Tech.*, **30**: 63 + (2019). + - Amanda L. Lund and Paul K. Romano, "`Implementation and Validation of Photon Transport in OpenMC `_", Argonne National Laboratory, Technical Report ANL/MCS-TM-381 (2018). @@ -200,11 +205,21 @@ Miscellaneous Multigroup Cross Section Generation ----------------------------------- +- William Boyd, Adam Nelson, Paul K. Romano, Samuel Shaner, Benoit Forget, and + Kord Smith, "`Multigroup Cross-Section Generation with the OpenMC Monte Carlo + Particle Transport Code `_," + *Nucl. Technol.* (2019). + - William Boyd, Benoit Forget, and Kord Smith, "`A single-step framework to generate spatially self-shielded multi-group cross sections from Monte Carlo transport simulations `_," *Ann. Nucl. Energy*, **125**, 261-271 (2019). +- Kun Zhuang, Xiaobin Tang, and Liangzhi Cao, "`Development and verification of + a model for generation of MSFR few-group homogenized cross-sections based on a + Monte Carlo code OpenMC `_," + *Ann. Nucl. Energy*, **124**, 187-197 (2019). + - Changho Lee and Yeon Sang Jung, "Verification of the Cross Section Library Generated Using OpenMC and MC\ :sup:`2`-3 for PROTEUS," *Proc. PHYSOR*, Cancun, Mexico, Apr. 22-26 (2018). @@ -454,6 +469,11 @@ Parallelism Depletion --------- +- 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 + for ADS `_," *Nucl. Sci. Tech.*, + **30**: 44 (2019). + - Colin Josey, Benoit Forget, and Kord Smith, "`High order methods for the integration of the Bateman equations and other problems of the form of y' = F(y,t)y `_," *J. Comput. Phys.*, diff --git a/docs/source/pythonapi/model.rst b/docs/source/pythonapi/model.rst index 90e8b779d..14f4a4beb 100644 --- a/docs/source/pythonapi/model.rst +++ b/docs/source/pythonapi/model.rst @@ -11,6 +11,7 @@ Convenience Functions :template: myfunction.rst openmc.model.borated_water + openmc.model.cylinder_from_points openmc.model.get_hexagonal_prism openmc.model.get_rectangular_prism openmc.model.subdivide diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/cross_sections.rst index dc0e9092a..e6a79815f 100644 --- a/docs/source/usersguide/cross_sections.rst +++ b/docs/source/usersguide/cross_sections.rst @@ -192,11 +192,11 @@ Photon Cross Sections Photon interaction data is needed to run OpenMC with photon transport enabled. Some of this data, namely bremsstrahlung cross sections from `Seltzer and -Berger`_, stopping powers from the `NIST ESTAR database`_, and Compton profiles -calculated by `Biggs et al.`_ and available in the Geant4 G4EMLOW data file, is -distributed with OpenMC. The rest is available from the NNDC_, which provides -ENDF data from the photo-atomic and atomic relaxation sublibraries of the -ENDF/B-VII.1 library. +Berger`_, mean excitation energy from the `NIST ESTAR database`_, and Compton +profiles calculated by `Biggs et al.`_ and available in the Geant4 G4EMLOW data +file, is distributed with OpenMC. The rest is available from the NNDC_, which +provides ENDF data from the photo-atomic and atomic relaxation sublibraries of +the ENDF/B-VII.1 library. Most of the pregenerated HDF5 libraries available at https://openmc.mcs.anl.gov already have photon interaction data included. If you are building a data diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index 70a665c53..80e60bd34 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -88,7 +88,7 @@ parameters for a sphere are the :math:`x,y,z` coordinates of the center of the sphere and the radius of the sphere. All of these parameters can be set either as optional keyword arguments to the class constructor or via attributes:: - sphere = openmc.Sphere(R=10.0) + sphere = openmc.Sphere(r=10.0) # This is equivalent sphere = openmc.Sphere() @@ -98,7 +98,7 @@ Once a surface has been created, half-spaces can be obtained by applying the unary ``-`` or ``+`` operators, corresponding to the negative and positive half-spaces, respectively. For example:: - >>> sphere = openmc.Sphere(R=10.0) + >>> sphere = openmc.Sphere(r=10.0) >>> inside_sphere = -sphere >>> outside_sphere = +sphere >>> type(inside_sphere) @@ -140,10 +140,10 @@ may want to specify different behavior for particles passing through a surface. To specify a vacuum boundary condition, simply change the :attr:`Surface.boundary_type` attribute to 'vacuum':: - outer_surface = openmc.Sphere(R=100.0, boundary_type='vacuum') + outer_surface = openmc.Sphere(r=100.0, boundary_type='vacuum') # This is equivalent - outer_surface = openmc.Sphere(R=100.0) + outer_surface = openmc.Sphere(r=100.0) outer_surface.boundary_type = 'vacuum' Reflective and periodic boundary conditions can be set with the strings @@ -154,8 +154,8 @@ can be determined automatically. For non-axis-aligned planes, it is necessary to specify pairs explicitly using the :attr:`Surface.periodic_surface` attribute as in the following example:: - p1 = openmc.Plane(A=0.3, B=5.0, D=1.0, boundary_type='periodic') - p2 = openmc.Plane(A=0.3, B=5.0, D=-1.0, boundary_type='periodic') + p1 = openmc.Plane(a=0.3, b=5.0, d=1.0, boundary_type='periodic') + p2 = openmc.Plane(a=0.3, b=5.0, d=-1.0, boundary_type='periodic') p1.periodic_surface = p2 Rotationally-periodic boundary conditions can be specified for a pair of diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index d44ee9594..96b4f5024 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -243,9 +243,6 @@ coverage Compile and link code instrumented for coverage analysis. This is typically used in conjunction with gcov_. -maxcoord - Maximum number of nested coordinate levels in geometry. Defaults to 10. - To set any of these options (e.g. turning on debug mode), the following form should be used: diff --git a/docs/source/usersguide/tallies.rst b/docs/source/usersguide/tallies.rst index 1210bc282..2ced8acf0 100644 --- a/docs/source/usersguide/tallies.rst +++ b/docs/source/usersguide/tallies.rst @@ -220,6 +220,16 @@ The following tables show all valid scores: | |multiplicity from (n,2n), (n,3n), and (n,4n) | | |reactions. | +----------------------+---------------------------------------------------+ + |H1-production |Total production of H1. | + +----------------------+---------------------------------------------------+ + |H2-production |Total production of H2 (deuterium). | + +----------------------+---------------------------------------------------+ + |H3-production |Total production of H3 (tritium). | + +----------------------+---------------------------------------------------+ + |He3-production |Total production of He3. | + +----------------------+---------------------------------------------------+ + |He4-production |Total production of He4 (alpha particles). | + +----------------------+---------------------------------------------------+ .. table:: **Miscellaneous scores: units are indicated for each.** @@ -246,6 +256,10 @@ The following tables show all valid scores: |inverse-velocity |The flux-weighted inverse velocity where the | | |velocity is in units of centimeters per second. | +----------------------+---------------------------------------------------+ + |heating |Total neutron heating in units of eV per source | + | |particle. This corresponds to MT=301 produced by | + | |NJOY's HEATR module. | + +----------------------+---------------------------------------------------+ |kappa-fission |The recoverable energy production rate due to | | |fission. The recoverable energy is defined as the | | |fission product kinetic energy, prompt and delayed | @@ -281,3 +295,7 @@ The following tables show all valid scores: |decay-rate |The delayed-nu-fission-weighted decay rate where | | |the decay rate is in units of inverse seconds. | +----------------------+---------------------------------------------------+ + |damage-energy |Damage energy production in units of eV per source | + | |particle. This corresponds to MT=444 produced by | + | |NJOY's HEATR module. | + +----------------------+---------------------------------------------------+ diff --git a/docs/source/usersguide/volume.rst b/docs/source/usersguide/volume.rst index 482f66fd0..e4bdbf715 100644 --- a/docs/source/usersguide/volume.rst +++ b/docs/source/usersguide/volume.rst @@ -30,7 +30,7 @@ arguments are not necessary. For example, :: - sphere = openmc.Sphere(R=10.0) + sphere = openmc.Sphere(r=10.0) cell = openm.Cell(region=-sphere) vol_calc = openmc.VolumeCalculation([cell], 1000000) diff --git a/examples/jupyter/post-processing.ipynb b/examples/jupyter/post-processing.ipynb index fd25e810d..1223f67f3 100644 --- a/examples/jupyter/post-processing.ipynb +++ b/examples/jupyter/post-processing.ipynb @@ -10,9 +10,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -40,9 +38,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# 1.6 enriched fuel\n", @@ -75,9 +71,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Materials collection\n", @@ -97,14 +91,12 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create cylinders for the fuel and clad\n", - "fuel_outer_radius = openmc.ZCylinder(x0=0.0, y0=0.0, R=0.39218)\n", - "clad_outer_radius = openmc.ZCylinder(x0=0.0, y0=0.0, R=0.45720)\n", + "fuel_outer_radius = openmc.ZCylinder(x0=0.0, y0=0.0, r=0.39218)\n", + "clad_outer_radius = openmc.ZCylinder(x0=0.0, y0=0.0, r=0.45720)\n", "\n", "# Create boundary planes to surround the geometry\n", "min_x = openmc.XPlane(x0=-0.63, boundary_type='reflective')\n", @@ -125,9 +117,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a fuel pin\n", @@ -162,9 +152,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create root Cell\n", @@ -189,9 +177,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and set root Universe\n", @@ -201,9 +187,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Export to \"geometry.xml\"\n", @@ -219,10 +203,8 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", @@ -254,10 +236,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": false - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a Plot\n", @@ -282,22 +262,9 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Run openmc in plotting mode\n", "openmc.plot_geometry(output=False)" @@ -305,14 +272,12 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": false - }, + "execution_count": null, + "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+EMBQIvI1Ad4sUAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTItMDRUMjA6NDc6\nMzUtMDY6MDBGrChsAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEyLTA0VDIwOjQ3OjM1LTA2OjAw\nN/GQ0AAAAABJRU5ErkJggg==\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEVyEhLpgJFNv8T///98iBL0AAAAAWJLR0QDEQxM8gAAAAd0SU1FB+MEAw4yF98MNh4AAAKlSURBVGje7ZrBscIwDETxwSWkn5TAgXCgBPqhBA6kyj/fDhCIJa2zZAwz0pmHpZWdSazd7Tw8PDw8PDw8vinCMBzW03HIsRLvhnv0HL7qD+IwjzXKzaNaxeEt9kz21RWEBV5XQbfka3pQWL4qgdLyNQkUcbwFT/FP4zjWt+D+++OY4laZQJgtnqNOwe5l9XkGmIIL/PEHUAGTeuc5P15wBbu34ucSIAXkX77h4xUtIBSXnxIAOhCLy98TANNfLj8lYBYQCuLPWmAWEBe9f90DUPdKy08JWB0U1HsoaAgQxPSnAgwBopz+VABQvoDnAnqTP0r8zealzfPcQqqAQSs/C6AKGLX0pwKs8uX0cwGaAHr6uYC9wSt46qDCB4RXBDTkMwWMhnxZQF3+s8pf1AZY5VsCWuVnAUQ+YPxB43X5koAiH035soCa/AaeBOw34m359AaQPCK/1oAAyJ8aIPBI+7QGRkD+3IBt+A6QPzeg34SH2pcauN+Kt9uXGljkse0jb6BP8AD+vwGKPLZ95A0UofbnDbAFj20/eQN+gD8h/LgRD25/8QCA2088AD/Oo8dPOoDo8ZMOoPPNeej4pwdAgUcfX9IDzHnnf5lnz88XnH/nSf4M8cIL7I+/P3yCP0G88P7W+v2z9ft36+8P9vuJ/X5r/f3Jfj83//5vff/R+v6Hvb9i78/Y+7vW94/N71/Z+2P2/pq9P2fv7+n5ATu/YOcn7PyGnR+x8yt6ftYN3PzOENCcH7LzS3Z+Ss9vO62DV5uPmgAXSz5+fs7O72n/QBQLwPwLrH+C9W/Q/hHWv8L6Z2j/ThZgvX+I9S/R/inWv8X6x2j/Guufo/17rH+Q9S/S/knWv0n7R2n/Kuufpf27tH+Y9i/vWP+0h4eHh4eHh8cW8QcxLJDBvLKoigAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOS0wNC0wM1QxNDo1MDoyMy0wNTowMKoZjXIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTktMDQtMDNUMTQ6NTA6MjMtMDU6MDDbRDXOAAAAAElFTkSuQmCC\n", "text/plain": [ "" ] @@ -339,10 +304,8 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Instantiate an empty Tallies object\n", @@ -351,10 +314,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "collapsed": false - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Create mesh which will be used for tally\n", @@ -375,10 +336,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "collapsed": true - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "# Export to \"tallies.xml\"\n", @@ -394,9 +353,8 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": { - "collapsed": false, "scrolled": true }, "outputs": [ @@ -404,197 +362,133 @@ "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-2017 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.9.0\n", - " Git SHA1 | 9b7cebf7bc34d60e0f1750c3d6cb103df11e8dc4\n", - " Date/Time | 2017-12-04 20:47:36\n", - " OpenMP Threads | 4\n", + " Version | 0.10.0\n", + " Git SHA1 | cc09ba4a5bf2edd95624e386b72803d83ddc9f4f\n", + " Date/Time | 2019-04-03 14:50:23\n", + " OpenMP Threads | 2\n", "\n", " Reading settings XML file...\n", " Reading cross sections XML file...\n", " Reading materials XML file...\n", " Reading geometry XML file...\n", - " Building neighboring cells lists for each surface...\n", - " Reading U235 from /home/romano/openmc/scripts/nndc_hdf5/U235.h5\n", - " Reading U238 from /home/romano/openmc/scripts/nndc_hdf5/U238.h5\n", - " Reading O16 from /home/romano/openmc/scripts/nndc_hdf5/O16.h5\n", - " Reading H1 from /home/romano/openmc/scripts/nndc_hdf5/H1.h5\n", - " Reading B10 from /home/romano/openmc/scripts/nndc_hdf5/B10.h5\n", - " Reading Zr90 from /home/romano/openmc/scripts/nndc_hdf5/Zr90.h5\n", - " Maximum neutron transport energy: 2.00000E+07 eV for U235\n", + " Reading U235 from /home/shriwise/opt/openmc/xs/nndc_hdf5/U235.h5\n", + " Reading U238 from /home/shriwise/opt/openmc/xs/nndc_hdf5/U238.h5\n", + " Reading O16 from /home/shriwise/opt/openmc/xs/nndc_hdf5/O16.h5\n", + " Reading H1 from /home/shriwise/opt/openmc/xs/nndc_hdf5/H1.h5\n", + " Reading B10 from /home/shriwise/opt/openmc/xs/nndc_hdf5/B10.h5\n", + " Reading Zr90 from /home/shriwise/opt/openmc/xs/nndc_hdf5/Zr90.h5\n", + " Maximum neutron transport energy: 20000000.000000 eV for U235\n", " Reading tallies XML file...\n", " Writing summary.h5 file...\n", " Initializing source particles...\n", "\n", " ====================> K EIGENVALUE SIMULATION <====================\n", "\n", - " Bat./Gen. k Average k \n", - " ========= ======== ==================== \n", - " 1/1 1.04359 \n", - " 2/1 1.04323 \n", - " 3/1 1.04711 \n", - " 4/1 1.03892 \n", - " 5/1 1.02442 \n", - " 6/1 1.02046 \n", - " 7/1 1.05998 \n", - " 8/1 1.04184 \n", - " 9/1 1.04786 \n", - " 10/1 1.06636 \n", - " 11/1 1.07229 \n", - " 12/1 1.04413 1.05821 +/- 0.01408\n", - " 13/1 1.06376 1.06006 +/- 0.00834\n", - " 14/1 1.06898 1.06229 +/- 0.00630\n", - " 15/1 1.05095 1.06002 +/- 0.00538\n", - " 16/1 1.04453 1.05744 +/- 0.00510\n", - " 17/1 1.05626 1.05727 +/- 0.00431\n", - " 18/1 1.03423 1.05439 +/- 0.00472\n", - " 19/1 1.04240 1.05306 +/- 0.00437\n", - " 20/1 1.03719 1.05147 +/- 0.00422\n", - " 21/1 1.04308 1.05071 +/- 0.00389\n", - " 22/1 1.03956 1.04978 +/- 0.00367\n", - " 23/1 1.05824 1.05043 +/- 0.00344\n", - " 24/1 1.03151 1.04908 +/- 0.00346\n", - " 25/1 1.02695 1.04760 +/- 0.00354\n", - " 26/1 1.02581 1.04624 +/- 0.00358\n", - " 27/1 1.09932 1.04936 +/- 0.00459\n", - " 28/1 1.05983 1.04995 +/- 0.00437\n", - " 29/1 1.03381 1.04910 +/- 0.00422\n", - " 30/1 1.06727 1.05001 +/- 0.00410\n", - " 31/1 1.03180 1.04914 +/- 0.00400\n", - " 32/1 1.04520 1.04896 +/- 0.00382\n", - " 33/1 1.07158 1.04994 +/- 0.00378\n", - " 34/1 1.04283 1.04965 +/- 0.00363\n", - " 35/1 1.03272 1.04897 +/- 0.00354\n", - " 36/1 1.02730 1.04814 +/- 0.00351\n", - " 37/1 1.01975 1.04709 +/- 0.00353\n", - " 38/1 1.04815 1.04712 +/- 0.00341\n", - " 39/1 1.02642 1.04641 +/- 0.00336\n", - " 40/1 1.04063 1.04622 +/- 0.00325\n", - " 41/1 0.97384 1.04388 +/- 0.00392\n", - " 42/1 1.06049 1.04440 +/- 0.00383\n", - " 43/1 1.04467 1.04441 +/- 0.00371\n", - " 44/1 1.04454 1.04441 +/- 0.00360\n", - " 45/1 1.06529 1.04501 +/- 0.00355\n", - " 46/1 1.05496 1.04529 +/- 0.00346\n", - " 47/1 1.03717 1.04507 +/- 0.00337\n", - " 48/1 1.03874 1.04490 +/- 0.00328\n", - " 49/1 1.02083 1.04428 +/- 0.00326\n", - " 50/1 1.04847 1.04439 +/- 0.00318\n", - " 51/1 1.03789 1.04423 +/- 0.00310\n", - " 52/1 1.04447 1.04423 +/- 0.00303\n", - " 53/1 1.01942 1.04366 +/- 0.00301\n", - " 54/1 1.04639 1.04372 +/- 0.00294\n", - " 55/1 1.02539 1.04331 +/- 0.00291\n", - " 56/1 1.06312 1.04374 +/- 0.00288\n", - " 57/1 1.05854 1.04406 +/- 0.00283\n", - " 58/1 1.05150 1.04421 +/- 0.00278\n", - " 59/1 1.04321 1.04419 +/- 0.00272\n", - " 60/1 1.04762 1.04426 +/- 0.00266\n", - " 61/1 0.99442 1.04328 +/- 0.00279\n", - " 62/1 1.06907 1.04378 +/- 0.00278\n", - " 63/1 1.03170 1.04355 +/- 0.00274\n", - " 64/1 1.04308 1.04354 +/- 0.00268\n", - " 65/1 1.01439 1.04301 +/- 0.00269\n", - " 66/1 1.04581 1.04306 +/- 0.00264\n", - " 67/1 1.04404 1.04308 +/- 0.00259\n", - " 68/1 1.03158 1.04288 +/- 0.00256\n", - " 69/1 1.04953 1.04299 +/- 0.00251\n", - " 70/1 1.06338 1.04333 +/- 0.00250\n", - " 71/1 1.03768 1.04324 +/- 0.00246\n", - " 72/1 1.02531 1.04295 +/- 0.00243\n", - " 73/1 1.04552 1.04299 +/- 0.00240\n", - " 74/1 1.04293 1.04299 +/- 0.00236\n", - " 75/1 1.05928 1.04324 +/- 0.00233\n", - " 76/1 1.05057 1.04335 +/- 0.00230\n", - " 77/1 1.01846 1.04298 +/- 0.00230\n", - " 78/1 1.05755 1.04320 +/- 0.00227\n", - " 79/1 1.05222 1.04333 +/- 0.00224\n", - " 80/1 1.04860 1.04340 +/- 0.00221\n", - " 81/1 1.03026 1.04322 +/- 0.00219\n", - " 82/1 1.02360 1.04294 +/- 0.00218\n", - " 83/1 1.06679 1.04327 +/- 0.00217\n", - " 84/1 1.06297 1.04354 +/- 0.00216\n", - " 85/1 1.04426 1.04355 +/- 0.00213\n", - " 86/1 1.00337 1.04302 +/- 0.00217\n", - " 87/1 1.04787 1.04308 +/- 0.00214\n", - " 88/1 1.04332 1.04308 +/- 0.00211\n", - " 89/1 1.04369 1.04309 +/- 0.00208\n", - " 90/1 1.05006 1.04318 +/- 0.00206\n", - " 91/1 1.05394 1.04331 +/- 0.00204\n", - " 92/1 1.06017 1.04352 +/- 0.00202\n", - " 93/1 1.02032 1.04324 +/- 0.00202\n", - " 94/1 1.04816 1.04330 +/- 0.00200\n", - " 95/1 1.06601 1.04356 +/- 0.00199\n", - " 96/1 1.02876 1.04339 +/- 0.00197\n", - " 97/1 1.03929 1.04334 +/- 0.00195\n", - " 98/1 1.01958 1.04307 +/- 0.00195\n", - " 99/1 1.01899 1.04280 +/- 0.00195\n", - " 100/1 1.05150 1.04290 +/- 0.00193\n", - " Creating state point statepoint.100.h5...\n", - "\n", - " =======================> TIMING STATISTICS <=======================\n", - "\n", - " Total time for initialization = 6.5927E-01 seconds\n", - " Reading cross sections = 6.1679E-01 seconds\n", - " Total time in simulation = 1.2768E+02 seconds\n", - " Time in transport only = 1.2740E+02 seconds\n", - " Time in inactive batches = 2.7221E+00 seconds\n", - " Time in active batches = 1.2496E+02 seconds\n", - " Time synchronizing fission bank = 2.4179E-02 seconds\n", - " Sampling source sites = 1.7261E-02 seconds\n", - " SEND/RECV source sites = 6.7268E-03 seconds\n", - " Time accumulating tallies = 1.5442E-02 seconds\n", - " Total time for finalization = 1.6664E-01 seconds\n", - " Total time elapsed = 1.2854E+02 seconds\n", - " Calculation Rate (inactive) = 18368.1 neutrons/second\n", - " Calculation Rate (active) = 3601.09 neutrons/second\n", - "\n", - " ============================> RESULTS <============================\n", - "\n", - " k-effective (Collision) = 1.04331 +/- 0.00192\n", - " k-effective (Track-length) = 1.04290 +/- 0.00193\n", - " k-effective (Absorption) = 1.04136 +/- 0.00151\n", - " Combined k-effective = 1.04183 +/- 0.00122\n", - " Leakage Fraction = 0.00000 +/- 0.00000\n", - "\n" + " Bat./Gen. k Average k\n", + " ========= ======== ====================\n", + " 1/1 1.04359\n", + " 2/1 1.04323\n", + " 3/1 1.04711\n", + " 4/1 1.03892\n", + " 5/1 1.02459\n", + " 6/1 1.03936\n", + " 7/1 1.03529\n", + " 8/1 1.01590\n", + " 9/1 1.03060\n", + " 10/1 1.02892\n", + " 11/1 1.03987\n", + " 12/1 1.04395 1.04191 +/- 0.00204\n", + " 13/1 1.04971 1.04451 +/- 0.00285\n", + " 14/1 1.03880 1.04308 +/- 0.00247\n", + " 15/1 1.03092 1.04065 +/- 0.00310\n", + " 16/1 1.04653 1.04163 +/- 0.00271\n", + " 17/1 1.04114 1.04156 +/- 0.00229\n", + " 18/1 1.06033 1.04391 +/- 0.00307\n", + " 19/1 1.04163 1.04365 +/- 0.00272\n", + " 20/1 1.04992 1.04428 +/- 0.00251\n", + " 21/1 1.01577 1.04169 +/- 0.00345\n", + " 22/1 1.03611 1.04122 +/- 0.00318\n", + " 23/1 1.03251 1.04055 +/- 0.00300\n", + " 24/1 1.03996 1.04051 +/- 0.00278\n", + " 25/1 1.06132 1.04190 +/- 0.00294\n", + " 26/1 1.03581 1.04152 +/- 0.00277\n", + " 27/1 1.05195 1.04213 +/- 0.00268\n", + " 28/1 1.03721 1.04186 +/- 0.00254\n", + " 29/1 1.03475 1.04148 +/- 0.00243\n", + " 30/1 1.03057 1.04094 +/- 0.00237\n", + " 31/1 1.04407 1.04109 +/- 0.00226\n", + " 32/1 1.04661 1.04134 +/- 0.00217\n", + " 33/1 1.03286 1.04097 +/- 0.00210\n", + " 34/1 1.01253 1.03978 +/- 0.00234\n", + " 35/1 1.03488 1.03959 +/- 0.00225\n", + " 36/1 1.02509 1.03903 +/- 0.00223\n", + " 37/1 1.03647 1.03894 +/- 0.00215\n", + " 38/1 1.06387 1.03983 +/- 0.00226\n", + " 39/1 1.02418 1.03929 +/- 0.00224\n", + " 40/1 1.05815 1.03992 +/- 0.00226\n", + " 41/1 1.04433 1.04006 +/- 0.00219\n", + " 42/1 1.04627 1.04025 +/- 0.00213\n", + " 43/1 1.05089 1.04057 +/- 0.00209\n", + " 44/1 1.02985 1.04026 +/- 0.00205\n", + " 45/1 1.06236 1.04089 +/- 0.00209\n", + " 46/1 1.04283 1.04094 +/- 0.00203\n", + " 47/1 1.04404 1.04103 +/- 0.00197\n", + " 48/1 1.05946 1.04151 +/- 0.00198\n", + " 49/1 1.03286 1.04129 +/- 0.00194\n", + " 50/1 1.07609 1.04216 +/- 0.00208\n", + " 51/1 1.02097 1.04164 +/- 0.00210\n", + " 52/1 1.08390 1.04265 +/- 0.00228\n", + " 53/1 1.01654 1.04204 +/- 0.00231\n", + " 54/1 1.02701 1.04170 +/- 0.00228\n", + " 55/1 1.04845 1.04185 +/- 0.00223\n", + " 56/1 1.05401 1.04212 +/- 0.00220\n", + " 57/1 1.04673 1.04221 +/- 0.00216\n", + " 58/1 1.04093 1.04219 +/- 0.00211\n", + " 59/1 1.03205 1.04198 +/- 0.00208\n", + " 60/1 1.05368 1.04221 +/- 0.00205\n", + " 61/1 1.02273 1.04183 +/- 0.00204\n", + " 62/1 1.03259 1.04165 +/- 0.00201\n", + " 63/1 1.06216 1.04204 +/- 0.00201\n", + " 64/1 1.03658 1.04194 +/- 0.00198\n", + " 65/1 1.02072 1.04155 +/- 0.00198\n", + " 66/1 1.03019 1.04135 +/- 0.00195\n", + " 67/1 1.05241 1.04155 +/- 0.00193\n", + " 68/1 1.05906 1.04185 +/- 0.00192\n", + " 69/1 1.05263 1.04203 +/- 0.00190\n", + " 70/1 1.02176 1.04169 +/- 0.00189\n", + " 71/1 1.03390 1.04157 +/- 0.00187\n", + " 72/1 1.05470 1.04178 +/- 0.00185\n", + " 73/1 1.03892 1.04173 +/- 0.00182\n", + " 74/1 0.98570 1.04086 +/- 0.00199\n", + " 75/1 1.02591 1.04063 +/- 0.00198\n" ] - }, - { - "data": { - "text/plain": [ - "0" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ @@ -618,9 +512,8 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": { - "collapsed": false, "scrolled": true }, "outputs": [], @@ -638,26 +531,9 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Tally\n", - "\tID =\t1\n", - "\tName =\tflux\n", - "\tFilters =\tMeshFilter\n", - "\tNuclides =\ttotal \n", - "\tScores =\t['flux', 'fission']\n", - "\tEstimator =\ttracklength\n", - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "tally = sp.get_tally(scores=['flux'])\n", "print(tally)" @@ -672,33 +548,9 @@ }, { "cell_type": "code", - "execution_count": 19, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[[ 0.40981574, 0. ]],\n", - "\n", - " [[ 0.40963388, 0. ]],\n", - "\n", - " [[ 0.41117481, 0. ]],\n", - "\n", - " ..., \n", - " [[ 0.41179009, 0. ]],\n", - "\n", - " [[ 0.41329412, 0. ]],\n", - "\n", - " [[ 0.41494587, 0. ]]])" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "tally.sum" ] @@ -712,52 +564,9 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "(10000, 1, 2)\n" - ] - }, - { - "data": { - "text/plain": [ - "(array([[[ 0.00455351, 0. ]],\n", - " \n", - " [[ 0.00455149, 0. ]],\n", - " \n", - " [[ 0.00456861, 0. ]],\n", - " \n", - " ..., \n", - " [[ 0.00457545, 0. ]],\n", - " \n", - " [[ 0.00459216, 0. ]],\n", - " \n", - " [[ 0.00461051, 0. ]]]),\n", - " array([[[ 2.00748004e-05, 0.00000000e+00]],\n", - " \n", - " [[ 1.75039529e-05, 0.00000000e+00]],\n", - " \n", - " [[ 1.96093103e-05, 0.00000000e+00]],\n", - " \n", - " ..., \n", - " [[ 1.69721143e-05, 0.00000000e+00]],\n", - " \n", - " [[ 1.58964240e-05, 0.00000000e+00]],\n", - " \n", - " [[ 1.81009205e-05, 0.00000000e+00]]]))" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "print(tally.mean.shape)\n", "(tally.mean, tally.std_dev)" @@ -772,26 +581,9 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Tally\n", - "\tID =\t2\n", - "\tName =\tflux\n", - "\tFilters =\tMeshFilter\n", - "\tNuclides =\ttotal \n", - "\tScores =\t['flux']\n", - "\tEstimator =\ttracklength\n", - "\n" - ] - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "flux = tally.get_slice(scores=['flux'])\n", "fission = tally.get_slice(scores=['fission'])\n", @@ -807,10 +599,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "collapsed": false - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ "flux.std_dev.shape = (100, 100)\n", @@ -821,32 +611,9 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\nRNgj+L+7omDDDxCT/gAAAABJRU5ErkJggg==\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "fig = plt.subplot(121)\n", "fig.imshow(flux.mean)\n", @@ -863,22 +630,9 @@ }, { "cell_type": "code", - "execution_count": 24, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Determine relative error\n", "relative_error = np.zeros_like(flux.std_dev)\n", @@ -905,29 +659,9 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([ (1.0, [0.08865821621194064, 0.3784631548839458, 0.3904972254761878], [-0.1326194120629367, 0.6232164386612264, 0.7707226233389667], 1192346.6320091304, 0),\n", - " (1.0, [0.08865821621194064, 0.3784631548839458, 0.3904972254761878], [-0.6644604177986176, -0.6182443857504494, 0.41984072297352965], 356616.8122252148, 0),\n", - " (1.0, [-0.0524358478826145, -0.2989344511695743, -0.17188682459930016], [0.26140031911472983, 0.8973965547376798, 0.35545646246996265], 836460.5302888667, 0),\n", - " ...,\n", - " (1.0, [-0.1061743912824093, 0.2687838889464804, 0.3209783107825243], [0.04074864852534854, 0.6722488179526406, -0.7392029994559244], 4419569.703536596, 0),\n", - " (1.0, [-0.1061743912824093, 0.2687838889464804, 0.3209783107825243], [0.2921501228867853, 0.9089796683155593, 0.2973285527596903], 2716068.3317314633, 0),\n", - " (1.0, [0.24742871049665383, 0.2887788071034633, 0.12388141872813332], [0.9088435703078486, -0.040848021273558396, -0.4151322727373983], 1005076.8690394862, 0)], \n", - " dtype=[('wgt', '" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\nAAAASUVORK5CYII=\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ "# Create log-spaced energy bins from 1 keV to 10 MeV\n", "energy_bins = np.logspace(3,7)\n", @@ -1029,35 +719,12 @@ }, { "cell_type": "code", - "execution_count": 28, - "metadata": { - "collapsed": false - }, - "outputs": [ - { - "data": { - "text/plain": [ - "(-0.5, 0.5)" - ] - }, - "execution_count": 28, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "execution_count": null, + "metadata": {}, + "outputs": [], "source": [ - "plt.quiver(sp.source['xyz'][:,0], sp.source['xyz'][:,1],\n", - " sp.source['uvw'][:,0], sp.source['uvw'][:,1],\n", + "plt.quiver(sp.source['r']['x'], sp.source['r']['y'],\n", + " sp.source['u']['x'], sp.source['u']['y'],\n", " np.log(sp.source['E']), cmap='jet', scale=20.0)\n", "plt.colorbar()\n", "plt.xlim((-0.5,0.5))\n", @@ -1081,9 +748,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.7" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/examples/jupyter/search.ipynb b/examples/jupyter/search.ipynb index 5c754049a..6031c4c4d 100644 --- a/examples/jupyter/search.ipynb +++ b/examples/jupyter/search.ipynb @@ -14,9 +14,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize third-party libraries and the OpenMC Python API\n", @@ -43,9 +41,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "# Create the model. `ppm_Boron` will be the parametric variable.\n", @@ -74,8 +70,8 @@ " materials = openmc.Materials([fuel, zircaloy, water])\n", " \n", " # Create cylinders for the fuel and clad\n", - " fuel_outer_radius = openmc.ZCylinder(R=0.39218)\n", - " clad_outer_radius = openmc.ZCylinder(R=0.45720)\n", + " fuel_outer_radius = openmc.ZCylinder(r=0.39218)\n", + " clad_outer_radius = openmc.ZCylinder(r=0.45720)\n", "\n", " # Create boundary planes to surround the geometry\n", " min_x = openmc.XPlane(x0=-0.63, boundary_type='reflective')\n", @@ -139,24 +135,142 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "Iteration: 1; Guess of 1.00e+03 produced a keff of 1.08721 +/- 0.00158\n", - "Iteration: 2; Guess of 2.50e+03 produced a keff of 0.95263 +/- 0.00147\n", - "Iteration: 3; Guess of 1.75e+03 produced a keff of 1.01466 +/- 0.00163\n", - "Iteration: 4; Guess of 2.12e+03 produced a keff of 0.98475 +/- 0.00167\n", - "Iteration: 5; Guess of 1.94e+03 produced a keff of 0.99954 +/- 0.00154\n", - "Iteration: 6; Guess of 1.84e+03 produced a keff of 1.00428 +/- 0.00162\n", - "Iteration: 7; Guess of 1.89e+03 produced a keff of 1.00633 +/- 0.00166\n", - "Iteration: 8; Guess of 1.91e+03 produced a keff of 1.00388 +/- 0.00166\n", - "Iteration: 9; Guess of 1.93e+03 produced a keff of 0.99813 +/- 0.00142\n", - "Critical Boron Concentration: 1926 ppm\n" + "Iteration: 1; Guess of 1.00e+03 produced a keff of 1.08853 +/- 0.00158\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 2; Guess of 2.50e+03 produced a keff of 0.95372 +/- 0.00148\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 3; Guess of 1.75e+03 produced a keff of 1.01328 +/- 0.00169\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 4; Guess of 2.12e+03 produced a keff of 0.98150 +/- 0.00158\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 5; Guess of 1.94e+03 produced a keff of 0.99886 +/- 0.00146\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 6; Guess of 1.84e+03 produced a keff of 1.00759 +/- 0.00162\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 7; Guess of 1.89e+03 produced a keff of 1.00063 +/- 0.00166\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 8; Guess of 1.91e+03 produced a keff of 0.99970 +/- 0.00150\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/shriwise/.pyenv/versions/3.6.7/lib/python3.6/site-packages/openmc-0.10.0-py3.6-linux-x86_64.egg/openmc/mixin.py:71: IDWarning: Another Universe instance already exists with id=0.\n", + " warn(msg, IDWarning)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration: 9; Guess of 1.90e+03 produced a keff of 0.99935 +/- 0.00164\n", + "Critical Boron Concentration: 1902 ppm\n" ] } ], @@ -179,18 +293,18 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { - "image/png": "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\nb2ZmVnJO9mZmZiXnZG9mZlZyTvZmZmYl52RvZmZWck72ZmZmJedkb2ZmVnJO9mZmZiVX2B30Wtlt\nC5Yzfc4SVqxZz6j2NqZOPIjJh/spu2Zm1pqc7KvctmA502YvYv2GTQAsX7OeabMXATjhm5lZS3I3\nfpXpc5ZsTvQV6zdsYvqcJU2KyMzMbPs42VdZsWb9NpWbmZkNdE72VUa1t21TuZmZ2UDnZF9l6sSD\naBs2ZIuytmFDmDrxoCZFZGZmtn08QK9KZRCeR+ObmVlZONnXMPnw0U7uZmZWGu7GNzMzKzknezMz\ns5IrLNlLmilplaRH69RL0lWSlkpaKOmIXN3lkhZLejwto6LiNDMzK7siz+yvByZ1U38ycEB6TQG+\nDiDp7cD/AA4F3gocCZxQYJxmZmalVliyj4h5wIvdLHIqMCsy9wPtkvYGAtgJ2BF4DTAMeK6oOM3M\nzMqumdfsRwPLcvNdwOiI+AVwL7AyveZExONNiM/MzKwUmpnsa12HD0lvAt4CjCH7QnCipONrbkCa\nIqlTUufq1asLDNXMzKx1NTPZdwH75ObHACuA9wL3R8S6iFgH3AkcU2sDETEjIjoiomPkyJGFB2xm\nZtaKmpnsbwfOTqPyjwHWRsRK4NfACZKGShpGNjjP3fhmZma9VNgd9CTdCEwARkjqAi4hG2xHRFwL\n3AGcAiwFXgLOS6veApwILCIbrPejiPh+UXGamZmVXWHJPiLO7KE+gPNrlG8C/rqouMzMzAYb30HP\nzMys5JzszczMSs7J3szMrOSc7M3MzErOyd7MzKzknOzNzMxKzsnezMys5JzszczMSs7J3szMrOSc\n7M3MzErOyd7MzKzknOzNzMxKzsnezMys5JzszczMSs7J3szMrOSc7M3MzErOyd7MzKzknOzNzMxK\nrrBkL2mmpFWSHq1TL0lXSVoqaaGkI3J1+0q6S9Ljkh6TNLaoOM3MzMquyDP764FJ3dSfDByQXlOA\nr+fqZgHTI+ItwFHAqoJiNDMzK72hRW04Iub1cEZ+KjArIgK4X1K7pL2B3YGhEXF32s66omI0MzMb\nDJp5zX40sCw335XKDgTWSJotaYGk6ZKGNCVCMzOzEmhmsleNsiDrbTgOuBA4EngDcG7NDUhTJHVK\n6ly9enVRcZqZmbW0Zib7LmCf3PwYYEUqXxART0fERuA24Iga6xMRMyKiIyI6Ro4cWXjAZmZmraiZ\nyf524Ow0Kv8YYG1ErAQeBHaXVMneJwKPNStIMzOzVlfYAD1JNwITgBGSuoBLgGEAEXEtcAdwCrAU\neAk4L9VtknQhcI8kAfOB64qK08zMrOyKHI1/Zg/1AZxfp+5u4NAi4jIzMxtsfAc9MzOzknOyNzMz\nKzknezMzs5JzsjczMys5J3szM7OSc7I3MzMrOSd7MzOzknOyNzMzKzknezMzs5JzsjczMys5J3sz\nM7OSc7I3MzMrOSd7MzOzknOyNzMzKzknezMzs5JzsjczMys5J3szM7OSc7I3MzMrOSd7MzOzkiss\n2UuaKWmVpEfr1EvSVZKWSloo6Yiq+uGSlku6uqgYzczMBoMiz+yvByZ1U38ycEB6TQG+XlX/ReC+\nQiIzMzMbRApL9hExD3ixm0VOBWZF5n6gXdLeAJLeBuwF3FVUfGZmZoNFM6/ZjwaW5ea7gNGSdgC+\nCkxtSlRmZmYl08xkrxplAXwcuCMiltWo33ID0hRJnZI6V69e3ecBmpmZlcHQJu67C9gnNz8GWAEc\nCxwn6ePALsCOktZFxEXVG4iIGcAMgI6Ojig+ZDMzs9bTzGR/O3CBpJuAo4G1EbESOKuygKRzgY5a\nid7MzMwa01Cyl7QX8GVgVEScLGkccGxEfKObdW4EJgAjJHUBlwDDACLiWuAO4BRgKfAScN52tMPM\nzMzqUETPvd+S7gS+CVwcEYdJGgosiIhDig6wUR0dHdHZ2dnsMMzMzPqNpPkR0dHTco0O0BsRETcD\nrwBExEZg03bEZ2ZmZv2k0WT/B0l7ko2WR9IxwNrCojIzM7M+0+gAvc+QDah7o6SfASOB9xUWlZmZ\nmfWZhpJ9RDwk6QTgILLfxy+JiA2FRmZmZmZ9otHR+GdXFR0hiYiYVUBMZmZm1oca7cY/Mje9E/Au\n4CHAyd7MzGyAa7Qb/xP5eUm7Af9RSERmZmbWp3p7b/yXyB5Na2ZmZgNco9fsv0/62R3ZF4RxwM1F\nBWVmZmZ9p9Fr9lfkpjcCv4qIrgLiMTMzsz7W6DX7+4oOxMzMzIrRbbKX9Hte7b7fogqIiBheSFRm\nZmbWZ7pN9hGxa38FYmZmZsXYpufZS3od2e/sAYiIX/d5RGZmZtanGvrpnaT3SHoSeAa4D3gWuLPA\nuMzMzKyPNPo7+y8CxwBPRMT+ZHfQ+1lhUZmZmVmfaTTZb4iIF4AdJO0QEfcC4wuMy8zMzPpIo9fs\n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\n", + "image/png": "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\n", "text/plain": [ - "" + "
" ] }, - "metadata": {}, + "metadata": { + "needs_background": "light" + }, "output_type": "display_data" } ], @@ -198,7 +312,7 @@ "plt.figure(figsize=(8, 4.5))\n", "plt.title('Eigenvalue versus Boron Concentration')\n", "# Create a scatter plot using the mean value of keff\n", - "plt.scatter(guesses, [keffs[i][0] for i in range(len(keffs))])\n", + "plt.scatter(guesses, [keffs[i].nominal_value for i in range(len(keffs))])\n", "plt.xlabel('Boron Concentration [ppm]')\n", "plt.ylabel('Eigenvalue')\n", "plt.show()" @@ -230,7 +344,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/python/basic/build-xml.py b/examples/python/basic/build-xml.py index 0dc83ed44..5caed2810 100644 --- a/examples/python/basic/build-xml.py +++ b/examples/python/basic/build-xml.py @@ -36,9 +36,9 @@ materials_file.export_to_xml() ############################################################################### # Instantiate ZCylinder surfaces -surf1 = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=7, name='surf 1') -surf2 = openmc.ZCylinder(surface_id=2, x0=0, y0=0, R=9, name='surf 2') -surf3 = openmc.ZCylinder(surface_id=3, x0=0, y0=0, R=11, name='surf 3') +surf1 = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=7, name='surf 1') +surf2 = openmc.ZCylinder(surface_id=2, x0=0, y0=0, r=9, name='surf 2') +surf3 = openmc.ZCylinder(surface_id=3, x0=0, y0=0, r=11, name='surf 3') surf3.boundary_type = 'vacuum' # Instantiate Cells diff --git a/examples/python/lattice/hexagonal/build-xml.py b/examples/python/lattice/hexagonal/build-xml.py index 24087be9e..c0aa815b4 100644 --- a/examples/python/lattice/hexagonal/build-xml.py +++ b/examples/python/lattice/hexagonal/build-xml.py @@ -43,7 +43,7 @@ left = openmc.XPlane(surface_id=1, x0=-3, name='left') right = openmc.XPlane(surface_id=2, x0=3, name='right') bottom = openmc.YPlane(surface_id=3, y0=-4, name='bottom') top = openmc.YPlane(surface_id=4, y0=4, name='top') -fuel_surf = openmc.ZCylinder(surface_id=5, x0=0, y0=0, R=0.4) +fuel_surf = openmc.ZCylinder(surface_id=5, x0=0, y0=0, r=0.4) left.boundary_type = 'vacuum' right.boundary_type = 'vacuum' diff --git a/examples/python/lattice/nested/build-xml.py b/examples/python/lattice/nested/build-xml.py index ef74173f5..37483a939 100644 --- a/examples/python/lattice/nested/build-xml.py +++ b/examples/python/lattice/nested/build-xml.py @@ -39,9 +39,9 @@ left = openmc.XPlane(surface_id=1, x0=-2, name='left') right = openmc.XPlane(surface_id=2, x0=2, name='right') bottom = openmc.YPlane(surface_id=3, y0=-2, name='bottom') top = openmc.YPlane(surface_id=4, y0=2, name='top') -fuel1 = openmc.ZCylinder(surface_id=5, x0=0, y0=0, R=0.4) -fuel2 = openmc.ZCylinder(surface_id=6, x0=0, y0=0, R=0.3) -fuel3 = openmc.ZCylinder(surface_id=7, x0=0, y0=0, R=0.2) +fuel1 = openmc.ZCylinder(surface_id=5, x0=0, y0=0, r=0.4) +fuel2 = openmc.ZCylinder(surface_id=6, x0=0, y0=0, r=0.3) +fuel3 = openmc.ZCylinder(surface_id=7, x0=0, y0=0, r=0.2) left.boundary_type = 'vacuum' right.boundary_type = 'vacuum' diff --git a/examples/python/lattice/simple/build-xml.py b/examples/python/lattice/simple/build-xml.py index 6222cd366..1cdfa24f3 100644 --- a/examples/python/lattice/simple/build-xml.py +++ b/examples/python/lattice/simple/build-xml.py @@ -39,9 +39,9 @@ left = openmc.XPlane(surface_id=1, x0=-2, name='left') right = openmc.XPlane(surface_id=2, x0=2, name='right') bottom = openmc.YPlane(surface_id=3, y0=-2, name='bottom') top = openmc.YPlane(surface_id=4, y0=2, name='top') -fuel1 = openmc.ZCylinder(surface_id=5, x0=0, y0=0, R=0.4) -fuel2 = openmc.ZCylinder(surface_id=6, x0=0, y0=0, R=0.3) -fuel3 = openmc.ZCylinder(surface_id=7, x0=0, y0=0, R=0.2) +fuel1 = openmc.ZCylinder(surface_id=5, x0=0, y0=0, r=0.4) +fuel2 = openmc.ZCylinder(surface_id=6, x0=0, y0=0, r=0.3) +fuel3 = openmc.ZCylinder(surface_id=7, x0=0, y0=0, r=0.2) left.boundary_type = 'vacuum' right.boundary_type = 'vacuum' diff --git a/examples/python/pincell/build-xml.py b/examples/python/pincell/build-xml.py index a9e6a5a74..c728d5f36 100644 --- a/examples/python/pincell/build-xml.py +++ b/examples/python/pincell/build-xml.py @@ -49,9 +49,9 @@ materials_file.export_to_xml() ############################################################################### # Instantiate ZCylinder surfaces -fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.39218, name='Fuel OR') -clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, R=0.40005, name='Clad IR') -clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, R=0.45720, name='Clad OR') +fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=0.39218, name='Fuel OR') +clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, r=0.40005, name='Clad IR') +clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, r=0.45720, name='Clad OR') left = openmc.XPlane(surface_id=4, x0=-0.62992, name='left') right = openmc.XPlane(surface_id=5, x0=0.62992, name='right') bottom = openmc.YPlane(surface_id=6, y0=-0.62992, name='bottom') diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index a2a5e0e7f..c1ad9d20a 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -16,7 +16,6 @@ particles = 1000 time_step = 1*24*60*60 # s final_time = 5*24*60*60 # s time_steps = np.full(final_time // time_step, time_step) - chain_file = './chain_simple.xml' power = 174 # W/cm, for 2D simulations only (use W for 3D) @@ -54,9 +53,9 @@ borated_water.add_s_alpha_beta('c_H_in_H2O') ############################################################################### # Instantiate ZCylinder surfaces -fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.39218, name='Fuel OR') -clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, R=0.40005, name='Clad IR') -clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, R=0.45720, name='Clad OR') +fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=0.39218, name='Fuel OR') +clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, r=0.40005, name='Clad IR') +clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, r=0.45720, name='Clad OR') left = openmc.XPlane(surface_id=4, x0=-0.62992, name='left') right = openmc.XPlane(surface_id=5, x0=0.62992, name='right') bottom = openmc.YPlane(surface_id=6, y0=-0.62992, name='bottom') @@ -100,7 +99,7 @@ geometry = openmc.Geometry(root) # Compute cell areas area = {} -area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2 +area[fuel] = np.pi * fuel_or.coefficients['r'] ** 2 # Set materials volume for depletion. Set to an area for 2D simulations uo2.volume = area[fuel] diff --git a/examples/python/pincell_multigroup/build-xml.py b/examples/python/pincell_multigroup/build-xml.py index 337044508..995cd87db 100644 --- a/examples/python/pincell_multigroup/build-xml.py +++ b/examples/python/pincell_multigroup/build-xml.py @@ -98,7 +98,7 @@ materials_file.export_to_xml() ############################################################################### # Instantiate ZCylinder surfaces -fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.54, name='Fuel OR') +fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=0.54, name='Fuel OR') left = openmc.XPlane(surface_id=4, x0=-0.63, name='left') right = openmc.XPlane(surface_id=5, x0=0.63, name='right') bottom = openmc.YPlane(surface_id=6, y0=-0.63, name='bottom') diff --git a/include/openmc/bank.h b/include/openmc/bank.h index b7fab9ac6..93e2be669 100644 --- a/include/openmc/bank.h +++ b/include/openmc/bank.h @@ -20,15 +20,14 @@ namespace openmc { namespace simulation { -extern "C" int64_t n_bank; - extern std::vector source_bank; extern std::vector fission_bank; +extern std::vector secondary_bank; #ifdef _OPENMP extern std::vector master_fission_bank; #endif -#pragma omp threadprivate(fission_bank, n_bank) +#pragma omp threadprivate(fission_bank, secondary_bank) } // namespace simulation diff --git a/include/openmc/capi.h b/include/openmc/capi.h index f8e53e578..7e4f4253f 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -73,6 +73,8 @@ extern "C" { int openmc_next_batch(int* status); int openmc_nuclide_name(int index, const char** name); int openmc_plot_geometry(); + int openmc_id_map(const void* slice, int32_t* data_out); + int openmc_property_map(const void* slice, double* data_out); int openmc_reset(); int openmc_run(); void openmc_set_seed(int64_t new_seed); diff --git a/include/openmc/cell.h b/include/openmc/cell.h index f436592cd..cc4429b44 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -43,6 +43,7 @@ constexpr int32_t OP_UNION {std::numeric_limits::max() - 4}; class Cell; class Universe; +class UniversePartitioner; namespace model { extern std::vector> cells; @@ -65,6 +66,8 @@ public: //! \brief Write universe information to an HDF5 group. //! \param group_id An HDF5 group id. void to_hdf5(hid_t group_id) const; + + std::unique_ptr partitioner_; }; //============================================================================== @@ -182,6 +185,7 @@ class DAGCell : public Cell public: moab::DagMC* dagmc_ptr_; DAGCell(); + int32_t dag_index_; bool contains(Position r, Direction u, int32_t on_surface) const; @@ -192,6 +196,36 @@ public: }; #endif +//============================================================================== +//! Speeds up geometry searches by grouping cells in a search tree. +// +//! Currently this object only works with universes that are divided up by a +//! bunch of z-planes. It could be generalized to other planes, cylinders, +//! and spheres. +//============================================================================== + +class UniversePartitioner +{ +public: + explicit UniversePartitioner(const Universe& univ); + + //! Return the list of cells that could contain the given coordinates. + const std::vector& get_cells(Position r, Direction u) const; + +private: + //! A sorted vector of indices to surfaces that partition the universe + std::vector surfs_; + + //! Vectors listing the indices of the cells that lie within each partition + // + //! There are n+1 partitions with n surfaces. `partitions_.front()` gives the + //! cells that lie on the negative side of `surfs_.front()`. + //! `partitions_.back()` gives the cells that lie on the positive side of + //! `surfs_.back()`. Otherwise, `partitions_[i]` gives cells sandwiched + //! between `surfs_[i-1]` and `surfs_[i]`. + std::vector> partitions_; +}; + //============================================================================== // Non-member functions //============================================================================== diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 067e6431a..d9307abe2 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -225,6 +225,13 @@ constexpr int N_3P {197}; constexpr int N_N3P {198}; constexpr int N_3N2PA {199}; constexpr int N_5N2P {200}; +constexpr int N_XP {203}; +constexpr int N_XD {204}; +constexpr int N_XT {205}; +constexpr int N_X3HE {206}; +constexpr int N_XA {207}; +constexpr int HEATING {301}; +constexpr int DAMAGE_ENERGY {444}; constexpr int COHERENT {502}; constexpr int INCOHERENT {504}; constexpr int PAIR_PROD_ELEC {515}; @@ -396,8 +403,6 @@ constexpr int LEAKAGE {3}; // Miscellaneous constexpr int C_NONE {-1}; constexpr int F90_NONE {0}; //TODO: replace usage of this with C_NONE -constexpr int ERROR_INT {-2147483647}; // TODO: use when F90 - // interop is gone // Interpolation rules enum class Interpolation { diff --git a/include/openmc/error.h b/include/openmc/error.h index 206c9b5b2..5f30f0252 100644 --- a/include/openmc/error.h +++ b/include/openmc/error.h @@ -7,6 +7,12 @@ #include "openmc/capi.h" +#ifdef __GNUC__ +#define UNREACHABLE() __builtin_unreachable() +#else +#define UNREACHABLE() (void)0 +#endif + namespace openmc { inline void @@ -29,13 +35,13 @@ set_errmsg(const std::stringstream& message) [[noreturn]] void fatal_error(const std::string& message, int err=-1); -inline +[[noreturn]] inline void fatal_error(const std::stringstream& message) { fatal_error(message.str()); } -inline +[[noreturn]] inline void fatal_error(const char* message) { fatal_error({message, std::strlen(message)}); diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index a2da2c5f2..8e6e8a82e 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -1,6 +1,8 @@ #ifndef OPENMC_GEOMETRY_H #define OPENMC_GEOMETRY_H +#include +#include #include #include @@ -15,18 +17,37 @@ namespace openmc { namespace model { -extern "C" int root_universe; +extern int root_universe; //!< Index of root universe +extern int n_coord_levels; //!< Number of CSG coordinate levels extern std::vector overlap_check_count; } // namespace model +//============================================================================== +// Information about nearest boundary crossing +//============================================================================== + +struct BoundaryInfo { + double distance {INFINITY}; //!< distance to nearest boundary + int surface_index {0}; //!< if boundary is surface, index in surfaces vector + int coord_level; //!< coordinate level after crossing boundary + std::array lattice_translation {}; //!< which way lattice indices will change +}; + +//============================================================================== +//! Check two distances by coincidence tolerance +//============================================================================== + +inline bool coincident(double d1, double d2) { + return std::abs(d1 - d2) < FP_COINCIDENT; +} + //============================================================================== //! Check for overlapping cells at a particle's position. //============================================================================== -extern "C" bool -check_cell_overlap(Particle* p); +bool check_cell_overlap(Particle* p); //============================================================================== //! Locate a particle in the geometry tree and set its geometry data fields. @@ -40,23 +61,19 @@ check_cell_overlap(Particle* p); //! valid geometry coordinate stack. //============================================================================== -extern "C" bool -find_cell(Particle* p, bool use_neighbor_lists); +bool find_cell(Particle* p, bool use_neighbor_lists); //============================================================================== //! Move a particle into a new lattice tile. //============================================================================== -extern "C" void -cross_lattice(Particle* p, int lattice_translation[3]); +void cross_lattice(Particle* p, const BoundaryInfo& boundary); //============================================================================== //! Find the next boundary a particle will intersect. //============================================================================== -extern "C" void -distance_to_boundary(Particle* p, double* dist, int* surface_crossed, - int lattice_translation[3], int* next_level); +BoundaryInfo distance_to_boundary(Particle* p); } // namespace openmc diff --git a/include/openmc/geometry_aux.h b/include/openmc/geometry_aux.h index 5898c61f3..ffcdcf885 100644 --- a/include/openmc/geometry_aux.h +++ b/include/openmc/geometry_aux.h @@ -57,7 +57,7 @@ void finalize_geometry(std::vector>& nuc_temps, //! \return The index of the root universe. //============================================================================== -extern "C" int32_t find_root_universe(); +int32_t find_root_universe(); //============================================================================== //! Populate all data structures needed for distribcells. @@ -74,7 +74,7 @@ void prepare_distribcell(); //! the root universe). //============================================================================== -extern "C" void count_cell_instances(int32_t univ_indx); +void count_cell_instances(int32_t univ_indx); //============================================================================== //! Recursively search through universes and count universe instances. @@ -84,8 +84,7 @@ extern "C" void count_cell_instances(int32_t univ_indx); //! search_univ. //============================================================================== -extern "C" int -count_universe_instances(int32_t search_univ, int32_t target_univ_id); +int count_universe_instances(int32_t search_univ, int32_t target_univ_id); //============================================================================== //! Build a character array representing the path to a distribcell instance. @@ -107,7 +106,7 @@ distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset); //! \return The number of coordinate levels. //============================================================================== -extern "C" int maximum_levels(int32_t univ); +int maximum_levels(int32_t univ); //============================================================================== //! Deallocates global vectors and maps for cells, universes, and lattices. diff --git a/include/openmc/lattice.h b/include/openmc/lattice.h index 9c8b8af68..25ab4c034 100644 --- a/include/openmc/lattice.h +++ b/include/openmc/lattice.h @@ -152,8 +152,7 @@ public: int indx_; //!< An index to a Lattice universes or offsets array. LatticeIter(Lattice &lat, int indx) - : lat_(lat), - indx_(indx) + : indx_(indx), lat_(lat) {} bool operator==(const LatticeIter &rhs) {return (indx_ == rhs.indx_);} diff --git a/include/openmc/material.h b/include/openmc/material.h index 8a09dd9a2..3109f6d78 100644 --- a/include/openmc/material.h +++ b/include/openmc/material.h @@ -47,7 +47,7 @@ public: explicit Material(pugi::xml_node material_node); // Methods - void calculate_xs(const Particle& p) const; + void calculate_xs(Particle& p) const; //! Assign thermal scattering tables to specific nuclides within the material //! so the code knows when to apply bound thermal scattering data @@ -95,20 +95,33 @@ public: std::unique_ptr ttb_; private: + //! Calculate the collision stopping power + void collision_stopping_power(double* s_col, bool positron); + //! Initialize bremsstrahlung data void init_bremsstrahlung(); //! Normalize density void normalize_density(); - void calculate_neutron_xs(const Particle& p) const; - void calculate_photon_xs(const Particle& p) const; + void calculate_neutron_xs(Particle& p) const; + void calculate_photon_xs(Particle& p) const; }; //============================================================================== // Non-member functions //============================================================================== +//! Calculate Sternheimer adjustment factor +double sternheimer_adjustment(const std::vector& f, const + std::vector& e_b_sq, double e_p_sq, double n_conduction, double + log_I, double tol, int max_iter); + +//! Calculate density effect correction +double density_effect(const std::vector& f, const std::vector& + e_b_sq, double e_p_sq, double n_conduction, double rho, double E, double tol, + int max_iter); + //! Read material data from materials.xml void read_materials_xml(); diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index b13f9568a..f53bba224 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -43,7 +43,7 @@ public: // Methods //! Determine which bins were crossed by a particle - //! + // //! \param[in] p Particle to check //! \param[out] bins Bins that were crossed //! \param[out] lengths Fraction of tracklength in each bin @@ -51,58 +51,56 @@ public: std::vector& lengths) const; //! Determine which surface bins were crossed by a particle - //! + // //! \param[in] p Particle to check //! \param[out] bins Surface bins that were crossed void surface_bins_crossed(const Particle* p, std::vector& bins) const; //! Get bin at a given position in space - //! + // //! \param[in] r Position to get bin for //! \return Mesh bin int get_bin(Position r) const; //! Get bin given mesh indices - //! + // //! \param[in] Array of mesh indices //! \return Mesh bin int get_bin_from_indices(const int* ijk) const; //! Get mesh indices given a position - //! + // //! \param[in] r Position to get indices for //! \param[out] ijk Array of mesh indices //! \param[out] in_mesh Whether position is in mesh void get_indices(Position r, int* ijk, bool* in_mesh) const; //! Get mesh indices corresponding to a mesh bin - //! + // //! \param[in] bin Mesh bin //! \param[out] ijk Mesh indices void get_indices_from_bin(int bin, int* ijk) const; - //! Check if a line connected by two points intersects the mesh - //! - //! \param[in] r0 Starting position + //! Check where a line segment intersects the mesh and if it intersects at all + // + //! \param[in,out] r0 In: starting position, out: intersection point //! \param[in] r1 Ending position - //! \return Whether line connecting r0 and r1 intersects mesh - bool intersects(Position r0, Position r1) const; + //! \param[out] ijk Indices of the mesh bin containing the intersection point + //! \return Whether the line segment connecting r0 and r1 intersects mesh + bool intersects(Position& r0, Position r1, int* ijk) const; //! Write mesh data to an HDF5 group - //! + // //! \param[in] group HDF5 group void to_hdf5(hid_t group) const; //! Count number of bank sites in each mesh bin / energy bin - //! - //! \param[in] n Number of bank sites + // //! \param[in] bank Array of bank sites - //! \param[in] n_energy Number of energies - //! \param[in] energies Array of energies //! \param[out] Whether any bank sites are outside the mesh //! \return Array indicating number of sites in each mesh/energy bin - xt::xarray count_sites(int64_t n, const Particle::Bank* bank, - int n_energy, const double* energies, bool* outside) const; + xt::xarray count_sites(const std::vector& bank, + bool* outside) const; int id_ {-1}; //!< User-specified ID int n_dimension_; //!< Number of dimensions @@ -113,9 +111,9 @@ public: xt::xarray width_; //!< Width of each mesh element private: - bool intersects_1d(Position r0, Position r1) const; - bool intersects_2d(Position r0, Position r1) const; - bool intersects_3d(Position r0, Position r1) const; + bool intersects_1d(Position& r0, Position r1, int* ijk) const; + bool intersects_2d(Position& r0, Position r1, int* ijk) const; + bool intersects_3d(Position& r0, Position r1, int* ijk) const; }; //============================================================================== @@ -123,10 +121,12 @@ private: //============================================================================== //! Read meshes from either settings/tallies +// //! \param[in] root XML node void read_meshes(pugi::xml_node root); //! Write mesh data to an HDF5 group +// //! \param[in] group HDF5 group void meshes_to_hdf5(hid_t group); diff --git a/include/openmc/nuclide.h b/include/openmc/nuclide.h index c8b34d08e..4be898a64 100644 --- a/include/openmc/nuclide.h +++ b/include/openmc/nuclide.h @@ -13,6 +13,7 @@ #include "openmc/constants.h" #include "openmc/endf.h" +#include "openmc/particle.h" #include "openmc/reaction.h" #include "openmc/reaction_product.h" #include "openmc/urr.h" @@ -20,69 +21,6 @@ namespace openmc { -//============================================================================== -// Constants -//============================================================================== - -constexpr double CACHE_INVALID {-1.0}; - -//============================================================================== -//! Cached microscopic cross sections for a particular nuclide at the current -//! energy -//============================================================================== - -struct NuclideMicroXS { - // Microscopic cross sections in barns - double total; //!< total cross section - double absorption; //!< absorption (disappearance) - double fission; //!< fission - double nu_fission; //!< neutron production from fission - - double elastic; //!< If sab_frac is not 1 or 0, then this value is - //!< averaged over bound and non-bound nuclei - double thermal; //!< Bound thermal elastic & inelastic scattering - double thermal_elastic; //!< Bound thermal elastic scattering - double photon_prod; //!< microscopic photon production xs - - // Cross sections for depletion reactions (note that these are not stored in - // macroscopic cache) - double reaction[DEPLETION_RX.size()]; - - // Indicies and factors needed to compute cross sections from the data tables - int index_grid; //!< Index on nuclide energy grid - int index_temp; //!< Temperature index for nuclide - double interp_factor; //!< Interpolation factor on nuc. energy grid - int index_sab {-1}; //!< Index in sab_tables - int index_temp_sab; //!< Temperature index for sab_tables - double sab_frac; //!< Fraction of atoms affected by S(a,b) - bool use_ptable; //!< In URR range with probability tables? - - // Energy and temperature last used to evaluate these cross sections. If - // these values have changed, then the cross sections must be re-evaluated. - double last_E {0.0}; //!< Last evaluated energy - double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant - //!< * temperature (eV)) -}; - -//============================================================================== -// MATERIALMACROXS contains cached macroscopic cross sections for the material a -// particle is traveling through -//============================================================================== - -struct MaterialMacroXS { - double total; //!< macroscopic total xs - double absorption; //!< macroscopic absorption xs - double fission; //!< macroscopic fission xs - double nu_fission; //!< macroscopic production xs - double photon_prod; //!< macroscopic photon production xs - - // Photon cross sections - double coherent; //!< macroscopic coherent xs - double incoherent; //!< macroscopic incoherent xs - double photoelectric; //!< macroscopic photoelectric xs - double pair_production; //!< macroscopic pair production xs -}; - //============================================================================== // Data for a nuclide //============================================================================== @@ -102,14 +40,13 @@ public: //! Initialize logarithmic grid for energy searches void init_grid(); - void calculate_xs(int i_sab, double E, int i_log_union, - double sqrtkT, double sab_frac); + void calculate_xs(int i_sab, int i_log_union, double sab_frac, Particle& p); - void calculate_sab_xs(int i_sab, double E, double sqrtkT, double sab_frac); + void calculate_sab_xs(int i_sab, double sab_frac, Particle& p); // Methods double nu(double E, EmissionMode mode, int group=0) const; - void calculate_elastic_xs() const; + void calculate_elastic_xs(Particle& p) const; //! Determines the microscopic 0K elastic cross section at a trial relative //! energy used in resonance scattering @@ -117,7 +54,7 @@ public: //! \brief Determines cross sections in the unresolved resonance range //! from probability tables. - void calculate_urr_xs(int i_temp, double E) const; + void calculate_urr_xs(int i_temp, Particle& p) const; // Data members std::string name_; //!< Name of nuclide, e.g. "U235" @@ -194,15 +131,6 @@ extern std::unordered_map nuclide_map; } // namespace data -namespace simulation { - -// Cross section caches -extern NuclideMicroXS* micro_xs; -extern MaterialMacroXS material_xs; -#pragma omp threadprivate(micro_xs, material_xs) - -} // namespace simulation - //============================================================================== // Non-member functions //============================================================================== diff --git a/include/openmc/particle.h b/include/openmc/particle.h index 2cb30a44f..8ec53de44 100644 --- a/include/openmc/particle.h +++ b/include/openmc/particle.h @@ -6,9 +6,11 @@ #include #include +#include // for unique_ptr #include #include +#include "openmc/constants.h" #include "openmc/position.h" namespace openmc { @@ -24,15 +26,14 @@ namespace openmc { // use to store the bins for delayed group tallies. constexpr int MAX_DELAYED_GROUPS {8}; -// Maximum number of secondary particles created -constexpr int MAX_SECONDARY {1000}; - // Maximum number of lost particles constexpr int MAX_LOST_PARTICLES {10}; // Maximum number of lost particles, relative to the total number of particles constexpr double REL_MAX_LOST_PARTICLES {1.0e-6}; +constexpr double CACHE_INVALID {-1.0}; + //============================================================================== // Class declarations //============================================================================== @@ -52,12 +53,88 @@ struct LocalCoord { void reset(); }; +//============================================================================== +//! Cached microscopic cross sections for a particular nuclide at the current +//! energy +//============================================================================== + +struct NuclideMicroXS { + // Microscopic cross sections in barns + double total; //!< total cross section + double absorption; //!< absorption (disappearance) + double fission; //!< fission + double nu_fission; //!< neutron production from fission + + double elastic; //!< If sab_frac is not 1 or 0, then this value is + //!< averaged over bound and non-bound nuclei + double thermal; //!< Bound thermal elastic & inelastic scattering + double thermal_elastic; //!< Bound thermal elastic scattering + double photon_prod; //!< microscopic photon production xs + + // Cross sections for depletion reactions (note that these are not stored in + // macroscopic cache) + double reaction[DEPLETION_RX.size()]; + + // Indicies and factors needed to compute cross sections from the data tables + int index_grid; //!< Index on nuclide energy grid + int index_temp; //!< Temperature index for nuclide + double interp_factor; //!< Interpolation factor on nuc. energy grid + int index_sab {-1}; //!< Index in sab_tables + int index_temp_sab; //!< Temperature index for sab_tables + double sab_frac; //!< Fraction of atoms affected by S(a,b) + bool use_ptable; //!< In URR range with probability tables? + + // Energy and temperature last used to evaluate these cross sections. If + // these values have changed, then the cross sections must be re-evaluated. + double last_E {0.0}; //!< Last evaluated energy + double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant + //!< * temperature (eV)) +}; + +//============================================================================== +//! Cached microscopic photon cross sections for a particular element at the +//! current energy +//============================================================================== + +struct ElementMicroXS { + int index_grid; //!< index on element energy grid + double last_E {0.0}; //!< last evaluated energy in [eV] + double interp_factor; //!< interpolation factor on energy grid + double total; //!< microscopic total photon xs + double coherent; //!< microscopic coherent xs + double incoherent; //!< microscopic incoherent xs + double photoelectric; //!< microscopic photoelectric xs + double pair_production; //!< microscopic pair production xs +}; + +//============================================================================== +// MACROXS contains cached macroscopic cross sections for the material a +// particle is traveling through +//============================================================================== + +struct MacroXS { + double total; //!< macroscopic total xs + double absorption; //!< macroscopic absorption xs + double fission; //!< macroscopic fission xs + double nu_fission; //!< macroscopic production xs + double photon_prod; //!< macroscopic photon production xs + + // Photon cross sections + double coherent; //!< macroscopic coherent xs + double incoherent; //!< macroscopic incoherent xs + double photoelectric; //!< macroscopic photoelectric xs + double pair_production; //!< macroscopic pair production xs +}; + //============================================================================ //! State of a particle being transported through geometry //============================================================================ class Particle { public: + //========================================================================== + // Aliases and type definitions + //! Particle types enum class Type { neutron, photon, electron, positron @@ -73,19 +150,87 @@ public: Type particle; }; + //========================================================================== // Constructors + Particle(); + //========================================================================== + // Methods and accessors + + // Accessors for position in global coordinates + Position& r() { return coord_[0].r; } + const Position& r() const { return coord_[0].r; } + + // Accessors for position in local coordinates + Position& r_local() { return coord_[n_coord_ - 1].r; } + const Position& r_local() const { return coord_[n_coord_ - 1].r; } + + // Accessors for direction in global coordinates + Direction& u() { return coord_[0].u; } + const Direction& u() const { return coord_[0].u; } + + // Accessors for direction in local coordinates + Direction& u_local() { return coord_[n_coord_ - 1].u; } + const Direction& u_local() const { return coord_[n_coord_ - 1].u; } + + //! resets all coordinate levels for the particle + void clear(); + + //! create a secondary particle + // + //! stores the current phase space attributes of the particle in the + //! secondary bank and increments the number of sites in the secondary bank. + //! \param u Direction of the secondary particle + //! \param E Energy of the secondary particle in [eV] + //! \param type Particle type + void create_secondary(Direction u, double E, Type type) const; + + //! initialize from a source site + // + //! initializes a particle from data stored in a source site. The source + //! site may have been produced from an external source, from fission, or + //! simply as a secondary particle. + //! \param src Source site data + void from_source(const Bank* src); + + //! Transport a particle from birth to death + void transport(); + + //! Cross a surface and handle boundary conditions + void cross_surface(); + + //! mark a particle as lost and create a particle restart file + //! \param message A warning message to display + void mark_as_lost(const char* message); + + void mark_as_lost(const std::string& message) + {mark_as_lost(message.c_str());} + + void mark_as_lost(const std::stringstream& message) + {mark_as_lost(message.str());} + + //! create a particle restart HDF5 file + void write_restart() const; + + //========================================================================== + // Data members + + // Cross section caches + std::vector neutron_xs_; //!< Microscopic neutron cross sections + std::vector photon_xs_; //!< Microscopic photon cross sections + MacroXS macro_xs_; //!< Macroscopic cross sections + int64_t id_; //!< Unique ID Type type_ {Type::neutron}; //!< Particle type (n, p, e, etc.) int n_coord_ {1}; //!< number of current coordinate levels int cell_instance_; //!< offset for distributed properties - LocalCoord coord_[MAX_COORD]; //!< coordinates for all levels + std::vector coord_; //!< coordinates for all levels // Particle coordinates before crossing a surface int n_coord_last_ {1}; //!< number of current coordinates - int cell_last_[MAX_COORD]; //!< coordinates for all levels + std::vector cell_last_; //!< coordinates for all levels // Energy data double E_; //!< post-collision energy in eV @@ -135,65 +280,6 @@ public: // Track output bool write_track_ {false}; - - // Secondary particles created - int64_t n_secondary_ {}; - Bank secondary_bank_[MAX_SECONDARY]; - - // Accessors for position in global coordinates - Position& r() { return coord_[0].r; } - const Position& r() const { return coord_[0].r; } - - // Accessors for position in local coordinates - Position& r_local() { return coord_[n_coord_ - 1].r; } - const Position& r_local() const { return coord_[n_coord_ - 1].r; } - - // Accessors for direction in global coordinates - Direction& u() { return coord_[0].u; } - const Direction& u() const { return coord_[0].u; } - - // Accessors for direction in local coordinates - Direction& u_local() { return coord_[n_coord_ - 1].u; } - const Direction& u_local() const { return coord_[n_coord_ - 1].u; } - - //! resets all coordinate levels for the particle - void clear(); - - //! create a secondary particle - // - //! stores the current phase space attributes of the particle in the - //! secondary bank and increments the number of sites in the secondary bank. - //! \param u Direction of the secondary particle - //! \param E Energy of the secondary particle in [eV] - //! \param type Particle type - void create_secondary(Direction u, double E, Type type); - - //! initialize from a source site - // - //! initializes a particle from data stored in a source site. The source - //! site may have been produced from an external source, from fission, or - //! simply as a secondary particle. - //! \param src Source site data - void from_source(const Bank* src); - - //! Transport a particle from birth to death - void transport(); - - //! Cross a surface and handle boundary conditions - void cross_surface(); - - //! mark a particle as lost and create a particle restart file - //! \param message A warning message to display - void mark_as_lost(const char* message); - - void mark_as_lost(const std::string& message) - {mark_as_lost(message.c_str());} - - void mark_as_lost(const std::stringstream& message) - {mark_as_lost(message.str());} - - //! create a particle restart HDF5 file - void write_restart() const; }; } // namespace openmc diff --git a/include/openmc/photon.h b/include/openmc/photon.h index a2279f502..902d24232 100644 --- a/include/openmc/photon.h +++ b/include/openmc/photon.h @@ -42,7 +42,7 @@ public: PhotonInteraction(hid_t group, int i_element); // Methods - void calculate_xs(double E) const; + void calculate_xs(Particle& p) const; void compton_scatter(double alpha, bool doppler, double* alpha_out, double* mu, int* i_shell) const; @@ -87,7 +87,8 @@ public: // Stopping power data double I_; // mean excitation energy - xt::xtensor stopping_power_collision_; + xt::xtensor n_electrons_; + xt::xtensor ionization_energy_; xt::xtensor stopping_power_radiative_; // Bremsstrahlung scaled DCS @@ -97,22 +98,6 @@ private: void compton_doppler(double alpha, double mu, double* E_out, int* i_shell) const; }; -//============================================================================== -//! Cached microscopic photon cross sections for a particular element at the -//! current energy -//============================================================================== - -struct ElementMicroXS { - int index_grid; //!< index on element energy grid - double last_E {0.0}; //!< last evaluated energy in [eV] - double interp_factor; //!< interpolation factor on energy grid - double total; //!< microscopic total photon xs - double coherent; //!< microscopic coherent xs - double incoherent; //!< microscopic incoherent xs - double photoelectric; //!< microscopic photoelectric xs - double pair_production; //!< microscopic pair production xs -}; - //============================================================================== // Non-member functions //============================================================================== @@ -135,11 +120,6 @@ extern std::unordered_map element_map; } // namespace data -namespace simulation { -extern ElementMicroXS* micro_photon_xs; -#pragma omp threadprivate(micro_photon_xs) -} // namespace simulation - } // namespace openmc #endif // OPENMC_PHOTON_H diff --git a/include/openmc/physics.h b/include/openmc/physics.h index a7dd20aad..107380eb8 100644 --- a/include/openmc/physics.h +++ b/include/openmc/physics.h @@ -7,6 +7,8 @@ #include "openmc/position.h" #include "openmc/reaction.h" +#include + namespace openmc { //============================================================================== @@ -47,24 +49,23 @@ int sample_nuclide(const Particle* p); //! Determine the average total, prompt, and delayed neutrons produced from //! fission and creates appropriate bank sites. void create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx, - Particle::Bank* bank_array, int64_t* bank_size, int64_t bank_capacity); + std::vector& bank); int sample_element(Particle* p); -Reaction* sample_fission(int i_nuclide, double E); +Reaction* sample_fission(int i_nuclide, const Particle* p); -void sample_photon_product(int i_nuclide, double E, int* i_rx, int* i_product); +void sample_photon_product(int i_nuclide, const Particle* p, int* i_rx, int* i_product); void absorption(Particle* p, int i_nuclide); void scatter(Particle*, int i_nuclide); //! Treats the elastic scattering of a neutron with a target. -void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, double& E, - Direction& u, double& mu_lab); +void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, + Particle* p); -void sab_scatter(int i_nuclide, int i_sab, double& E, - Direction& u, double& mu); +void sab_scatter(int i_nuclide, int i_sab, Particle* p); //! samples the target velocity. The constant cross section free gas model is //! the default method. Methods for correctly accounting for the energy diff --git a/include/openmc/physics_mg.h b/include/openmc/physics_mg.h index cdf1f7089..f246cb6ec 100644 --- a/include/openmc/physics_mg.h +++ b/include/openmc/physics_mg.h @@ -8,6 +8,8 @@ #include "openmc/particle.h" #include "openmc/nuclide.h" +#include + namespace openmc { //! \brief samples particle behavior after a collision event. @@ -31,12 +33,9 @@ scatter(Particle* p); //! \brief Determines the average total, prompt and delayed neutrons produced //! from fission and creates the appropriate bank sites. //! \param p Particle to operate on -//! \param bank_array The particle bank to populate -//! \param size_bank Number of particles currently in the bank -//! \param bank_array_size Allocated size of the bank +//! \param bank The particle bank to populate void -create_fission_sites(Particle* p, Particle::Bank* bank_array, int64_t* size_bank, - int64_t bank_array_size); +create_fission_sites(Particle* p, std::vector& bank); //! \brief Handles an absorption event //! \param p Particle to operate on diff --git a/include/openmc/plot.h b/include/openmc/plot.h index a6f215cfb..8e5aa02a6 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -10,6 +10,8 @@ #include "hdf5.h" #include "openmc/position.h" #include "openmc/constants.h" +#include "openmc/cell.h" +#include "openmc/geometry.h" #include "openmc/particle.h" #include "openmc/xml_interface.h" @@ -53,6 +55,28 @@ struct RGBColor { typedef xt::xtensor ImageData; +struct IdData { + // Constructor + IdData(size_t h_res, size_t v_res); + + // Methods + void set_value(size_t y, size_t x, const Particle& p, int level); + + // Members + xt::xtensor data_; //!< 2D array of cell & material ids +}; + +struct PropertyData { + // Constructor + PropertyData(size_t h_res, size_t v_res); + + // Methods + void set_value(size_t y, size_t x, const Particle& p, int level); + + // Members + xt::xtensor data_; //!< 2D array of temperature & density data +}; + enum class PlotType { slice = 1, voxel = 2 @@ -65,16 +89,98 @@ enum class PlotBasis { }; enum class PlotColorBy { - cells = 1, - mats = 2 + cells = 0, + mats = 1 }; //=============================================================================== // Plot class //=============================================================================== +class PlotBase { +public: + template T get_map() const; -class Plot -{ + // Members +public: + Position origin_; //!< Plot origin in geometry + Position width_; //!< Plot width in geometry + PlotBasis basis_; //!< Plot basis (XY/XZ/YZ) + std::array pixels_; //!< Plot size in pixels + int level_; //!< Plot universe level +}; + +template +T PlotBase::get_map() const { + + size_t width = pixels_[0]; + size_t height = pixels_[1]; + + // get pixel size + double in_pixel = (width_[0])/static_cast(width); + double out_pixel = (width_[1])/static_cast(height); + + // size data array + T data(width, height); + + // setup basis indices and initial position centered on pixel + int in_i, out_i; + Position xyz = origin_; + switch(basis_) { + case PlotBasis::xy : + in_i = 0; + out_i = 1; + break; + case PlotBasis::xz : + in_i = 0; + out_i = 2; + break; + case PlotBasis::yz : + in_i = 1; + out_i = 2; + break; +#ifdef __GNUC__ + default: + __builtin_unreachable(); +#endif + } + + // set initial position + xyz[in_i] = origin_[in_i] - width_[0] / 2. + in_pixel / 2.; + xyz[out_i] = origin_[out_i] + width_[1] / 2. - out_pixel / 2.; + + // arbitrary direction + Direction dir = {0.7071, 0.7071, 0.0}; + + #pragma omp parallel + { + Particle p; + p.r() = xyz; + p.u() = dir; + p.coord_[0].universe = model::root_universe; + int level = level_; + int j{}; + + #pragma omp for + for (int y = 0; y < height; y++) { + p.r()[out_i] = xyz[out_i] - out_pixel * y; + for (int x = 0; x < width; x++) { + p.r()[in_i] = xyz[in_i] + in_pixel * x; + p.n_coord_ = 1; + // local variables + bool found_cell = find_cell(&p, 0); + j = p.n_coord_ - 1; + if (level >=0) {j = level + 1;} + if (found_cell) { + data.set_value(y, x, p, j); + } + } // inner for + } // outer for + } // omp parallel + + return data; +} + +class Plot : public PlotBase { public: // Constructor @@ -95,17 +201,12 @@ private: void set_meshlines(pugi::xml_node plot_node); void set_mask(pugi::xml_node plot_node); - // Members +// Members public: int id_; //!< Plot ID PlotType type_; //!< Plot type (Slice/Voxel) PlotColorBy color_by_; //!< Plot coloring (cell/material) - Position origin_; //!< Plot origin in geometry - Position width_; //!< Plot width in geometry - PlotBasis basis_; //!< Plot basis (XY/XZ/YZ) - std::array pixels_; //!< Plot size in pixels int meshlines_width_; //!< Width of lines added to the plot - int level_; //!< Plot universe level int index_meshlines_mesh_; //!< Index of the mesh to draw on the plot RGBColor meshlines_color_; //!< Color of meshlines on the plot RGBColor not_found_; //!< Plot background color @@ -127,13 +228,6 @@ void draw_mesh_lines(Plot pl, ImageData& data); //! \param[out] image data associated with the plot object void output_ppm(Plot pl, const ImageData& data); -//! Get the rgb color for a given particle position in a plot -//! \param[in] particle with position for current pixel -//! \param[in] plot object -//! \param[out] rgb color -//! \param[out] cell or material id for particle position -void position_rgb(Particle p, Plot pl, RGBColor& rgb, int& id); - //! Initialize a voxel file //! \param[in] id of an open hdf5 file //! \param[in] dimensions of the voxel file (dx, dy, dz) diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index 3eb5b1f05..fef6ebbcd 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -37,19 +37,15 @@ extern "C" int restart_batch; //!< batch at which a restart job resumed extern "C" bool satisfy_triggers; //!< have tally triggers been satisfied? extern "C" int total_gen; //!< total number of generations simulated extern double total_weight; //!< Total source weight in a batch -extern "C" int64_t work; //!< number of particles per process +extern int64_t work_per_rank; //!< number of particles per MPI rank extern std::vector k_generation; extern std::vector work_index; // Threadprivate variables extern "C" bool trace; //!< flag to show debug information -#ifdef _OPENMP -extern "C" int n_threads; //!< number of OpenMP threads -extern "C" int thread_id; //!< ID of a given thread -#endif -#pragma omp threadprivate(current_work, thread_id, trace) +#pragma omp threadprivate(current_work, trace) } // namespace simulation diff --git a/include/openmc/surface.h b/include/openmc/surface.h index e1acefa23..4db397a59 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -131,6 +131,8 @@ class DAGSurface : public Surface public: moab::DagMC* dagmc_ptr_; DAGSurface(); + int32_t dag_index_; + double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; @@ -178,7 +180,6 @@ public: class SurfaceXPlane : public PeriodicSurface { - double x0_; public: explicit SurfaceXPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -188,6 +189,8 @@ public: bool periodic_translate(const PeriodicSurface* other, Position& r, Direction& u) const; BoundingBox bounding_box() const; + + double x0_; }; //============================================================================== @@ -198,7 +201,6 @@ public: class SurfaceYPlane : public PeriodicSurface { - double y0_; public: explicit SurfaceYPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -208,6 +210,8 @@ public: bool periodic_translate(const PeriodicSurface* other, Position& r, Direction& u) const; BoundingBox bounding_box() const; + + double y0_; }; //============================================================================== @@ -218,7 +222,6 @@ public: class SurfaceZPlane : public PeriodicSurface { - double z0_; public: explicit SurfaceZPlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -228,6 +231,8 @@ public: bool periodic_translate(const PeriodicSurface* other, Position& r, Direction& u) const; BoundingBox bounding_box() const; + + double z0_; }; //============================================================================== @@ -238,7 +243,6 @@ public: class SurfacePlane : public PeriodicSurface { - double A_, B_, C_, D_; public: explicit SurfacePlane(pugi::xml_node surf_node); double evaluate(Position r) const; @@ -248,6 +252,8 @@ public: bool periodic_translate(const PeriodicSurface* other, Position& r, Direction& u) const; BoundingBox bounding_box() const; + + double A_, B_, C_, D_; }; //============================================================================== @@ -259,13 +265,14 @@ public: class SurfaceXCylinder : public CSGSurface { - double y0_, z0_, radius_; public: explicit SurfaceXCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double y0_, z0_, radius_; }; //============================================================================== @@ -277,13 +284,14 @@ public: class SurfaceYCylinder : public CSGSurface { - double x0_, z0_, radius_; public: explicit SurfaceYCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, z0_, radius_; }; //============================================================================== @@ -295,13 +303,14 @@ public: class SurfaceZCylinder : public CSGSurface { - double x0_, y0_, radius_; public: explicit SurfaceZCylinder(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, radius_; }; //============================================================================== @@ -313,13 +322,14 @@ public: class SurfaceSphere : public CSGSurface { - double x0_, y0_, z0_, radius_; public: explicit SurfaceSphere(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, radius_; }; //============================================================================== @@ -331,13 +341,14 @@ public: class SurfaceXCone : public CSGSurface { - double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceXCone(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, radius_sq_; }; //============================================================================== @@ -349,13 +360,14 @@ public: class SurfaceYCone : public CSGSurface { - double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceYCone(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, radius_sq_; }; //============================================================================== @@ -367,13 +379,14 @@ public: class SurfaceZCone : public CSGSurface { - double x0_, y0_, z0_, radius_sq_; public: explicit SurfaceZCone(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + double x0_, y0_, z0_, radius_sq_; }; //============================================================================== @@ -384,14 +397,15 @@ public: class SurfaceQuadric : public CSGSurface { - // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 - double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; public: explicit SurfaceQuadric(pugi::xml_node surf_node); double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; void to_hdf5_inner(hid_t group_id) const; + + // Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy + Jz + K = 0 + double A_, B_, C_, D_, E_, F_, G_, H_, J_, K_; }; //============================================================================== diff --git a/include/openmc/tallies/tally_scoring.h b/include/openmc/tallies/tally_scoring.h index af03b1aa2..2c5a29c17 100644 --- a/include/openmc/tallies/tally_scoring.h +++ b/include/openmc/tallies/tally_scoring.h @@ -59,14 +59,14 @@ private: //! since collisions do not occur in voids. // //! \param p The particle being tracked -void score_collision_tally(const Particle* p); +void score_collision_tally(Particle* p); //! Score tallies based on a simple count of events (for continuous energy). // //! Analog tallies are triggered at every collision, not every event. // //! \param p The particle being tracked -void score_analog_tally_ce(const Particle* p); +void score_analog_tally_ce(Particle* p); //! Score tallies based on a simple count of events (for multigroup). // @@ -83,7 +83,7 @@ void score_analog_tally_mg(const Particle* p); // //! \param p The particle being tracked //! \param distance The distance in [cm] traveled by the particle -void score_tracklength_tally(const Particle* p, double distance); +void score_tracklength_tally(Particle* p, double distance); //! Score surface or mesh-surface tallies for particle currents. // diff --git a/include/openmc/thermal.h b/include/openmc/thermal.h index 624cf31bd..b737f005b 100644 --- a/include/openmc/thermal.h +++ b/include/openmc/thermal.h @@ -10,7 +10,7 @@ #include "xtensor/xtensor.hpp" #include "openmc/hdf5_interface.h" -#include "openmc/nuclide.h" +#include "openmc/particle.h" namespace openmc { diff --git a/openmc/capi/__init__.py b/openmc/capi/__init__.py index 1015df216..5ca867d67 100644 --- a/openmc/capi/__init__.py +++ b/openmc/capi/__init__.py @@ -42,7 +42,6 @@ else: def _dagmc_enabled(): return c_bool.in_dll(_dll, "dagmc_enabled").value - from .error import * from .core import * from .nuclide import * @@ -53,3 +52,4 @@ from .filter import * from .tally import * from .settings import settings from .math import * +from .plot import * diff --git a/openmc/capi/core.py b/openmc/capi/core.py index c637ee28f..9884ee027 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -17,7 +17,8 @@ class _Bank(Structure): ('u', c_double*3), ('E', c_double), ('wgt', c_double), - ('delayed_group', c_int)] + ('delayed_group', c_int), + ('particle', c_int)] # Define input type for numpy arrays that will be passed into C++ functions diff --git a/openmc/capi/plot.py b/openmc/capi/plot.py new file mode 100644 index 000000000..9ff398f5c --- /dev/null +++ b/openmc/capi/plot.py @@ -0,0 +1,245 @@ +from ctypes import c_int, c_size_t, c_int32, c_double, Structure, POINTER + +from . import _dll +from .error import _error_handler + +import numpy as np + + +class _Position(Structure): + """Definition of an xyz location in space with underlying c-types + + C-type Attributes + ----------------- + x : c_double + Position's x value (default: 0.0) + y : c_double + Position's y value (default: 0.0) + z : c_double + Position's z value (default: 0.0) + """ + _fields_ = [('x', c_double), + ('y', c_double), + ('z', c_double)] + + def __getitem__(self, idx): + if idx == 0: + return self.x + elif idx == 1: + return self.y + elif idx == 2: + return self.z + else: + raise IndexError("{} index is invalid for _Position".format(idx)) + + def __setitem__(self, idx, val): + if idx == 0: + self.x = val + elif idx == 1: + self.y = val + elif idx == 2: + self.z = val + else: + raise IndexError("{} index is invalid for _Position".format(idx)) + + def __repr__(self): + return "({}, {}, {})".format(self.x, self.y, self.z) + + +class _PlotBase(Structure): + """A structure defining a 2-D geometry slice with underlying c-types + + C-Type Attributes + ----------------- + origin : openmc.capi.plot._Position + A position defining the origin of the plot. + width_ : openmc.capi.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. + pixels_ : c_size_t[3] + The resolution of the plot in the horizontal and vertical dimensions + level_ : c_int + The universe level for the plot view + + Attributes + ---------- + origin : tuple or list of ndarray + Origin (center) of the plot + width : float + The horizontal dimension of the plot in geometry units (cm) + height : float + The vertical dimension of the plot in geometry units (cm) + basis : string + One of {'xy', 'xz', 'yz'} indicating the horizontal and vertical + axes of the plot. + h_res : int + The horizontal resolution of the plot in pixels + v_res : int + The vertical resolution of the plot in pixels + level : int + The universe level for the plot (default: -1 -> all universes shown) + """ + _fields_ = [('origin_', _Position), + ('width_', _Position), + ('basis_', c_int), + ('pixels_', 3*c_size_t), + ('level_', c_int)] + + def __init__(self): + self.level_ = -1 + + @property + def origin(self): + return self.origin_ + + @property + def width(self): + return self.width_.x + + @property + def height(self): + return self.width_.y + + @property + def basis(self): + if self.basis_ == 1: + return 'xy' + elif self.basis_ == 2: + return 'xz' + elif self.basis_ == 3: + return 'yz' + + raise ValueError("Plot basis {} is invalid".format(self.basis_)) + + @property + def h_res(self): + return self.pixels_[0] + + @property + def v_res(self): + return self.pixels_[1] + + @property + def level(self): + return int(self.level_) + + @origin.setter + def origin(self, origin): + self.origin_.x = origin[0] + self.origin_.y = origin[1] + self.origin_.z = origin[2] + + @width.setter + def width(self, width): + self.width_.x = width + + @height.setter + def height(self, height): + self.width_.y = height + + @basis.setter + def basis(self, basis): + if isinstance(basis, str): + valid_bases = ('xy', 'xz', 'yz') + basis = basis.lower() + if basis not in valid_bases: + raise ValueError("{} is not a valid plot basis.".format(basis)) + + if basis == 'xy': + self.basis_ = 1 + elif basis == 'xz': + self.basis_ = 2 + elif basis == 'yz': + self.basis_ = 3 + return + + if isinstance(basis, int): + valid_bases = (1, 2, 3) + if basis not in valid_bases: + raise ValueError("{} is not a valid plot basis.".format(basis)) + self.basis_ = basis + return + + raise ValueError("{} of type {} is an" + " invalid plot basis".format(basis, type(basis))) + + @h_res.setter + def h_res(self, h_res): + self.pixels_[0] = h_res + + @v_res.setter + def v_res(self, v_res): + self.pixels_[1] = v_res + + @level.setter + def level(self, level): + self.level_ = level + + def __repr__(self): + out_str = ["-----", + "Plot:", + "-----", + "Origin: {}".format(self.origin), + "Width: {}".format(self.width), + "Height: {}".format(self.height), + "Basis: {}".format(self.basis), + "HRes: {}".format(self.h_res), + "VRes: {}".format(self.v_res), + "Level: {}".format(self.level)] + return '\n'.join(out_str) + + +_dll.openmc_id_map.argtypes = [POINTER(_PlotBase), POINTER(c_int32)] +_dll.openmc_id_map.restype = c_int +_dll.openmc_id_map.errcheck = _error_handler + + +def id_map(plot): + """ + Generate a 2-D map of cell and material IDs. Used for in-memory image + generation. + + Parameters + ---------- + plot : openmc.capi.plot._PlotBase + Object describing the slice of the model to be generated + + Returns + ------- + id_map : numpy.ndarray + A NumPy array with shape (vertical pixels, horizontal pixels, 2) of + OpenMC property ids with dtype int32 + + """ + img_data = np.zeros((plot.v_res, plot.h_res, 2), + dtype=np.dtype('int32')) + _dll.openmc_id_map(plot, img_data.ctypes.data_as(POINTER(c_int32))) + return img_data + + +_dll.openmc_property_map.argtypes = [POINTER(_PlotBase), POINTER(c_double)] +_dll.openmc_property_map.restype = c_int +_dll.openmc_property_map.errcheck = _error_handler + + +def property_map(plot): + """ + Generate a 2-D map of cell temperatures and material densities. Used for + in-memory image generation. + + Parameters + ---------- + plot : openmc.capi.plot._PlotBase + Object describing the slice of the model to be generated + + Returns + ------- + property_map : numpy.ndarray + A NumPy array with shape (vertical pixels, horizontal pixels, 2) of + OpenMC property ids with dtype float + + """ + prop_data = np.zeros((plot.v_res, plot.h_res, 2)) + _dll.openmc_property_map(plot, prop_data.ctypes.data_as(POINTER(c_double))) + return prop_data diff --git a/openmc/cell.py b/openmc/cell.py index 8503deffa..8381fa8d8 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -571,7 +571,10 @@ class Cell(IDManagerMixin): # Check for other attributes t = get_text(elem, 'temperature') if t is not None: - c.temperature = float(t) + if ' ' in t: + c.temperature = [float(t_i) for t_i in t.split()] + else: + c.temperature = float(t) for key in ('temperature', 'rotation', 'translation'): value = get_text(elem, key) if value is not None: diff --git a/openmc/data/angle_distribution.py b/openmc/data/angle_distribution.py index a1f498ba6..638590cff 100644 --- a/openmc/data/angle_distribution.py +++ b/openmc/data/angle_distribution.py @@ -115,7 +115,7 @@ class AngleDistribution(EqualityMixin): Angular distribution """ - energy = group['energy'].value + energy = group['energy'][()] data = group['mu'] offsets = data.attrs['offsets'] interpolation = data.attrs['interpolation'] diff --git a/openmc/data/correlated.py b/openmc/data/correlated.py index 8cc4509ce..b760f2e2f 100644 --- a/openmc/data/correlated.py +++ b/openmc/data/correlated.py @@ -210,15 +210,15 @@ class CorrelatedAngleEnergy(AngleEnergy): interp_data = group['energy'].attrs['interpolation'] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'].value + energy = group['energy'][()] offsets = group['energy_out'].attrs['offsets'] interpolation = group['energy_out'].attrs['interpolation'] n_discrete_lines = group['energy_out'].attrs['n_discrete_lines'] - dset_eout = group['energy_out'].value + dset_eout = group['energy_out'][()] energy_out = [] - dset_mu = group['mu'].value + dset_mu = group['mu'][()] mu = [] n_energy = len(energy) diff --git a/openmc/data/density_effect.h5 b/openmc/data/density_effect.h5 new file mode 100644 index 000000000..77d973517 Binary files /dev/null and b/openmc/data/density_effect.h5 differ diff --git a/openmc/data/endf.py b/openmc/data/endf.py index 8e5fcf1a3..6131f3c6b 100644 --- a/openmc/data/endf.py +++ b/openmc/data/endf.py @@ -66,7 +66,7 @@ SUM_RULES = {1: [2, 3], 106: list(range(750, 800)), 107: list(range(800, 850))} -ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)') +ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]) ?(\d+)') def float_endf(s): @@ -89,12 +89,15 @@ def float_endf(s): The number """ - return float(ENDF_FLOAT_RE.sub(r'\1e\2', s)) + return float(ENDF_FLOAT_RE.sub(r'\1e\2\3', s)) -def _int_endf(s): - """Convert string to int. Used for INTG records where blank entries - indicate a 0. +def int_endf(s): + """Convert string of integer number in ENDF to int. + + The ENDF-6 format technically allows integers to be represented by a field + of all blanks. This function acts like int(s) except when s is a string of + all whitespace, in which case zero is returned. Parameters ---------- @@ -106,8 +109,7 @@ def _int_endf(s): integer The number or 0 """ - s = s.strip() - return int(s) if s else 0 + return 0 if s.isspace() else int(s) def get_text_record(file_obj): @@ -127,35 +129,35 @@ def get_text_record(file_obj): return file_obj.readline()[:66] -def get_cont_record(file_obj, skipC=False): +def get_cont_record(file_obj, skip_c=False): """Return data from a CONT record in an ENDF-6 file. Parameters ---------- file_obj : file-like object ENDF-6 file to read from - skipC : bool + skip_c : bool Determine whether to skip the first two quantities (C1, C2) of the CONT record. Returns ------- - list + tuple The six items within the CONT record """ line = file_obj.readline() - if skipC: + if skip_c: C1 = None C2 = None else: C1 = float_endf(line[:11]) C2 = float_endf(line[11:22]) - L1 = int(line[22:33]) - L2 = int(line[33:44]) - N1 = int(line[44:55]) - N2 = int(line[55:66]) - return [C1, C2, L1, L2, N1, N2] + L1 = int_endf(line[22:33]) + L2 = int_endf(line[33:44]) + N1 = int_endf(line[44:55]) + N2 = int_endf(line[55:66]) + return (C1, C2, L1, L2, N1, N2) def get_head_record(file_obj): @@ -168,18 +170,18 @@ def get_head_record(file_obj): Returns ------- - list + tuple The six items within the HEAD record """ line = file_obj.readline() ZA = int(float_endf(line[:11])) AWR = float_endf(line[11:22]) - L1 = int(line[22:33]) - L2 = int(line[33:44]) - N1 = int(line[44:55]) - N2 = int(line[55:66]) - return [ZA, AWR, L1, L2, N1, N2] + L1 = int_endf(line[22:33]) + L2 = int_endf(line[33:44]) + N1 = int_endf(line[44:55]) + N2 = int_endf(line[55:66]) + return (ZA, AWR, L1, L2, N1, N2) def get_list_record(file_obj): @@ -233,10 +235,10 @@ def get_tab1_record(file_obj): line = file_obj.readline() C1 = float_endf(line[:11]) C2 = float_endf(line[11:22]) - L1 = int(line[22:33]) - L2 = int(line[33:44]) - n_regions = int(line[44:55]) - n_pairs = int(line[55:66]) + L1 = int_endf(line[22:33]) + L2 = int_endf(line[33:44]) + n_regions = int_endf(line[44:55]) + n_pairs = int_endf(line[55:66]) params = [C1, C2, L1, L2] # Read the interpolation region data, namely NBT and INT @@ -247,8 +249,8 @@ def get_tab1_record(file_obj): line = file_obj.readline() to_read = min(3, n_regions - m) for j in range(to_read): - breakpoints[m] = int(line[0:11]) - interpolation[m] = int(line[11:22]) + breakpoints[m] = int_endf(line[0:11]) + interpolation[m] = int_endf(line[11:22]) line = line[22:] m += 1 @@ -306,9 +308,9 @@ def get_intg_record(file_obj): """ # determine how many items are in list and NDIGIT items = get_cont_record(file_obj) - ndigit = int(items[2]) - npar = int(items[3]) # Number of parameters - nlines = int(items[4]) # Lines to read + ndigit = items[2] + npar = items[3] # Number of parameters + nlines = items[4] # Lines to read NROW_RULES = {2: 18, 3: 12, 4: 11, 5: 9, 6: 8} nrow = NROW_RULES[ndigit] @@ -316,13 +318,13 @@ def get_intg_record(file_obj): corr = np.identity(npar) for i in range(nlines): line = file_obj.readline() - ii = _int_endf(line[:5]) - 1 # -1 to account for 0 indexing - jj = _int_endf(line[5:10]) - 1 + ii = int_endf(line[:5]) - 1 # -1 to account for 0 indexing + jj = int_endf(line[5:10]) - 1 factor = 10**ndigit for j in range(nrow): if jj+j >= ii: break - element = _int_endf(line[11+(ndigit+1)*j:11+(ndigit+1)*(j+1)]) + element = int_endf(line[11+(ndigit+1)*j:11+(ndigit+1)*(j+1)]) if element > 0: corr[ii, jj] = (element+0.5)/factor elif element < 0: @@ -507,16 +509,7 @@ class Evaluation(object): # File numbers, reaction designations, and number of records for i in range(NXC): - line = file_obj.readline() - mf = int(line[22:33]) - mt = int(line[33:44]) - nc = int(line[44:55]) - try: - mod = int(line[55:66]) - except ValueError: - # In JEFF 3.2, a few isotopes of U have MOD values that are - # missing. This prevents failure on these isotopes. - mod = 0 + _, _, mf, mt, nc, mod = get_cont_record(file_obj, skip_c=True) self.reaction_list.append((mf, mt, nc, mod)) @property diff --git a/openmc/data/energy_distribution.py b/openmc/data/energy_distribution.py index 9e01a4b30..bff97bf81 100644 --- a/openmc/data/energy_distribution.py +++ b/openmc/data/energy_distribution.py @@ -1144,7 +1144,7 @@ class ContinuousTabular(EnergyDistribution): interp_data = group['energy'].attrs['interpolation'] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'].value + energy = group['energy'][()] data = group['distribution'] offsets = data.attrs['offsets'] diff --git a/openmc/data/function.py b/openmc/data/function.py index 36443e761..f167a3c23 100644 --- a/openmc/data/function.py +++ b/openmc/data/function.py @@ -349,8 +349,8 @@ class Tabulated1D(Function1D): raise ValueError("Expected an HDF5 attribute 'type' equal to '" + cls.__name__ + "'") - x = dataset.value[0, :] - y = dataset.value[1, :] + x = dataset[0, :] + y = dataset[1, :] breakpoints = dataset.attrs['breakpoints'] interpolation = dataset.attrs['interpolation'] return cls(x, y, breakpoints, interpolation) @@ -434,7 +434,7 @@ class Polynomial(np.polynomial.Polynomial, Function1D): if dataset.attrs['type'].decode() != cls.__name__: raise ValueError("Expected an HDF5 attribute 'type' equal to '" + cls.__name__ + "'") - return cls(dataset.value) + return cls(dataset[()]) class Combination(EqualityMixin): diff --git a/openmc/data/kalbach_mann.py b/openmc/data/kalbach_mann.py index 4be0c15d5..c7df015a7 100644 --- a/openmc/data/kalbach_mann.py +++ b/openmc/data/kalbach_mann.py @@ -202,7 +202,7 @@ class KalbachMann(AngleEnergy): interp_data = group['energy'].attrs['interpolation'] energy_breakpoints = interp_data[0, :] energy_interpolation = interp_data[1, :] - energy = group['energy'].value + energy = group['energy'][()] data = group['distribution'] offsets = data.attrs['offsets'] diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 9022061a0..6178161ef 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -356,24 +356,24 @@ class WindowedMultipole(EqualityMixin): # Read scalars. - out.spacing = group['spacing'].value - out.sqrtAWR = group['sqrtAWR'].value - out.E_min = group['E_min'].value - out.E_max = group['E_max'].value + out.spacing = group['spacing'][()] + out.sqrtAWR = group['sqrtAWR'][()] + out.E_min = group['E_min'][()] + out.E_max = group['E_max'][()] # Read arrays. err = "WMP '{}' array shape is not consistent with the '{}' array shape" - out.data = group['data'].value + out.data = group['data'][()] - out.windows = group['windows'].value + out.windows = group['windows'][()] - out.broaden_poly = group['broaden_poly'].value.astype(np.bool) + out.broaden_poly = group['broaden_poly'][...].astype(np.bool) if out.broaden_poly.shape[0] != out.windows.shape[0]: raise ValueError(err.format('broaden_poly', 'windows')) - out.curvefit = group['curvefit'].value + out.curvefit = group['curvefit'][()] if out.curvefit.shape[0] != out.windows.shape[0]: raise ValueError(err.format('curvefit', 'windows')) diff --git a/openmc/data/neutron.py b/openmc/data/neutron.py index eec083602..cd1e40604 100644 --- a/openmc/data/neutron.py +++ b/openmc/data/neutron.py @@ -448,11 +448,13 @@ class IncidentNeutron(EqualityMixin): for rx in self.reactions.values(): # Skip writing redundant reaction if it doesn't have photon # production or is a summed transmutation reaction. MT=4 is also - # sometimes needed for probability tables. + # sometimes needed for probability tables. Also write gas + # production, heating, and damage energy production. if rx.redundant: photon_rx = any(p.particle == 'photon' for p in rx.products) - transmutation_rx = (rx.mt in (16, 103, 104, 105, 106, 107)) - if not (photon_rx or transmutation_rx or rx.mt == 4): + keep_mts = (4, 16, 103, 104, 105, 106, 107, + 203, 204, 205, 206, 207, 301, 444) + if not (photon_rx or rx.mt in keep_mts): continue rx_group = rxs_group.create_group('reaction_{:03}'.format(rx.mt)) @@ -519,7 +521,7 @@ class IncidentNeutron(EqualityMixin): kTg = group['kTs'] kTs = [] for temp in kTg: - kTs.append(kTg[temp].value) + kTs.append(kTg[temp][()]) data = cls(name, atomic_number, mass_number, metastable, atomic_weight_ratio, kTs) @@ -527,7 +529,7 @@ class IncidentNeutron(EqualityMixin): # Read energy grid e_group = group['energy'] for temperature, dset in e_group.items(): - data.energy[temperature] = dset.value + data.energy[temperature] = dset[()] # Read reaction data rxs_group = group['reactions'] @@ -615,13 +617,14 @@ class IncidentNeutron(EqualityMixin): # Read energy grid n_energy = ace.nxs[3] - energy = ace.xss[ace.jxs[1]:ace.jxs[1] + n_energy]*EV_PER_MEV + i = ace.jxs[1] + energy = ace.xss[i : i + n_energy]*EV_PER_MEV data.energy[strT] = energy - total_xs = ace.xss[ace.jxs[1] + n_energy:ace.jxs[1] + 2 * n_energy] - absorption_xs = ace.xss[ace.jxs[1] + 2 * n_energy:ace.jxs[1] + - 3 * n_energy] + total_xs = ace.xss[i + n_energy : i + 2*n_energy] + absorption_xs = ace.xss[i + 2*n_energy : i + 3*n_energy] + heating_number = ace.xss[i + 4*n_energy : i + 5*n_energy]*EV_PER_MEV - # Create redundant reactions (total and absorption) + # Create redundant reactions (total, absorption, and heating) total = Reaction(1) total.xs[strT] = Tabulated1D(energy, total_xs) total.redundant = True @@ -633,13 +636,15 @@ class IncidentNeutron(EqualityMixin): absorption.redundant = True data.reactions[101] = absorption + heating = Reaction(301) + heating.xs[strT] = Tabulated1D(energy, heating_number*total_xs) + heating.redundant = True + data.reactions[301] = heating + # Read each reaction n_reaction = ace.nxs[4] + 1 for i in range(n_reaction): rx = Reaction.from_ace(ace, i) - # Don't include gas production / damage cross sections - if 200 < rx.mt < 219 or rx.mt == 444: - continue data.reactions[rx.mt] = rx # Some photon production reactions may be assigned to MTs that don't @@ -690,6 +695,8 @@ class IncidentNeutron(EqualityMixin): mts = data.get_reaction_components(rx.mt) if mts != [rx.mt]: rx.redundant = True + if rx.mt in (203, 204, 205, 206, 207, 444): + rx.redundant = True # Read unresolved resonance probability tables urr = ProbabilityTables.from_ace(ace) @@ -784,16 +791,19 @@ class IncidentNeutron(EqualityMixin): return data @classmethod - def from_njoy(cls, filename, temperatures=None, **kwargs): + def from_njoy(cls, filename, temperatures=None, evaluation=None, **kwargs): """Generate incident neutron data by running NJOY. Parameters ---------- filename : str - Path to ENDF evaluation + Path to ENDF file temperatures : iterable of float Temperatures in Kelvin to produce data at. If omitted, data is produced at room temperature (293.6 K) + evaluation : openmc.data.endf.Evaluation, optional + If the ENDF file contains multiple material evaluations, this + argument indicates which evaluation to use. **kwargs Keyword arguments passed to :func:`openmc.data.njoy.make_ace` @@ -808,6 +818,7 @@ class IncidentNeutron(EqualityMixin): ace_file = os.path.join(tmpdir, 'ace') xsdir_file = os.path.join(tmpdir, 'xsdir') pendf_file = os.path.join(tmpdir, 'pendf') + kwargs['evaluation'] = evaluation make_ace(filename, temperatures, ace_file, xsdir_file, pendf_file, **kwargs) @@ -818,7 +829,7 @@ class IncidentNeutron(EqualityMixin): data.add_temperature_from_ace(table) # Add fission energy release data - ev = Evaluation(filename) + ev = evaluation if evaluation is not None else Evaluation(filename) if (1, 458) in ev.section: data.fission_energy = FissionEnergyRelease.from_endf(ev, data) diff --git a/openmc/data/njoy.py b/openmc/data/njoy.py index 0c82f32f1..ddc1efb1c 100644 --- a/openmc/data/njoy.py +++ b/openmc/data/njoy.py @@ -72,8 +72,13 @@ broadr / %%%%%%%%%%%%%%%%%%%%%%% Doppler broaden XS %%%%%%%%%%%%%%%%%%%%%%%%%%%% _TEMPLATE_HEATR = """ heatr / %%%%%%%%%%%%%%%%%%%%%%%%% Add heating kerma %%%%%%%%%%%%%%%%%%%%%%%%%%%% {nendf} {nheatr_in} {nheatr} / -{mat} 3 / -302 318 402 / +{mat} 4 / +302 318 402 444 / +""" + +_TEMPLATE_GASPR = """ +gaspr / %%%%%%%%%%%%%%%%%%%%%%%%% Add gas production %%%%%%%%%%%%%%%%%%%%%%%%%%% +{nendf} {ngaspr_in} {ngaspr} / """ _TEMPLATE_PURR = """ @@ -186,7 +191,7 @@ def run(commands, tapein, tapeout, input_filename=None, stdout=False, def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): - """Generate ACE file from an ENDF file + """Generate pointwise ENDF file from an ENDF file Parameters ---------- @@ -211,8 +216,8 @@ def make_pendf(filename, pendf='pendf', error=0.001, stdout=False): def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None, - error=0.001, broadr=True, heatr=True, purr=True, acer=True, - **kwargs): + error=0.001, broadr=True, heatr=True, gaspr=True, purr=True, + acer=True, evaluation=None, **kwargs): """Generate incident neutron ACE file from an ENDF file Parameters @@ -234,10 +239,15 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None, Indicating whether to Doppler broaden XS when running NJOY heatr : bool, optional Indicating whether to add heating kerma when running NJOY + gaspr : bool, optional + Indicating whether to add gas production data when running NJOY purr : bool, optional Indicating whether to add probability table when running NJOY acer : bool, optional Indicating whether to generate ACE file when running NJOY + evaluation : openmc.data.endf.Evaluation, optional + If the ENDF file contains multiple material evaluations, this argument + indicates which evaluation should be used. **kwargs Keyword arguments passed to :func:`openmc.data.njoy.run` @@ -247,7 +257,7 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None, If the NJOY process returns with a non-zero status """ - ev = endf.Evaluation(filename) + ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material zsymam = ev.target['zsymam'] @@ -285,6 +295,13 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None, commands += _TEMPLATE_HEATR nlast = nheatr + # gaspr + if gaspr: + ngaspr_in = nlast + ngaspr = ngaspr_in + 1 + commands += _TEMPLATE_GASPR + nlast = ngaspr + # purr if purr: npurr_in = nlast @@ -338,7 +355,8 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None, def make_ace_thermal(filename, filename_thermal, temperatures=None, - ace='ace', xsdir='xsdir', error=0.001, **kwargs): + ace='ace', xsdir='xsdir', error=0.001, evaluation=None, + evaluation_thermal=None, **kwargs): """Generate thermal scattering ACE file from ENDF files Parameters @@ -356,6 +374,12 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, Path of xsdir file to write error : float, optional Fractional error tolerance for NJOY processing + evaluation : openmc.data.endf.Evaluation, optional + If the ENDF neutron sublibrary file contains multiple material + evaluations, this argument indicates which evaluation to use. + evaluation_thermal : openmc.data.endf.Evaluation, optional + If the ENDF thermal scattering sublibrary file contains multiple + material evaluations, this argument indicates which evaluation to use. **kwargs Keyword arguments passed to :func:`openmc.data.njoy.run` @@ -365,11 +389,12 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None, If the NJOY process returns with a non-zero status """ - ev = endf.Evaluation(filename) + ev = evaluation if evaluation is not None else endf.Evaluation(filename) mat = ev.material zsymam = ev.target['zsymam'] - ev_thermal = endf.Evaluation(filename_thermal) + ev_thermal = (evaluation_thermal if evaluation_thermal is not None + else endf.Evaluation(filename_thermal)) mat_thermal = ev_thermal.material zsymam_thermal = ev_thermal.target['zsymam'] diff --git a/openmc/data/photon.py b/openmc/data/photon.py index c73216b84..e038ca05d 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -19,71 +19,62 @@ from .endf import Evaluation, get_head_record, get_tab1_record, get_list_record from .function import Tabulated1D -_SUBSHELLS = ['K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', - 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'O1', 'O2', - 'O3', 'O4', 'O5', 'O6', 'O7', 'O8', 'O9', 'P1', 'P2', - 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11', - 'Q1', 'Q2', 'Q3'] - - -# Helper function to map designator to subshell string or None -def _subshell(i): - if i == 0: - return None - else: - return _SUBSHELLS[i - 1] - +# Electron subshell labels +_SUBSHELLS = [None, 'K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5', + 'N1', 'N2', 'N3', 'N4', 'N5', 'N6', 'N7', 'O1', 'O2', 'O3', + 'O4', 'O5', 'O6', 'O7', 'O8', 'O9', 'P1', 'P2', 'P3', 'P4', + 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11','Q1', 'Q2', 'Q3'] _REACTION_NAME = { - 501: 'Total photon interaction', - 502: 'Photon coherent scattering', - 504: 'Photon incoherent scattering', - 515: 'Pair production, electron field', - 516: 'Total pair production', - 517: 'Pair production, nuclear field', - 522: 'Photoelectric absorption', - 526: 'Electro-atomic scattering', - 527: 'Electro-atomic bremsstrahlung', - 528: 'Electro-atomic excitation', - 534: 'K (1s1/2) subshell photoelectric', - 535: 'L1 (2s1/2) subshell photoelectric', - 536: 'L2 (2p1/2) subshell photoelectric', - 537: 'L3 (2p3/2) subshell photoelectric', - 538: 'M1 (3s1/2) subshell photoelectric', - 539: 'M2 (3p1/2) subshell photoelectric', - 540: 'M3 (3p3/2) subshell photoelectric', - 541: 'M4 (3d3/2) subshell photoelectric', - 542: 'M5 (3d5/2) subshell photoelectric', - 543: 'N1 (4s1/2) subshell photoelectric', - 544: 'N2 (4p1/2) subshell photoelectric', - 545: 'N3 (4p3/2) subshell photoelectric', - 546: 'N4 (4d3/2) subshell photoelectric', - 547: 'N5 (4d5/2) subshell photoelectric', - 548: 'N6 (4f5/2) subshell photoelectric', - 549: 'N7 (4f7/2) subshell photoelectric', - 550: 'O1 (5s1/2) subshell photoelectric', - 551: 'O2 (5p1/2) subshell photoelectric', - 552: 'O3 (5p3/2) subshell photoelectric', - 553: 'O4 (5d3/2) subshell photoelectric', - 554: 'O5 (5d5/2) subshell photoelectric', - 555: 'O6 (5f5/2) subshell photoelectric', - 556: 'O7 (5f7/2) subshell photoelectric', - 557: 'O8 (5g7/2) subshell photoelectric', - 558: 'O9 (5g9/2) subshell photoelectric', - 559: 'P1 (6s1/2) subshell photoelectric', - 560: 'P2 (6p1/2) subshell photoelectric', - 561: 'P3 (6p3/2) subshell photoelectric', - 562: 'P4 (6d3/2) subshell photoelectric', - 563: 'P5 (6d5/2) subshell photoelectric', - 564: 'P6 (6f5/2) subshell photoelectric', - 565: 'P7 (6f7/2) subshell photoelectric', - 566: 'P8 (6g7/2) subshell photoelectric', - 567: 'P9 (6g9/2) subshell photoelectric', - 568: 'P10 (6h9/2) subshell photoelectric', - 569: 'P11 (6h11/2) subshell photoelectric', - 570: 'Q1 (7s1/2) subshell photoelectric', - 571: 'Q2 (7p1/2) subshell photoelectric', - 572: 'Q3 (7p3/2) subshell photoelectric' + 501: ('Total photon interaction', 'total'), + 502: ('Photon coherent scattering', 'coherent'), + 504: ('Photon incoherent scattering', 'incoherent'), + 515: ('Pair production, electron field', 'pair_production_electron'), + 516: ('Total pair production', 'pair_production_total'), + 517: ('Pair production, nuclear field', 'pair_production_nuclear'), + 522: ('Photoelectric absorption', 'photoelectric'), + 526: ('Electro-atomic scattering', 'electro_atomic_scat'), + 527: ('Electro-atomic bremsstrahlung', 'electro_atomic_brem'), + 528: ('Electro-atomic excitation', 'electro_atomic_excit'), + 534: ('K (1s1/2) subshell photoelectric', 'K'), + 535: ('L1 (2s1/2) subshell photoelectric', 'L1'), + 536: ('L2 (2p1/2) subshell photoelectric', 'L2'), + 537: ('L3 (2p3/2) subshell photoelectric', 'L3'), + 538: ('M1 (3s1/2) subshell photoelectric', 'M1'), + 539: ('M2 (3p1/2) subshell photoelectric', 'M2'), + 540: ('M3 (3p3/2) subshell photoelectric', 'M3'), + 541: ('M4 (3d3/2) subshell photoelectric', 'M4'), + 542: ('M5 (3d5/2) subshell photoelectric', 'M5'), + 543: ('N1 (4s1/2) subshell photoelectric', 'N1'), + 544: ('N2 (4p1/2) subshell photoelectric', 'N2'), + 545: ('N3 (4p3/2) subshell photoelectric', 'N3'), + 546: ('N4 (4d3/2) subshell photoelectric', 'N4'), + 547: ('N5 (4d5/2) subshell photoelectric', 'N5'), + 548: ('N6 (4f5/2) subshell photoelectric', 'N6'), + 549: ('N7 (4f7/2) subshell photoelectric', 'N7'), + 550: ('O1 (5s1/2) subshell photoelectric', 'O1'), + 551: ('O2 (5p1/2) subshell photoelectric', 'O2'), + 552: ('O3 (5p3/2) subshell photoelectric', 'O3'), + 553: ('O4 (5d3/2) subshell photoelectric', 'O4'), + 554: ('O5 (5d5/2) subshell photoelectric', 'O5'), + 555: ('O6 (5f5/2) subshell photoelectric', 'O6'), + 556: ('O7 (5f7/2) subshell photoelectric', 'O7'), + 557: ('O8 (5g7/2) subshell photoelectric', 'O8'), + 558: ('O9 (5g9/2) subshell photoelectric', 'O9'), + 559: ('P1 (6s1/2) subshell photoelectric', 'P1'), + 560: ('P2 (6p1/2) subshell photoelectric', 'P2'), + 561: ('P3 (6p3/2) subshell photoelectric', 'P3'), + 562: ('P4 (6d3/2) subshell photoelectric', 'P4'), + 563: ('P5 (6d5/2) subshell photoelectric', 'P5'), + 564: ('P6 (6f5/2) subshell photoelectric', 'P6'), + 565: ('P7 (6f7/2) subshell photoelectric', 'P7'), + 566: ('P8 (6g7/2) subshell photoelectric', 'P8'), + 567: ('P9 (6g9/2) subshell photoelectric', 'P9'), + 568: ('P10 (6h9/2) subshell photoelectric', 'P10'), + 569: ('P11 (6h11/2) subshell photoelectric', 'P11'), + 570: ('Q1 (7s1/2) subshell photoelectric', 'Q1'), + 571: ('Q2 (7p1/2) subshell photoelectric', 'Q2'), + 572: ('Q3 (7p3/2) subshell photoelectric', 'Q3') } # Compton profiles are read from a pre-generated HDF5 file when they are first @@ -92,18 +83,15 @@ _REACTION_NAME = { # is a 2D array with shape (n_shells, n_momentum_values) stored on the key Z _COMPTON_PROFILES = {} -# Stopping powers are read from a pre-generated HDF5 file when they are first -# needed. The dictionary stores an array of energy values at which the other -# quantities are tabulated with the key 'energy' and for each element has the -# mean excitation energy and arrays containing the collision stopping powers -# and radiative stopping powers stored on the key 'Z'. -_STOPPING_POWERS = {} - # Scaled bremsstrahlung DCSs are read from a data file provided by Selzter and # Berger when they are first needed. The dictionary stores an array of n # incident electron kinetic energies with key 'electron_energies', an array of # k reduced photon energies with key 'photon_energies', and the cross sections # for each element are in a 2D array with shape (n, k) stored on the key 'Z'. +# It also stores data used for calculating the density effect correction and +# stopping power, namely, the mean excitation energy with the key 'I', number +# of electrons per subshell with the key 'num_electrons', and binding energies +# with the key 'ionization_energy'. _BREMSSTRAHLUNG = {} @@ -228,7 +216,7 @@ class AtomicRelaxation(EqualityMixin): # Get shell designators n = ace.nxs[7] idx = ace.jxs[11] - shells = [_subshell(int(i)) for i in ace.xss[idx : idx+n]] + shells = [_SUBSHELLS[int(i)] for i in ace.xss[idx : idx+n]] # Get number of electrons for each shell idx = ace.jxs[12] @@ -248,8 +236,8 @@ class AtomicRelaxation(EqualityMixin): if n_transitions > 0: records = [] for j in range(n_transitions): - subj = _subshell(int(ace.xss[idx])) - subk = _subshell(int(ace.xss[idx + 1])) + subj = _SUBSHELLS[int(ace.xss[idx])] + subk = _SUBSHELLS[int(ace.xss[idx + 1])] etr = ace.xss[idx + 2]*EV_PER_MEV if j == 0: ftr = ace.xss[idx + 3] @@ -304,7 +292,7 @@ class AtomicRelaxation(EqualityMixin): # Read data for each subshell for i in range(n_subshells): params, list_items = get_list_record(file_obj) - subi = _subshell(int(params[0])) + subi = _SUBSHELLS[int(params[0])] n_transitions = int(params[5]) binding_energy[subi] = list_items[0] num_electrons[subi] = list_items[1] @@ -313,8 +301,8 @@ class AtomicRelaxation(EqualityMixin): # Read transition data records = [] for j in range(n_transitions): - subj = _subshell(int(list_items[6*(j+1)])) - subk = _subshell(int(list_items[6*(j+1) + 1])) + subj = _SUBSHELLS[int(list_items[6*(j+1)])] + subk = _SUBSHELLS[int(list_items[6*(j+1) + 1])] etr = list_items[6*(j+1) + 2] ftr = list_items[6*(j+1) + 3] records.append((subj, subk, etr, ftr)) @@ -326,8 +314,70 @@ class AtomicRelaxation(EqualityMixin): # Return instance of class return cls(binding_energy, num_electrons, transitions) - def to_hdf5(self, group): - raise NotImplementedError + @classmethod + def from_hdf5(cls, group): + """Generate atomic relaxation data from an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + + Returns + ------- + openmc.data.AtomicRelaxation + Atomic relaxation data + + """ + # Create data dictionaries + binding_energy = {} + num_electrons = {} + transitions = {} + + designators = [s.decode() for s in group.attrs['designators']] + columns = ['secondary', 'tertiary', 'energy (eV)', 'probability'] + for shell in designators: + # Shell group + sub_group = group[shell] + + # Read subshell binding energy and number of electrons + if 'binding_energy' in sub_group.attrs: + binding_energy[shell] = sub_group.attrs['binding_energy'] + if 'num_electrons' in sub_group.attrs: + num_electrons[shell] = sub_group.attrs['num_electrons'] + + # Read transition data + if 'transitions' in sub_group: + df = pd.DataFrame(sub_group['transitions'][()], + columns=columns) + # Replace float indexes back to subshell strings + df[columns[:2]] = df[columns[:2]].replace( + np.arange(float(len(_SUBSHELLS))), _SUBSHELLS) + transitions[shell] = df + + return cls(binding_energy, num_electrons, transitions) + + def to_hdf5(self, group, shell): + """Write atomic relaxation data to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + shell : str + The subshell to write data for + + """ + + # Write subshell binding energy and number of electrons + group.attrs['binding_energy'] = self.binding_energy[shell] + group.attrs['num_electrons'] = self.num_electrons[shell] + + # Write transition data with replacements + if shell in self.transitions: + df = self.transitions[shell].replace( + _SUBSHELLS, range(len(_SUBSHELLS))) + group.create_dataset('transitions', data=df.values.astype(float)) class IncidentPhoton(EqualityMixin): @@ -352,12 +402,16 @@ class IncidentPhoton(EqualityMixin): atomic_relaxation : openmc.data.AtomicRelaxation or None Atomic relaxation data bremsstrahlung : dict - Dictionary of bremsstrahlung DCS data with keys 'electron_energy' - (incident electron kinetic energy values in [eV]), 'photon_energy' - (ratio of the energy of the emitted photon to the incident electron - kinetic energy), and 'dcs' (cross section values in [b]). The cross - sections are in scaled form: :math:`(\beta^2/Z^2) E_k (d\sigma/dE_k)`, - where :math:`E_k` is the energy of the emitted photon. + Dictionary of bremsstrahlung data with keys 'I' (mean excitation energy + in [eV]), 'num_electrons' (number of electrons in each subshell), + 'ionization_energy' (ionization potential of each subshell), + 'electron_energy' (incident electron kinetic energy values in [eV]), + 'photon_energy' (ratio of the energy of the emitted photon to the + incident electron kinetic energy), and 'dcs' (cross section values in + [b]). The cross sections are in scaled form: :math:`(\beta^2/Z^2) E_k + (d\sigma/dE_k)`, where :math:`E_k` is the energy of the emitted photon. + A negative number of electrons in a subshell indicates conduction + electrons. compton_profiles : dict Dictionary of Compton profile data with keys 'num_electrons' (number of electrons in each subshell), 'binding_energy' (ionization potential of @@ -368,11 +422,6 @@ class IncidentPhoton(EqualityMixin): reactions : collections.OrderedDict Contains the cross sections for each photon reaction. The keys are MT values and the values are instances of :class:`PhotonReaction`. - stopping_powers : dict - Dictionary of stopping power data with keys 'energy' (in [eV]), 'I' (mean - excitation energy), 's_collision' (collision stopping power in - [eV cm\ :sup:`2`/g]), and 's_radiative' (radiative stopping power in - [eV cm\ :sup:`2`/g]) """ @@ -381,7 +430,6 @@ class IncidentPhoton(EqualityMixin): self._atomic_relaxation = None self.reactions = OrderedDict() self.compton_profiles = {} - self.stopping_powers = {} self.bremsstrahlung = {} def __contains__(self, mt): @@ -514,10 +562,13 @@ class IncidentPhoton(EqualityMixin): idx += n_energy # Copy binding energy - shell = _subshell(d) + shell = _SUBSHELLS[d] e = data.atomic_relaxation.binding_energy[shell] rx.subshell_binding_energy = e + # Add bremsstrahlung DCS data + data._add_bremsstrahlung() + return data @classmethod @@ -560,12 +611,12 @@ class IncidentPhoton(EqualityMixin): if not _COMPTON_PROFILES: filename = os.path.join(os.path.dirname(__file__), 'compton_profiles.h5') with h5py.File(filename, 'r') as f: - _COMPTON_PROFILES['pz'] = f['pz'].value + _COMPTON_PROFILES['pz'] = f['pz'][()] for i in range(1, 101): group = f['{:03}'.format(i)] - num_electrons = group['num_electrons'].value - binding_energy = group['binding_energy'].value*EV_PER_MEV - J = group['J'].value + num_electrons = group['num_electrons'][()] + binding_energy = group['binding_energy'][()]*EV_PER_MEV + J = group['J'][()] _COMPTON_PROFILES[i] = {'num_electrons': num_electrons, 'binding_energy': binding_energy, 'J': J} @@ -577,29 +628,189 @@ class IncidentPhoton(EqualityMixin): data.compton_profiles['binding_energy'] = profile['binding_energy'] data.compton_profiles['J'] = [Tabulated1D(pz, J_k) for J_k in profile['J']] - # Load stopping power data if it has not yet been loaded - if not _STOPPING_POWERS: - filename = os.path.join(os.path.dirname(__file__), 'stopping_powers.h5') - with h5py.File(filename, 'r') as f: - # Units are in MeV; convert to eV - _STOPPING_POWERS['energy'] = f['energy'].value*EV_PER_MEV - for i in range(1, 99): - group = f['{:03}'.format(i)] + # Add bremsstrahlung DCS data + data._add_bremsstrahlung() - # Units are in MeV cm^2/g; convert to eV cm^2/g - _STOPPING_POWERS[i] = { - 'I': group.attrs['I'], - 's_collision': group['s_collision'].value*EV_PER_MEV, - 's_radiative': group['s_radiative'].value*EV_PER_MEV - } + return data - # Add stopping power data - if Z < 99: - data.stopping_powers['energy'] = _STOPPING_POWERS['energy'] - data.stopping_powers.update(_STOPPING_POWERS[Z]) + @classmethod + def from_hdf5(cls, group_or_filename): + """Generate photon reaction from an HDF5 group + Parameters + ---------- + group_or_filename : h5py.Group or str + HDF5 group containing interaction data. If given as a string, it is + assumed to be the filename for the HDF5 file, and the first group is + used to read from. + + Returns + ------- + openmc.data.IncidentPhoton + Photon interaction data + + """ + if isinstance(group_or_filename, h5py.Group): + group = group_or_filename + else: + h5file = h5py.File(str(group_or_filename), 'r') + + # Make sure version matches + if 'version' in h5file.attrs: + major, minor = h5file.attrs['version'] + # For now all versions of HDF5 data can be read + else: + raise IOError( + 'HDF5 data does not indicate a version. Your installation ' + 'of the OpenMC Python API expects version {}.x data.' + .format(HDF5_VERSION_MAJOR)) + + group = list(h5file.values())[0] + + Z = group.attrs['Z'] + data = cls(Z) + + # Read energy grid + energy = group['energy'][()] + + # Read cross section data + for mt, (name, key) in _REACTION_NAME.items(): + if key in group: + rgroup = group[key] + elif key in group['subshells']: + rgroup = group['subshells'][key] + else: + continue + + data.reactions[mt] = PhotonReaction.from_hdf5(rgroup, mt, energy) + + # Check for necessary reactions + for mt in (502, 504, 522): + assert mt in data, "Reaction {} not found".format(mt) + + # Read atomic relaxation + data.atomic_relaxation = AtomicRelaxation.from_hdf5(group['subshells']) + + # Read Compton profiles + if 'compton_profiles' in group: + rgroup = group['compton_profiles'] + profile = data.compton_profiles + profile['num_electrons'] = rgroup['num_electrons'][()] + profile['binding_energy'] = rgroup['binding_energy'][()] + + # Get electron momentum values + pz = rgroup['pz'][()] + J = rgroup['J'][()] + if pz.size != J.shape[1]: + raise ValueError("'J' array shape is not consistent with the " + "'pz' array shape") + profile['J'] = [Tabulated1D(pz, Jk) for Jk in J] + + # Read bremsstrahlung + if 'bremsstrahlung' in group: + rgroup = group['bremsstrahlung'] + data.bremsstrahlung['I'] = rgroup.attrs['I'] + for key in ('dcs', 'electron_energy', 'ionization_energy', + 'num_electrons', 'photon_energy'): + data.bremsstrahlung[key] = rgroup[key][()] + + return data + + def export_to_hdf5(self, path, mode='a', libver='earliest'): + """Export incident photon data to an HDF5 file. + + Parameters + ---------- + path : str + Path to write HDF5 file to + mode : {'r', r+', 'w', 'x', 'a'} + Mode that is used to open the HDF5 file. This is the second argument + to the :class:`h5py.File` constructor. + libver : {'earliest', 'latest'} + Compatibility mode for the HDF5 file. 'latest' will produce files + that are less backwards compatible but have performance benefits. + + """ + # Open file and write version + f = h5py.File(str(path), mode, libver=libver) + f.attrs['filetype'] = np.string_('data_photon') + if 'version' not in f.attrs: + f.attrs['version'] = np.array(HDF5_VERSION) + + group = f.create_group(self.name) + group.attrs['Z'] = Z = self.atomic_number + + # Determine union energy grid + union_grid = np.array([]) + for rx in self: + union_grid = np.union1d(union_grid, rx.xs.x) + group.create_dataset('energy', data=union_grid) + + # Write cross sections + shell_group = group.create_group('subshells') + designators = [] + for mt, rx in self.reactions.items(): + name, key = _REACTION_NAME[mt] + if mt in [502, 504, 515, 517, 522]: + sub_group = group.create_group(key) + elif mt >= 534 and mt <= 572: + # Subshell + designators.append(key) + sub_group = shell_group.create_group(key) + + # Write atomic relaxation + if key in self.atomic_relaxation.subshells: + self.atomic_relaxation.to_hdf5(sub_group, key) + else: + continue + + rx.to_hdf5(sub_group, union_grid, Z) + + shell_group.attrs['designators'] = np.array(designators, dtype='S') + + # Write Compton profiles + if self.compton_profiles: + compton_group = group.create_group('compton_profiles') + + profile = self.compton_profiles + compton_group.create_dataset('num_electrons', + data=profile['num_electrons']) + compton_group.create_dataset('binding_energy', + data=profile['binding_energy']) + + # Get electron momentum values + compton_group.create_dataset('pz', data=profile['J'][0].x) + + # Create/write 2D array of profiles + J = np.array([Jk.y for Jk in profile['J']]) + compton_group.create_dataset('J', data=J) + + # Write bremsstrahlung + if self.bremsstrahlung: + brem_group = group.create_group('bremsstrahlung') + for key, value in self.bremsstrahlung.items(): + if key == 'I': + brem_group.attrs[key] = value + else: + brem_group.create_dataset(key, data=value) + + def _add_bremsstrahlung(self): + """Add the data used in the thick-target bremsstrahlung approximation + + """ # Load bremsstrahlung data if it has not yet been loaded if not _BREMSSTRAHLUNG: + # Add data used for density effect correction + filename = os.path.join(os.path.dirname(__file__), 'density_effect.h5') + with h5py.File(filename, 'r') as f: + for i in range(1, 101): + group = f['{:03}'.format(i)] + _BREMSSTRAHLUNG[i] = { + 'I': group.attrs['I'], + 'num_electrons': group['num_electrons'][()], + 'ionization_energy': group['ionization_energy'][()] + } + filename = os.path.join(os.path.dirname(__file__), 'BREMX.DAT') brem = open(filename, 'r').read().split() @@ -640,153 +851,12 @@ class IncidentPhoton(EqualityMixin): # Get scaled DCS values (millibarns) on new energy grid dcs[:,j] = cs(log_energy) - _BREMSSTRAHLUNG[i] = {'dcs': dcs} + _BREMSSTRAHLUNG[i]['dcs'] = dcs # Add bremsstrahlung DCS data - data.bremsstrahlung['electron_energy'] = _BREMSSTRAHLUNG['electron_energy'] - data.bremsstrahlung['photon_energy'] = _BREMSSTRAHLUNG['photon_energy'] - data.bremsstrahlung['dcs'] = _BREMSSTRAHLUNG[Z]['dcs'] - - return data - - def export_to_hdf5(self, path, mode='a', libver='earliest'): - """Export incident photon data to an HDF5 file. - - Parameters - ---------- - path : str - Path to write HDF5 file to - mode : {'r', r+', 'w', 'x', 'a'} - Mode that is used to open the HDF5 file. This is the second argument - to the :class:`h5py.File` constructor. - - """ - # Open file and write version - f = h5py.File(str(path), mode, libver=libver) - f.attrs['filetype'] = np.string_('data_photon') - if 'version' not in f.attrs: - f.attrs['version'] = np.array(HDF5_VERSION) - - group = f.create_group(self.name) - group.attrs['Z'] = Z = self.atomic_number - - # Determine union energy grid - union_grid = np.array([]) - for rx in self: - union_grid = np.union1d(union_grid, rx.xs.x) - group.create_dataset('energy', data=union_grid) - - # Write coherent scattering cross section - rx = self.reactions[502] - coh_group = group.create_group('coherent') - coh_group.create_dataset('xs', data=rx.xs(union_grid)) - if rx.scattering_factor is not None: - # Create integrated form factor - ff = deepcopy(rx.scattering_factor) - ff.x *= ff.x - ff.y *= ff.y/Z**2 - int_ff = Tabulated1D(ff.x, ff.integral()) - int_ff.to_hdf5(coh_group, 'integrated_scattering_factor') - if rx.anomalous_real is not None: - rx.anomalous_real.to_hdf5(coh_group, 'anomalous_real') - if rx.anomalous_imag is not None: - rx.anomalous_imag.to_hdf5(coh_group, 'anomalous_imag') - - # Write incoherent scattering cross section - rx = self[504] - incoh_group = group.create_group('incoherent') - incoh_group.create_dataset('xs', data=rx.xs(union_grid)) - if rx.scattering_factor is not None: - rx.scattering_factor.to_hdf5(incoh_group, 'scattering_factor') - - # Write electron-field pair production cross section - if 515 in self: - pair_group = group.create_group('pair_production_electron') - pair_group.create_dataset('xs', data=self[515].xs(union_grid)) - - # Write nuclear-field pair production cross section - if 517 in self: - pair_group = group.create_group('pair_production_nuclear') - pair_group.create_dataset('xs', data=self[517].xs(union_grid)) - - # Write photoelectric cross section - photoelec_group = group.create_group('photoelectric') - photoelec_group.create_dataset('xs', data=self[522].xs(union_grid)) - - # Write photoionization cross sections - shell_group = group.create_group('subshells') - designators = [] - for mt, rx in self.reactions.items(): - if mt >= 534 and mt <= 572: - # Get name of subshell - shell = _SUBSHELLS[mt - 534] - designators.append(shell) - sub_group = shell_group.create_group(shell) - - if self.atomic_relaxation is not None: - relax = self.atomic_relaxation - # Write subshell binding energy and number of electrons - sub_group.attrs['binding_energy'] = relax.binding_energy[shell] - sub_group.attrs['num_electrons'] = relax.num_electrons[shell] - - # Write transition data with replacements - if shell in relax.transitions: - shell_values = _SUBSHELLS.copy() - shell_values.insert(0, None) - df = relax.transitions[shell].replace( - shell_values, range(len(shell_values))) - sub_group.create_dataset( - 'transitions', data=df.values.astype(float)) - - # Determine threshold - threshold = rx.xs.x[0] - idx = np.searchsorted(union_grid, threshold, side='right') - 1 - - # Interpolate cross section onto union grid and write - photoionization = rx.xs(union_grid[idx:]) - sub_group.create_dataset('xs', data=photoionization) - assert len(union_grid) == len(photoionization) + idx - sub_group['xs'].attrs['threshold_idx'] = idx - - shell_group.attrs['designators'] = np.array(designators, dtype='S') - - # Write Compton profiles - if self.compton_profiles: - compton_group = group.create_group('compton_profiles') - - profile = self.compton_profiles - compton_group.create_dataset('num_electrons', - data=profile['num_electrons']) - compton_group.create_dataset('binding_energy', - data=profile['binding_energy']) - - # Get electron momentum values - compton_group.create_dataset('pz', data=profile['J'][0].x) - - # Create/write 2D array of profiles - J = np.array([Jk.y for Jk in profile['J']]) - compton_group.create_dataset('J', data=J) - - # Write stopping powers - if self.stopping_powers: - s_group = group.create_group('stopping_powers') - - for key, value in self.stopping_powers.items(): - if key == 'I': - s_group.attrs[key] = value - else: - s_group.create_dataset(key, data=value) - - # Write bremsstrahlung - if self.bremsstrahlung: - brem_group = group.create_group('bremsstrahlung') - - brem = self.bremsstrahlung - brem_group.create_dataset('electron_energy', - data=brem['electron_energy']) - brem_group.create_dataset('photon_energy', - data=brem['photon_energy']) - brem_group.create_dataset('dcs', data=brem['dcs']) + self.bremsstrahlung['electron_energy'] = _BREMSSTRAHLUNG['electron_energy'] + self.bremsstrahlung['photon_energy'] = _BREMSSTRAHLUNG['photon_energy'] + self.bremsstrahlung.update(_BREMSSTRAHLUNG[self.atomic_number]) class PhotonReaction(EqualityMixin): @@ -822,7 +892,7 @@ class PhotonReaction(EqualityMixin): def __repr__(self): if self.mt in _REACTION_NAME: return "".format( - self.mt, _REACTION_NAME[self.mt]) + self.mt, _REACTION_NAME[self.mt][0]) else: return "".format(self.mt) @@ -999,3 +1069,93 @@ class PhotonReaction(EqualityMixin): params, rx.anomalous_imag = get_tab1_record(file_obj) return rx + + @classmethod + def from_hdf5(cls, group, mt, energy): + """Generate photon reaction from an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to read from + mt : int + The MT value of the reaction to get data for + energy : Iterable of float + arrays of energies at which cross sections are tabulated at + + Returns + ------- + openmc.data.PhotonReaction + Photon reaction data + + """ + # Create instance + rx = cls(mt) + + # Cross sections + xs = group['xs'][()] + # Replace zero elements to small non-zero to enable log-log + xs[xs == 0.0] = np.exp(-500.0) + + # Threshold + threshold_idx = 0 + if 'threshold_idx' in group['xs'].attrs: + threshold_idx = group['xs'].attrs['threshold_idx'] + + # Store cross section + rx.xs = Tabulated1D(energy[threshold_idx:], xs, [len(xs)], [5]) + + # Check for anomalous scattering factor + if 'anomalous_real' in group: + rx.anomalous_real = Tabulated1D.from_hdf5(group['anomalous_real']) + if 'anomalous_imag' in group: + rx.anomalous_imag = Tabulated1D.from_hdf5(group['anomalous_imag']) + + # Check for factors / scattering functions + if 'scattering_factor' in group: + rx.scattering_factor = Tabulated1D.from_hdf5(group['scattering_factor']) + + return rx + + def to_hdf5(self, group, energy, Z): + """Write photon reaction to an HDF5 group + + Parameters + ---------- + group : h5py.Group + HDF5 group to write to + energy : Iterable of float + arrays of energies at which cross sections are tabulated at + Z : int + atomic number + + """ + + # Write cross sections + if self.mt >= 534 and self.mt <= 572: + # Determine threshold + threshold = self.xs.x[0] + idx = np.searchsorted(energy, threshold, side='right') - 1 + + # Interpolate cross section onto union grid and write + photoionization = self.xs(energy[idx:]) + group.create_dataset('xs', data=photoionization) + assert len(energy) == len(photoionization) + idx + group['xs'].attrs['threshold_idx'] = idx + else: + group.create_dataset('xs', data=self.xs(energy)) + + # Write scattering factor + if self.scattering_factor is not None: + if self.mt == 502: + # Create integrated form factor + ff = deepcopy(self.scattering_factor) + ff.x *= ff.x + ff.y *= ff.y/Z**2 + int_ff = Tabulated1D(ff.x, ff.integral()) + int_ff.to_hdf5(group, 'integrated_scattering_factor') + self.scattering_factor.to_hdf5(group, 'scattering_factor') + if self.anomalous_real is not None: + self.anomalous_real.to_hdf5(group, 'anomalous_real') + if self.anomalous_imag is not None: + self.anomalous_imag.to_hdf5(group, 'anomalous_imag') diff --git a/openmc/data/reaction.py b/openmc/data/reaction.py index 11807cadc..c5abc73c3 100644 --- a/openmc/data/reaction.py +++ b/openmc/data/reaction.py @@ -51,7 +51,9 @@ REACTION_NAME = {1: '(n,total)', 2: '(n,elastic)', 4: '(n,level)', 189: '(n,nta)', 190: '(n,2n2p)', 191: '(n,p3He)', 192: '(n,d3He)', 193: '(n,3Hea)', 194: '(n,4n2p)', 195: '(n,4n2a)', 196: '(n,4npa)', 197: '(n,3p)', - 198: '(n,n3p)', 199: '(n,3n2pa)', 200: '(n,5n2p)', 444: '(n,damage)', + 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', 649: '(n,pc)', 699: '(n,dc)', 749: '(n,tc)', 799: '(n,3Hec)', 849: '(n,ac)', 891: '(n,2nc)'} REACTION_NAME.update({i: '(n,n{})'.format(i - 50) for i in range(50, 91)}) @@ -936,7 +938,7 @@ class Reaction(EqualityMixin): 'Could not create reaction cross section for MT={} ' 'at T={} because no corresponding energy grid ' 'exists.'.format(mt, T)) - xs = Tgroup['xs'].value + xs = Tgroup['xs'][()] threshold_idx = Tgroup['xs'].attrs['threshold_idx'] - 1 tabulated_xs = Tabulated1D(energy[T][threshold_idx:], xs) tabulated_xs._threshold_idx = threshold_idx @@ -988,6 +990,10 @@ class Reaction(EqualityMixin): # Read reaction cross section xs = ace.xss[ace.jxs[7] + loc + 1:ace.jxs[7] + loc + 1 + n_energy] + # For damage energy production, convert to eV + if mt == 444: + xs *= EV_PER_MEV + # Fix negatives -- known issue for Y89 in JEFF 3.2 if np.any(xs < 0.0): warn("Negative cross sections found for MT={} in {}. Setting " diff --git a/openmc/data/stopping_powers.h5 b/openmc/data/stopping_powers.h5 deleted file mode 100644 index 4bc5b1388..000000000 Binary files a/openmc/data/stopping_powers.h5 and /dev/null differ diff --git a/openmc/data/thermal.py b/openmc/data/thermal.py index f6ce6326b..27ed7aa29 100644 --- a/openmc/data/thermal.py +++ b/openmc/data/thermal.py @@ -200,8 +200,8 @@ class CoherentElastic(EqualityMixin): Coherent elastic scattering cross section """ - bragg_edges = dataset.value[0, :] - factors = dataset.value[1, :] + bragg_edges = dataset[0, :] + factors = dataset[1, :] return cls(bragg_edges, factors) @@ -414,7 +414,7 @@ class ThermalScattering(EqualityMixin): kTg = group['kTs'] kTs = [] for temp in kTg: - kTs.append(kTg[temp].value) + kTs.append(kTg[temp][()]) temperatures = [str(int(round(kT / K_BOLTZMANN))) + "K" for kT in kTs] table = cls(name, atomic_weight_ratio, kTs) @@ -438,7 +438,7 @@ class ThermalScattering(EqualityMixin): # Angular distribution if 'mu_out' in elastic_group: - table.elastic_mu_out[T] = elastic_group['mu_out'].value + table.elastic_mu_out[T] = elastic_group['mu_out'][()] # Read thermal inelastic scattering if 'inelastic' in Tgroup: @@ -446,8 +446,8 @@ class ThermalScattering(EqualityMixin): table.inelastic_xs[T] = Tabulated1D.from_hdf5( inelastic_group['xs']) if table.secondary_mode in ('equal', 'skewed'): - table.inelastic_e_out[T] = inelastic_group['energy_out'].value - table.inelastic_mu_out[T] = inelastic_group['mu_out'].value + table.inelastic_e_out[T] = inelastic_group['energy_out'][()] + table.inelastic_mu_out[T] = inelastic_group['mu_out'][()] elif table.secondary_mode == 'continuous': table.inelastic_dist[T] = AngleEnergy.from_hdf5( inelastic_group) @@ -610,7 +610,8 @@ class ThermalScattering(EqualityMixin): return table @classmethod - def from_njoy(cls, filename, filename_thermal, temperatures=None, **kwargs): + def from_njoy(cls, filename, filename_thermal, temperatures=None, + evaluation=None, evaluation_thermal=None, **kwargs): """Generate incident neutron data by running NJOY. Parameters @@ -623,6 +624,13 @@ class ThermalScattering(EqualityMixin): Temperatures in Kelvin to produce data at. If omitted, data is produced at all temperatures in the ENDF thermal scattering sublibrary. + evaluation : openmc.data.endf.Evaluation, optional + If the ENDF neutron sublibrary file contains multiple material + evaluations, this argument indicates which evaluation to use. + evaluation_thermal : openmc.data.endf.Evaluation, optional + If the ENDF thermal scattering sublibrary file contains multiple + material evaluations, this argument indicates which evaluation to + use. **kwargs Keyword arguments passed to :func:`openmc.data.njoy.make_ace_thermal` @@ -636,6 +644,8 @@ class ThermalScattering(EqualityMixin): # Run NJOY to create an ACE library ace_file = os.path.join(tmpdir, 'ace') xsdir_file = os.path.join(tmpdir, 'xsdir') + kwargs['evaluation'] = evaluation + kwargs['evaluation_thermal'] = evaluation_thermal make_ace_thermal(filename, filename_thermal, temperatures, ace_file, xsdir_file, **kwargs) diff --git a/openmc/data/urr.py b/openmc/data/urr.py index 0edccf6f0..53961a50a 100644 --- a/openmc/data/urr.py +++ b/openmc/data/urr.py @@ -166,8 +166,8 @@ class ProbabilityTables(EqualityMixin): absorption_flag = group.attrs['absorption'] multiply_smooth = bool(group.attrs['multiply_smooth']) - energy = group['energy'].value - table = group['table'].value + energy = group['energy'][()] + table = group['table'][()] return cls(energy, table, interpolation, inelastic_flag, absorption_flag, multiply_smooth) diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index 58e4b66dc..9fb6eec86 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -20,7 +20,7 @@ class ResultsList(list): check_filetype_version(fh, 'depletion results', _VERSION_RESULTS[0]) # Get number of results stored - n = fh["number"].value.shape[0] + n = fh["number"][...].shape[0] for i in range(n): self.append(Results.from_hdf5(fh, i)) diff --git a/openmc/examples.py b/openmc/examples.py index a5138377e..4e8c8cf92 100644 --- a/openmc/examples.py +++ b/openmc/examples.py @@ -51,8 +51,8 @@ def pwr_pin_cell(): # Instantiate ZCylinder surfaces pitch = 1.26 - fuel_or = openmc.ZCylinder(x0=0, y0=0, R=0.39218, name='Fuel OR') - clad_or = openmc.ZCylinder(x0=0, y0=0, R=0.45720, name='Clad OR') + fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name='Fuel OR') + clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name='Clad OR') left = openmc.XPlane(x0=-pitch/2, name='left', boundary_type='reflective') right = openmc.XPlane(x0=pitch/2, name='right', boundary_type='reflective') bottom = openmc.YPlane(y0=-pitch/2, name='bottom', @@ -256,14 +256,14 @@ def pwr_core(): bot_nozzle, top_nozzle, top_fa, bot_fa) # Define surfaces. - s1 = openmc.ZCylinder(R=0.41, surface_id=1) - s2 = openmc.ZCylinder(R=0.475, surface_id=2) - s3 = openmc.ZCylinder(R=0.56, surface_id=3) - s4 = openmc.ZCylinder(R=0.62, surface_id=4) - s5 = openmc.ZCylinder(R=187.6, surface_id=5) - s6 = openmc.ZCylinder(R=209.0, surface_id=6) - s7 = openmc.ZCylinder(R=229.0, surface_id=7) - s8 = openmc.ZCylinder(R=249.0, surface_id=8, boundary_type='vacuum') + s1 = openmc.ZCylinder(r=0.41, surface_id=1) + s2 = openmc.ZCylinder(r=0.475, surface_id=2) + s3 = openmc.ZCylinder(r=0.56, surface_id=3) + s4 = openmc.ZCylinder(r=0.62, surface_id=4) + s5 = openmc.ZCylinder(r=187.6, surface_id=5) + s6 = openmc.ZCylinder(r=209.0, surface_id=6) + s7 = openmc.ZCylinder(r=229.0, surface_id=7) + s8 = openmc.ZCylinder(r=249.0, surface_id=8, boundary_type='vacuum') s31 = openmc.ZPlane(z0=-229.0, surface_id=31, boundary_type='vacuum') s32 = openmc.ZPlane(z0=-199.0, surface_id=32) @@ -473,8 +473,8 @@ def pwr_assembly(): model.materials = (fuel, clad, hot_water) # Instantiate ZCylinder surfaces - fuel_or = openmc.ZCylinder(x0=0, y0=0, R=0.39218, name='Fuel OR') - clad_or = openmc.ZCylinder(x0=0, y0=0, R=0.45720, name='Clad OR') + fuel_or = openmc.ZCylinder(x0=0, y0=0, r=0.39218, name='Fuel OR') + clad_or = openmc.ZCylinder(x0=0, y0=0, r=0.45720, name='Clad OR') # Create boundary planes to surround the geometry pitch = 21.42 diff --git a/openmc/filter.py b/openmc/filter.py index ef68a4961..49d3ff5de 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -170,19 +170,19 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): # If the HDF5 'type' variable matches this class's short_name, then # there is no overriden from_hdf5 method. Pass the bins to __init__. - if group['type'].value.decode() == cls.short_name.lower(): - out = cls(group['bins'].value, filter_id=filter_id) - out._num_bins = group['n_bins'].value + if group['type'][()].decode() == cls.short_name.lower(): + out = cls(group['bins'][()], filter_id=filter_id) + out._num_bins = group['n_bins'][()] return out # Search through all subclasses and find the one matching the HDF5 # 'type'. Call that class's from_hdf5 method. for subclass in cls._recursive_subclasses(): - if group['type'].value.decode() == subclass.short_name.lower(): + if group['type'][()].decode() == subclass.short_name.lower(): return subclass.from_hdf5(group, **kwargs) raise ValueError("Unrecognized Filter class: '" - + group['type'].value.decode() + "'") + + group['type'][()].decode() + "'") @property def bins(self): @@ -618,16 +618,16 @@ class MeshFilter(Filter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") if 'meshes' not in kwargs: raise ValueError(cls.__name__ + " requires a 'meshes' keyword " "argument.") - mesh_id = group['bins'].value + mesh_id = group['bins'][()] mesh_obj = kwargs['meshes'][mesh_id] filter_id = int(group.name.split('/')[-1].lstrip('filter ')) @@ -1191,15 +1191,15 @@ class DistribcellFilter(Filter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - out = cls(group['bins'].value, filter_id=filter_id) - out._num_bins = group['n_bins'].value + out = cls(group['bins'][()], filter_id=filter_id) + out._num_bins = group['n_bins'][()] return out @@ -1638,13 +1638,13 @@ class EnergyFunctionFilter(Filter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") - energy = group['energy'].value - y = group['y'].value + energy = group['energy'][()] + y = group['y'][()] filter_id = int(group.name.split('/')[-1].lstrip('filter ')) return cls(energy, y, filter_id=filter_id) diff --git a/openmc/filter_expansion.py b/openmc/filter_expansion.py index 59457b685..cc085df8a 100644 --- a/openmc/filter_expansion.py +++ b/openmc/filter_expansion.py @@ -92,14 +92,14 @@ class LegendreFilter(ExpansionFilter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - out = cls(group['order'].value, filter_id) + out = cls(group['order'][()], filter_id) return out @@ -198,15 +198,15 @@ class SpatialLegendreFilter(ExpansionFilter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'].value - axis = group['axis'].value.decode() - min_, max_ = group['min'].value, group['max'].value + order = group['order'][()] + axis = group['axis'][()].decode() + min_, max_ = group['min'][()], group['max'][()] return cls(order, axis, min_, max_, filter_id) @@ -294,15 +294,15 @@ class SphericalHarmonicsFilter(ExpansionFilter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - out = cls(group['order'].value, filter_id) - out.cosine = group['cosine'].value.decode() + out = cls(group['order'][()], filter_id) + out.cosine = group['cosine'][()].decode() return out @@ -437,14 +437,14 @@ class ZernikeFilter(ExpansionFilter): @classmethod def from_hdf5(cls, group, **kwargs): - if group['type'].value.decode() != cls.short_name.lower(): + if group['type'][()].decode() != cls.short_name.lower(): raise ValueError("Expected HDF5 data for filter type '" + cls.short_name.lower() + "' but got '" - + group['type'].value.decode() + " instead") + + group['type'][()].decode() + " instead") filter_id = int(group.name.split('/')[-1].lstrip('filter ')) - order = group['order'].value - x, y, r = group['x'].value, group['y'].value, group['r'].value + order = group['order'][()] + x, y, r = group['x'][()], group['y'][()], group['r'][()] return cls(order, x, y, r, filter_id) diff --git a/openmc/geometry.py b/openmc/geometry.py index e939ecbe3..260bb9e83 100644 --- a/openmc/geometry.py +++ b/openmc/geometry.py @@ -97,9 +97,14 @@ class Geometry(object): # Clean the indentation in the file to be user-readable xml.clean_indentation(root_element) + # Check if path is a directory + p = Path(path) + if p.is_dir(): + p /= 'geometry.xml' + # Write the XML Tree to the geometry.xml file tree = ET.ElementTree(root_element) - tree.write(path, xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod def from_xml(cls, path='geometry.xml', materials=None): diff --git a/openmc/lattice.py b/openmc/lattice.py index 8bf4e4757..fb0a7a1bd 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -100,14 +100,14 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): """ lattice_id = int(group.name.split('/')[-1].lstrip('lattice ')) - name = group['name'].value.decode() if 'name' in group else '' - lattice_type = group['type'].value.decode() + name = group['name'][()].decode() if 'name' in group else '' + lattice_type = group['type'][()].decode() if lattice_type == 'rectangular': dimension = group['dimension'][...] lower_left = group['lower_left'][...] pitch = group['pitch'][...] - outer = group['outer'].value + outer = group['outer'][()] universe_ids = group['universes'][...] # Create the Lattice @@ -136,13 +136,13 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): lattice.universes = uarray elif lattice_type == 'hexagonal': - n_rings = group['n_rings'].value - n_axial = group['n_axial'].value - center = group['center'][...] - pitch = group['pitch'][...] - outer = group['outer'].value + n_rings = group['n_rings'][()] + n_axial = group['n_axial'][()] + center = group['center'][()] + pitch = group['pitch'][()] + outer = group['outer'][()] - universe_ids = group['universes'][...] + universe_ids = group['universes'][()] # Create the Lattice lattice = openmc.HexLattice(lattice_id, name) diff --git a/openmc/material.py b/openmc/material.py index f8f122308..55aef743c 100644 --- a/openmc/material.py +++ b/openmc/material.py @@ -1,6 +1,7 @@ from collections import OrderedDict from copy import deepcopy from numbers import Real, Integral +from pathlib import Path import warnings from xml.etree import ElementTree as ET @@ -276,10 +277,10 @@ class Material(IDManagerMixin): """ mat_id = int(group.name.split('/')[-1].lstrip('material ')) - name = group['name'].value.decode() if 'name' in group else '' - density = group['atom_density'].value + name = group['name'][()].decode() if 'name' in group else '' + density = group['atom_density'][()] if 'nuclide_densities' in group: - nuc_densities = group['nuclide_densities'][...] + nuc_densities = group['nuclide_densities'][()] # Create the Material material = cls(mat_id, name) @@ -289,7 +290,7 @@ class Material(IDManagerMixin): # Read the names of the S(a,b) tables for this Material and add them if 'sab_names' in group: - sab_tables = group['sab_names'].value + sab_tables = group['sab_names'][()] for sab_table in sab_tables: name = sab_table.decode() material.add_s_alpha_beta(name) @@ -298,13 +299,13 @@ class Material(IDManagerMixin): material.set_density(density=density, units='atom/b-cm') if 'nuclides' in group: - nuclides = group['nuclides'].value + nuclides = group['nuclides'][()] # Add all nuclides to the Material for fullname, density in zip(nuclides, nuc_densities): name = fullname.decode().strip() material.add_nuclide(name, percent=density, percent_type='ao') if 'macroscopics' in group: - macroscopics = group['macroscopics'].value + macroscopics = group['macroscopics'][()] # Add all macroscopics to the Material for fullname in macroscopics: name = fullname.decode().strip() @@ -1065,9 +1066,14 @@ class Materials(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(root_element) + # Check if path is a directory + p = Path(path) + if p.is_dir(): + p /= 'materials.xml' + # Write the XML Tree to the materials.xml file tree = ET.ElementTree(root_element) - tree.write(path, xml_declaration=True, encoding='utf-8') + tree.write(str(p), xml_declaration=True, encoding='utf-8') @classmethod def from_xml(cls, path='materials.xml'): diff --git a/openmc/mesh.py b/openmc/mesh.py index f454a6f88..ba07ec178 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -174,11 +174,11 @@ class Mesh(IDManagerMixin): # Read and assign mesh properties mesh = cls(mesh_id) - mesh.type = group['type'].value.decode() - mesh.dimension = group['dimension'].value - mesh.lower_left = group['lower_left'].value - mesh.upper_right = group['upper_right'].value - mesh.width = group['width'].value + mesh.type = group['type'][()].decode() + mesh.dimension = group['dimension'][()] + mesh.lower_left = group['lower_left'][()] + mesh.upper_right = group['upper_right'][()] + mesh.width = group['width'][()] return mesh diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index b3ce9438c..e505c7ebc 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -6,26 +6,50 @@ from openmc.mgxs.mgxs import * from openmc.mgxs.mdgxs import * GROUP_STRUCTURES = {} -"""Dictionary of commonly used energy group structures, including "CASMO-X" (where X -is 2, 4, 8, 16, 25, 40 or 70) from the CASMO_ lattice physics code and other commonly -used activation_ energy group structures "VITAMIN-J-175", "TRIPOLI-315", "CCFE-709_" -and "UKAEA-1102_" +"""Dictionary of commonly used energy group structures: +- "CASMO-X" (where X is 2, 4, 8, 16, 25, 40 or 70) from the CASMO_ lattice + physics code +- "XMAS-172_" designed for LWR analysis ([SAR1990]_, [SAN2004]_) +- "SHEM-361_" designed for LWR analysis to eliminate self-shielding calculations + of thermal resonances ([HFA2005]_, [SAN2007]_, [HEB2008]_) +- activation_ energy group structures "VITAMIN-J-175", "TRIPOLI-315", + "CCFE-709_" and "UKAEA-1102_" .. _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 .. _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 +.. [SAR1990] Sartori, E., OECD/NEA Data Bank: Standard Energy Group Structures + of Cross Section Libraries for Reactor Shielding, Reactor Cell and Fusion + Neutronics Applications: VITAMIN-J, ECCO-33, ECCO-2000 and XMAS JEF/DOC-315 + Revision 3 - DRAFT (December 11, 1990). +.. [SAN2004] Santamarina, A., Collignon, C., & Garat, C. (2004). French + calculation schemes for light water reactor analysis. United States: + American Nuclear Society - ANS. +.. [HFA2005] Hfaiedh, N. & Santamarina, A., "Determination of the Optimized + SHEM Mesh for Neutron Transport Calculations," Proc. Top. Mtg. in + Mathematics & Computations, Supercomputing, Reactor Physics and Nuclear and + Biological Applications, September 12-15, Avignon, France, 2005. +.. [SAN2007] Santamarina, A. & Hfaiedh, N. (2007). The SHEM energy mesh for + accurate fuel depletion and BUC calculations. Proceedings of the International + Conference on Safety Criticality ICNC 2007, St Peterburg (Russia), Vol. I pp. + 446-452. +.. [HEB2008] Hébert, Alain & Santamarina, Alain. (2008). Refinement of the + Santamarina-Hfaiedh energy mesh between 22.5 eV and 11.4 keV. International + Conference on the Physics of Reactors 2008, PHYSOR 08. 2. 929-938. """ GROUP_STRUCTURES['CASMO-2'] = np.array([ - 0., 6.25e-1, 2.e7]) + 0., 6.25e-1, 2.e7]) GROUP_STRUCTURES['CASMO-4'] = np.array([ - 0., 6.25e-1, 5.53e3, 8.21e5, 2.e7]) + 0., 6.25e-1, 5.53e3, 8.21e5, 2.e7]) GROUP_STRUCTURES['CASMO-8'] = np.array([ - 0., 5.8e-2, 1.4e-1, 2.8e-1, 6.25e-1, 4., 5.53e3, 8.21e5, 2.e7]) + 0., 5.8e-2, 1.4e-1, 2.8e-1, 6.25e-1, 4., 5.53e3, 8.21e5, 2.e7]) GROUP_STRUCTURES['CASMO-16'] = np.array([ - 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 8.5e-1, - 9.72e-1, 1.02, 1.097, 1.15, 1.3, 4., 5.53e3, 8.21e5, 2.e7]) + 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 8.5e-1, + 9.72e-1, 1.02, 1.097, 1.15, 1.3, 4., 5.53e3, 8.21e5, 2.e7]) GROUP_STRUCTURES['CASMO-25'] = np.array([ 0., 3.e-2, 5.8e-2, 1.4e-1, 2.8e-1, 3.5e-1, 6.25e-1, 9.72e-1, 1.02, 1.097, 1.15, 1.855, 4., 9.877, 1.5968e1, 1.4873e2, 5.53e3, 9.118e3, 1.11e5, 5.e5, @@ -46,6 +70,42 @@ GROUP_STRUCTURES['CASMO-70'] = np.array([ 3.6726e2, 9.069e2, 1.4251e3, 2.2395e3, 3.5191e3, 5.53e3, 9.118e3, 1.503e4, 2.478e4, 4.085e4, 6.734e4, 1.11e5, 1.83e5, 3.025e5, 5.e5, 8.21e5, 1.353e6, 2.231e6, 3.679e6, 6.0655e6, 2.e7]) +GROUP_STRUCTURES['XMAS-172'] = np.array([ + 1.00001e-05, 3.00000e-03, 5.00000e-03, 6.90000e-03, 1.00000e-02, + 1.50000e-02, 2.00000e-02, 2.50000e-02, 3.00000e-02, 3.50000e-02, + 4.20000e-02, 5.00000e-02, 5.80000e-02, 6.70000e-02, 7.70000e-02, + 8.00000e-02, 9.50000e-02, 1.00001e-01, 1.15000e-01, 1.34000e-01, + 1.40000e-01, 1.60000e-01, 1.80000e-01, 1.89000e-01, 2.20000e-01, + 2.48000e-01, 2.80000e-01, 3.00000e-01, 3.14500e-01, 3.20000e-01, + 3.50000e-01, 3.91000e-01, 4.00000e-01, 4.33000e-01, 4.85000e-01, + 5.00000e-01, 5.40000e-01, 6.25000e-01, 7.05000e-01, 7.80000e-01, + 7.90000e-01, 8.50000e-01, 8.60000e-01, 9.10000e-01, 9.30000e-01, + 9.50000e-01, 9.72000e-01, 9.86000e-01, 9.96000e-01, 1.02000e+00, + 1.03500e+00, 1.04500e+00, 1.07100e+00, 1.09700e+00, 1.11000e+00, + 1.12535e+00, 1.15000e+00, 1.17000e+00, 1.23500e+00, 1.30000e+00, + 1.33750e+00, 1.37000e+00, 1.44498e+00, 1.47500e+00, 1.50000e+00, + 1.59000e+00, 1.67000e+00, 1.75500e+00, 1.84000e+00, 1.93000e+00, + 2.02000e+00, 2.10000e+00, 2.13000e+00, 2.36000e+00, 2.55000e+00, + 2.60000e+00, 2.72000e+00, 2.76792e+00, 3.30000e+00, 3.38075e+00, + 4.00000e+00, 4.12925e+00, 5.04348e+00, 5.34643e+00, 6.16012e+00, + 7.52398e+00, 8.31529e+00, 9.18981e+00, 9.90555e+00, 1.12245e+01, + 1.37096e+01, 1.59283e+01, 1.94548e+01, 2.26033e+01, 2.49805e+01, + 2.76077e+01, 3.05113e+01, 3.37201e+01, 3.72665e+01, 4.01690e+01, + 4.55174e+01, 4.82516e+01, 5.15780e+01, 5.55951e+01, 6.79041e+01, + 7.56736e+01, 9.16609e+01, 1.36742e+02, 1.48625e+02, 2.03995e+02, + 3.04325e+02, 3.71703e+02, 4.53999e+02, 6.77287e+02, 7.48518e+02, + 9.14242e+02, 1.01039e+03, 1.23410e+03, 1.43382e+03, 1.50733e+03, + 2.03468e+03, 2.24867e+03, 3.35463e+03, 3.52662e+03, 5.00451e+03, + 5.53084e+03, 7.46586e+03, 9.11882e+03, 1.11378e+04, 1.50344e+04, + 1.66156e+04, 2.47875e+04, 2.73944e+04, 2.92830e+04, 3.69786e+04, + 4.08677e+04, 5.51656e+04, 6.73795e+04, 8.22975e+04, 1.11090e+05, + 1.22773e+05, 1.83156e+05, 2.47235e+05, 2.73237e+05, 3.01974e+05, + 4.07622e+05, 4.50492e+05, 4.97871e+05, 5.50232e+05, 6.08101e+05, + 8.20850e+05, 9.07180e+05, 1.00259e+06, 1.10803e+06, 1.22456e+06, + 1.35335e+06, 1.65299e+06, 2.01897e+06, 2.23130e+06, 2.46597e+06, + 3.01194e+06, 3.67879e+06, 4.49329e+06, 5.48812e+06, 6.06531e+06, + 6.70320e+06, 8.18731e+06, 1.00000e+07, 1.16183e+07, 1.38403e+07, + 1.49182e+07, 1.73325e+07, 1.96403e+07]) GROUP_STRUCTURES['VITAMIN-J-175'] = np.array([ 1.0000e-5, 1.0000e-1, 4.1399e-1, 5.3158e-1, 6.8256e-1, 8.7643e-1, 1.1253, 1.4450, 1.8554, 2.3824, 3.0590, @@ -123,6 +183,80 @@ GROUP_STRUCTURES['TRIPOLI-315,'] = np.array([ 7.788e6, 8.187e6, 8.607e6, 9.048e6, 9.512e6, 1.000e7, 1.051e7, 1.105e7, 1.162e7, 1.221e7, 1.284e7, 1.350e7, 1.384e7, 1.419e7, 1.455e7, 1.492e7, 1.568e7, 1.649e7, 1.691e7, 1.733e7, 1.964e7]) +GROUP_STRUCTURES['SHEM-361'] = np.array([ + 0.00000e+00, 2.49990e-03, 4.55602e-03, 7.14526e-03, 1.04505e-02, + 1.48300e-02, 2.00104e-02, 2.49394e-02, 2.92989e-02, 3.43998e-02, + 4.02999e-02, 4.73019e-02, 5.54982e-02, 6.51999e-02, 7.64969e-02, + 8.97968e-02, 1.04298e-01, 1.19995e-01, 1.37999e-01, 1.61895e-01, + 1.90005e-01, 2.09610e-01, 2.31192e-01, 2.54997e-01, 2.79989e-01, + 3.05012e-01, 3.25008e-01, 3.52994e-01, 3.90001e-01, 4.31579e-01, + 4.75017e-01, 5.20011e-01, 5.54990e-01, 5.94993e-01, 6.24999e-01, + 7.19999e-01, 8.00371e-01, 8.80024e-01, 9.19978e-01, 9.44022e-01, + 9.63960e-01, 9.81959e-01, 9.96501e-01, 1.00904e+00, 1.02101e+00, + 1.03499e+00, 1.07799e+00, 1.09198e+00, 1.10395e+00, 1.11605e+00, + 1.12997e+00, 1.14797e+00, 1.16999e+00, 1.21397e+00, 1.25094e+00, + 1.29304e+00, 1.33095e+00, 1.38098e+00, 1.41001e+00, 1.44397e+00, + 1.51998e+00, 1.58803e+00, 1.66895e+00, 1.77997e+00, 1.90008e+00, + 1.98992e+00, 2.07010e+00, 2.15695e+00, 2.21709e+00, 2.27299e+00, + 2.33006e+00, 2.46994e+00, 2.55000e+00, 2.59009e+00, 2.62005e+00, + 2.64004e+00, 2.70012e+00, 2.71990e+00, 2.74092e+00, 2.77512e+00, + 2.88405e+00, 3.14211e+00, 3.54307e+00, 3.71209e+00, 3.88217e+00, + 4.00000e+00, 4.21983e+00, 4.30981e+00, 4.41980e+00, 4.76785e+00, + 4.93323e+00, 5.10997e+00, 5.21008e+00, 5.32011e+00, 5.38003e+00, + 5.41025e+00, 5.48817e+00, 5.53004e+00, 5.61979e+00, 5.72015e+00, + 5.80021e+00, 5.96014e+00, 6.05991e+00, 6.16011e+00, 6.28016e+00, + 6.35978e+00, 6.43206e+00, 6.48178e+00, 6.51492e+00, 6.53907e+00, + 6.55609e+00, 6.57184e+00, 6.58829e+00, 6.60611e+00, 6.63126e+00, + 6.71668e+00, 6.74225e+00, 6.75981e+00, 6.77605e+00, 6.79165e+00, + 6.81070e+00, 6.83526e+00, 6.87021e+00, 6.91778e+00, 6.99429e+00, + 7.13987e+00, 7.38015e+00, 7.60035e+00, 7.73994e+00, 7.83965e+00, + 7.97008e+00, 8.13027e+00, 8.30032e+00, 8.52407e+00, 8.67369e+00, + 8.80038e+00, 8.97995e+00, 9.14031e+00, 9.50002e+00, 1.05793e+01, + 1.08038e+01, 1.10529e+01, 1.12694e+01, 1.15894e+01, 1.17094e+01, + 1.18153e+01, 1.19795e+01, 1.21302e+01, 1.23086e+01, 1.24721e+01, + 1.26000e+01, 1.33297e+01, 1.35460e+01, 1.40496e+01, 1.42505e+01, + 1.44702e+01, 1.45952e+01, 1.47301e+01, 1.48662e+01, 1.57792e+01, + 1.60498e+01, 1.65501e+01, 1.68305e+01, 1.74457e+01, 1.75648e+01, + 1.77590e+01, 1.79591e+01, 1.90848e+01, 1.91997e+01, 1.93927e+01, + 1.95974e+01, 2.00734e+01, 2.02751e+01, 2.04175e+01, 2.05199e+01, + 2.06021e+01, 2.06847e+01, 2.07676e+01, 2.09763e+01, 2.10604e+01, + 2.11448e+01, 2.12296e+01, 2.13360e+01, 2.14859e+01, 2.17018e+01, + 2.20011e+01, 2.21557e+01, 2.23788e+01, 2.25356e+01, 2.46578e+01, + 2.78852e+01, 3.16930e+01, 3.30855e+01, 3.45392e+01, 3.56980e+01, + 3.60568e+01, 3.64191e+01, 3.68588e+01, 3.73038e+01, 3.77919e+01, + 3.87874e+01, 3.97295e+01, 4.12270e+01, 4.21441e+01, 4.31246e+01, + 4.41721e+01, 4.52904e+01, 4.62053e+01, 4.75173e+01, 4.92591e+01, + 5.17847e+01, 5.29895e+01, 5.40600e+01, 5.70595e+01, 5.99250e+01, + 6.23083e+01, 6.36306e+01, 6.45923e+01, 6.50460e+01, 6.55029e+01, + 6.58312e+01, 6.61612e+01, 6.64929e+01, 6.68261e+01, 6.90682e+01, + 7.18869e+01, 7.35595e+01, 7.63322e+01, 7.93679e+01, 8.39393e+01, + 8.87741e+01, 9.33256e+01, 9.73287e+01, 1.00594e+02, 1.01098e+02, + 1.01605e+02, 1.02115e+02, 1.03038e+02, 1.05646e+02, 1.10288e+02, + 1.12854e+02, 1.15480e+02, 1.16524e+02, 1.17577e+02, 1.20554e+02, + 1.26229e+02, 1.32701e+02, 1.39504e+02, 1.46657e+02, 1.54176e+02, + 1.63056e+02, 1.67519e+02, 1.75229e+02, 1.83295e+02, 1.84952e+02, + 1.86251e+02, 1.87559e+02, 1.88877e+02, 1.90204e+02, 1.93078e+02, + 1.95996e+02, 2.00958e+02, 2.12108e+02, 2.24325e+02, 2.35590e+02, + 2.41796e+02, 2.56748e+02, 2.68297e+02, 2.76468e+02, 2.84888e+02, + 2.88327e+02, 2.95922e+02, 3.19928e+02, 3.35323e+02, 3.53575e+02, + 3.71703e+02, 3.90760e+02, 4.19094e+02, 4.53999e+02, 5.01746e+02, + 5.39204e+02, 5.77146e+02, 5.92941e+02, 6.00099e+02, 6.12834e+02, + 6.46837e+02, 6.77287e+02, 7.48517e+02, 8.32218e+02, 9.09681e+02, + 9.82494e+02, 1.06432e+03, 1.13467e+03, 1.34358e+03, 1.58620e+03, + 1.81183e+03, 2.08410e+03, 2.39729e+03, 2.70024e+03, 2.99618e+03, + 3.48107e+03, 4.09735e+03, 5.00451e+03, 6.11252e+03, 7.46585e+03, + 9.11881e+03, 1.11377e+04, 1.36037e+04, 1.48997e+04, 1.62005e+04, + 1.85847e+04, 2.26994e+04, 2.49991e+04, 2.61001e+04, 2.73944e+04, + 2.92810e+04, 3.34596e+04, 3.69786e+04, 4.08677e+04, 4.99159e+04, + 5.51656e+04, 6.73794e+04, 8.22974e+04, 9.46645e+04, 1.15624e+05, + 1.22773e+05, 1.40000e+05, 1.64999e+05, 1.95008e+05, 2.30014e+05, + 2.67826e+05, 3.20646e+05, 3.83884e+05, 4.12501e+05, 4.56021e+05, + 4.94002e+05, 5.78443e+05, 7.06511e+05, 8.60006e+05, 9.51119e+05, + 1.05115e+06, 1.16205e+06, 1.28696e+06, 1.33694e+06, 1.40577e+06, + 1.63654e+06, 1.90139e+06, 2.23130e+06, 2.72531e+06, 3.32871e+06, + 4.06569e+06, 4.96585e+06, 6.06530e+06, 6.70319e+06, 7.40817e+06, + 8.18730e+06, 9.04836e+06, 9.99999e+06, 1.16183e+07, 1.38403e+07, + 1.49182e+07, 1.96403e+07]) GROUP_STRUCTURES['CCFE-709'] = np.array([ 1.e-5, 1.0471e-5, 1.0965e-5, 1.1482e-5, 1.2023e-5, 1.2589e-5, 1.3183e-5, 1.3804e-5, 1.4454e-5, 1.5136e-5, diff --git a/openmc/mgxs_library.py b/openmc/mgxs_library.py index 51c91ef17..b98902030 100644 --- a/openmc/mgxs_library.py +++ b/openmc/mgxs_library.py @@ -2183,7 +2183,7 @@ class XSdata(object): kTs_group = group['kTs'] float_temperatures = [] for temperature in temperatures: - kT = kTs_group[temperature].value + kT = kTs_group[temperature][()] float_temperatures.append(kT / openmc.data.K_BOLTZMANN) attrs = group.attrs.keys() @@ -2219,7 +2219,7 @@ class XSdata(object): for xs_type in xs_types: set_func = 'set_' + xs_type.replace(' ', '_').replace('-', '_') if xs_type in temperature_group: - getattr(data, set_func)(temperature_group[xs_type].value, + getattr(data, set_func)(temperature_group[xs_type][()], float_temp) scatt_group = temperature_group['scatter_data'] @@ -2227,7 +2227,7 @@ class XSdata(object): # Get scatter matrix and 'un-flatten' it g_max = scatt_group['g_max'] g_min = scatt_group['g_min'] - flat_scatter = scatt_group['scatter_matrix'].value + flat_scatter = scatt_group['scatter_matrix'][()] scatter_matrix = np.zeros(data.xs_shapes["[G][G'][Order]"]) G = data.energy_groups.num_groups if data.representation == 'isotropic': @@ -2259,7 +2259,7 @@ class XSdata(object): # Repeat for multiplicity if 'multiplicity_matrix' in scatt_group: - flat_mult = scatt_group['multiplicity_matrix'].value + flat_mult = scatt_group['multiplicity_matrix'][()] mult_matrix = np.zeros(data.xs_shapes["[G][G']"]) flat_index = 0 for p in range(Np): diff --git a/openmc/model/funcs.py b/openmc/model/funcs.py index e974f93b1..5f2db4d30 100644 --- a/openmc/model/funcs.py +++ b/openmc/model/funcs.py @@ -2,8 +2,9 @@ from collections import OrderedDict from collections.abc import Iterable from math import sqrt from numbers import Real +from functools import partial -from openmc import XPlane, YPlane, Plane, ZCylinder +from openmc import XPlane, YPlane, Plane, ZCylinder, Quadric from openmc.checkvalue import check_type, check_value import openmc.data @@ -237,16 +238,16 @@ def get_hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), c = sqrt(3.)/3. # y = -x/sqrt(3) + a - upper_right = Plane(A=c, B=1., D=l+x*c+y, boundary_type=boundary_type) + upper_right = Plane(a=c, b=1., d=l+x*c+y, boundary_type=boundary_type) # y = x/sqrt(3) + a - upper_left = Plane(A=-c, B=1., D=l-x*c+y, boundary_type=boundary_type) + upper_left = Plane(a=-c, b=1., d=l-x*c+y, boundary_type=boundary_type) # y = x/sqrt(3) - a - lower_right = Plane(A=-c, B=1., D=-l-x*c+y, boundary_type=boundary_type) + lower_right = Plane(a=-c, b=1., d=-l-x*c+y, boundary_type=boundary_type) # y = -x/sqrt(3) - a - lower_left = Plane(A=c, B=1., D=-l+x*c+y, boundary_type=boundary_type) + lower_left = Plane(a=c, b=1., d=-l+x*c+y, boundary_type=boundary_type) prism = -right & +left & -upper_right & -upper_left & \ +lower_right & +lower_left @@ -262,17 +263,17 @@ def get_hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), c = sqrt(3.) # y = -sqrt(3)*(x - a) - upper_right = Plane(A=c, B=1., D=c*l+x*c+y, boundary_type=boundary_type) + upper_right = Plane(a=c, b=1., d=c*l+x*c+y, boundary_type=boundary_type) # y = sqrt(3)*(x + a) - lower_right = Plane(A=-c, B=1., D=-c*l-x*c+y, + lower_right = Plane(a=-c, b=1., d=-c*l-x*c+y, boundary_type=boundary_type) # y = -sqrt(3)*(x + a) - lower_left = Plane(A=c, B=1., D=-c*l+x*c+y, boundary_type=boundary_type) + lower_left = Plane(a=c, b=1., d=-c*l+x*c+y, boundary_type=boundary_type) # y = sqrt(3)*(x + a) - upper_left = Plane(A=-c, B=1., D=c*l-x*c+y, boundary_type=boundary_type) + upper_left = Plane(a=-c, b=1., d=c*l-x*c+y, boundary_type=boundary_type) prism = -top & +bottom & -upper_right & +lower_right & \ +lower_left & -upper_left @@ -292,8 +293,8 @@ def get_hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), t = l - corner_radius/c # Cylinder with corner radius and boundary type pre-applied - cyl1 = partial(ZCylinder, R=corner_radius, boundary_type=boundary_type) - cyl2 = partial(ZCylinder, R=corner_radius/(2*c), + cyl1 = partial(ZCylinder, r=corner_radius, boundary_type=boundary_type) + cyl2 = partial(ZCylinder, r=corner_radius/(2*c), boundary_type=boundary_type) if orientation == 'x': @@ -345,6 +346,54 @@ def get_hexagonal_prism(edge_length=1., orientation='y', origin=(0., 0.), return prism +def cylinder_from_points(p1, p2, r, **kwargs): + """Return cylinder defined by two points passing through its center. + + Parameters + ---------- + p1, p2 : 3-tuples + Coordinates of two points that pass through the center of the cylinder + r : float + Radius of the cylinder + kwargs : dict + Keyword arguments passed to the :class:`openmc.Quadric` constructor + + Returns + ------- + openmc.Quadric + Quadric surface representing the cylinder. + + """ + # Get x, y, z coordinates of two points + x1, y1, z1 = p1 + x2, y2, z2 = p2 + + # Define intermediate terms + dx = x2 - x1 + dy = y2 - y1 + dz = z2 - z1 + cx = y1*z2 + y2*z1 + cy = -(x1*z2 + x2*z1) + cz = x1*y2 + x2*y1 + + # Given p=(x,y,z), p1=(x1, y1, z1), p2=(x2, y2, z2), the equation for the + # cylinder can be derived as r = |(p - p1) ⨯ (p - p2)| / |p2 - p1|. + # Expanding out all terms and grouping according to what Quadric expects + # gives the following coefficients. + kwargs['a'] = dy*dy + dz*dz + kwargs['b'] = dx*dx + dz*dz + kwargs['c'] = dx*dx + dy*dy + kwargs['d'] = -2*dx*dy + kwargs['e'] = -2*dy*dz + kwargs['f'] = -2*dx*dz + kwargs['g'] = cy*dz - cz*dy + kwargs['h'] = cz*dx - cx*dz + kwargs['j'] = cx*dy - cy*dx + kwargs['k'] = -(dx*dx + dy*dy + dz*dz)*r*r + + return openmc.Quadric(**kwargs) + + def subdivide(surfaces): """Create regions separated by a series of surfaces. diff --git a/openmc/model/model.py b/openmc/model/model.py index b6a89792d..cf6603638 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -1,4 +1,5 @@ from collections.abc import Iterable +from pathlib import Path import openmc from openmc.checkvalue import check_type, check_value @@ -154,27 +155,39 @@ class Model(object): 'si_celi', 'si_leqi', 'celi', 'leqi')) getattr(dep.integrator, method)(op, timesteps, **kwargs) - def export_to_xml(self): - """Export model to XML files.""" + def export_to_xml(self, directory='.'): + """Export model to XML files. - self.settings.export_to_xml() + Parameters + ---------- + directory : str + Directory to write XML files to. If it doesn't exist already, it + will be created. + + """ + # Create directory if + d = Path(directory) + if not d.is_dir(): + d.mkdir(parents=True) + + self.settings.export_to_xml(d) if not self.settings.dagmc: - self.geometry.export_to_xml() + self.geometry.export_to_xml(d) # If a materials collection was specified, export it. Otherwise, look # for all materials in the geometry and use that to automatically build # a collection. if self.materials: - self.materials.export_to_xml() + self.materials.export_to_xml(d) else: materials = openmc.Materials(self.geometry.get_all_materials() .values()) - materials.export_to_xml() + materials.export_to_xml(d) if self.tallies: - self.tallies.export_to_xml() + self.tallies.export_to_xml(d) if self.plots: - self.plots.export_to_xml() + self.plots.export_to_xml(d) def run(self, **kwargs): """Creates the XML files, runs OpenMC, and returns k-effective diff --git a/openmc/model/triso.py b/openmc/model/triso.py index e04d7f589..2afc88d64 100644 --- a/openmc/model/triso.py +++ b/openmc/model/triso.py @@ -49,7 +49,7 @@ class TRISO(openmc.Cell): """ def __init__(self, outer_radius, fill, center=(0., 0., 0.)): - self._surface = openmc.Sphere(R=outer_radius) + self._surface = openmc.Sphere(r=outer_radius) super().__init__(fill=fill, region=-self._surface) self.center = np.asarray(center) diff --git a/openmc/particle_restart.py b/openmc/particle_restart.py index bc8666969..68a761458 100644 --- a/openmc/particle_restart.py +++ b/openmc/particle_restart.py @@ -49,44 +49,44 @@ class Particle(object): @property def current_batch(self): - return self._f['current_batch'].value + return self._f['current_batch'][()] @property def current_generation(self): - return self._f['current_generation'].value + return self._f['current_generation'][()] @property def energy(self): - return self._f['energy'].value + return self._f['energy'][()] @property def generations_per_batch(self): - return self._f['generations_per_batch'].value + return self._f['generations_per_batch'][()] @property def id(self): - return self._f['id'].value + return self._f['id'][()] @property def type(self): - return self._f['type'].value + return self._f['type'][()] @property def n_particles(self): - return self._f['n_particles'].value + return self._f['n_particles'][()] @property def run_mode(self): - return self._f['run_mode'].value.decode() + return self._f['run_mode'][()].decode() @property def uvw(self): - return self._f['uvw'].value + return self._f['uvw'][()] @property def weight(self): - return self._f['weight'].value + return self._f['weight'][()] @property def xyz(self): - return self._f['xyz'].value + return self._f['xyz'][()] diff --git a/openmc/plots.py b/openmc/plots.py index f99392def..eb582502e 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -1,9 +1,10 @@ from collections.abc import Iterable, Mapping from numbers import Real, Integral -from xml.etree import ElementTree as ET +from pathlib import Path import subprocess import sys import warnings +from xml.etree import ElementTree as ET import numpy as np @@ -818,6 +819,11 @@ class Plots(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(self._plots_file) + # Check if path is a directory + p = Path(path) + if p.is_dir(): + p /= 'plots.xml' + # Write the XML Tree to the plots.xml file tree = ET.ElementTree(self._plots_file) - tree.write(path, xml_declaration=True, encoding='utf-8', method="xml") + tree.write(str(p), xml_declaration=True, encoding='utf-8') diff --git a/openmc/region.py b/openmc/region.py index ceffadbe3..d12ae7112 100644 --- a/openmc/region.py +++ b/openmc/region.py @@ -229,14 +229,28 @@ class Region(metaclass=ABCMeta): clone : openmc.Region The clone of this region - Raises - ------ - NotImplementedError - This method is not implemented for the abstract region class. + """ + pass + + @abstractmethod + def translate(self, vector, memo=None): + """Translate region in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which region should be translated + memo : dict or None + Dictionary used for memoization. This parameter is used internally + and should not be specified by the user. + + Returns + ------- + openmc.Region + Translated region """ - raise NotImplementedError('The clone method is not implemented for ' - 'the abstract region class.') + pass class Intersection(Region, MutableSequence): @@ -247,7 +261,7 @@ class Intersection(Region, MutableSequence): following example: >>> equator = openmc.ZPlane(z0=0.0) - >>> earth = openmc.Sphere(R=637.1e6) + >>> earth = openmc.Sphere(r=637.1e6) >>> northern_hemisphere = -earth & +equator >>> southern_hemisphere = -earth & -equator >>> type(northern_hemisphere) @@ -352,6 +366,27 @@ class Intersection(Region, MutableSequence): clone[:] = [n.clone(memo) for n in self] return clone + def translate(self, vector, memo=None): + """Translate region in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which region should be translated + memo : dict or None + Dictionary used for memoization. This parameter is used internally + and should not be specified by the user. + + Returns + ------- + openmc.Intersection + Translated region + + """ + if memo is None: + memo = {} + return type(self)(n.translate(vector, memo) for n in self) + class Union(Region, MutableSequence): r"""Union of two or more regions. @@ -361,7 +396,7 @@ class Union(Region, MutableSequence): example: >>> s1 = openmc.ZPlane(z0=0.0) - >>> s2 = openmc.Sphere(R=637.1e6) + >>> s2 = openmc.Sphere(r=637.1e6) >>> type(-s2 | +s1) @@ -464,6 +499,27 @@ class Union(Region, MutableSequence): clone[:] = [n.clone(memo) for n in self] return clone + def translate(self, vector, memo=None): + """Translate region in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which region should be translated + memo : dict or None + Dictionary used for memoization. This parameter is used internally + and should not be specified by the user. + + Returns + ------- + openmc.Union + Translated region + + """ + if memo is None: + memo = {} + return type(self)(n.translate(vector, memo) for n in self) + class Complement(Region): """Complement of a region. @@ -584,3 +640,24 @@ class Complement(Region): clone = deepcopy(self) clone.node = self.node.clone(memo) return clone + + def translate(self, vector, memo=None): + """Translate region in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which region should be translated + memo : dict or None + Dictionary used for memoization. This parameter is used internally + and should not be specified by the user. + + Returns + ------- + openmc.Complement + Translated region + + """ + if memo is None: + memo = {} + return type(self)(self.node.translate(vector, memo)) diff --git a/openmc/settings.py b/openmc/settings.py index 23450d98d..f348bc6cb 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -1,4 +1,5 @@ from collections.abc import Iterable, MutableSequence, Mapping +from pathlib import Path from numbers import Real, Integral import warnings from xml.etree import ElementTree as ET @@ -129,8 +130,6 @@ class Settings(object): range. 'multipole' is a boolean indicating whether or not the windowed multipole method should be used to evaluate resolved resonance cross sections. - threads : int - Number of OpenMP threads trace : tuple or list Show detailed information about a single particle, indicated by three integers: the batch number, generation number, and particle number @@ -196,7 +195,6 @@ class Settings(object): self._statepoint = {} self._sourcepoint = {} - self._threads = None self._no_reduce = None self._verbosity = None @@ -311,10 +309,6 @@ class Settings(object): def statepoint(self): return self._statepoint - @property - def threads(self): - return self._threads - @property def no_reduce(self): return self._no_reduce @@ -622,12 +616,6 @@ class Settings(object): self._temperature = temperature - @threads.setter - def threads(self, threads): - cv.check_type('number of threads', threads, Integral) - cv.check_greater_than('number of threads', threads, 0) - self._threads = threads - @trace.setter def trace(self, trace): cv.check_type('trace', trace, Iterable, Integral) @@ -884,11 +872,6 @@ class Settings(object): else: element.text = str(value) - def _create_threads_subelement(self, root): - if self._threads is not None: - element = ET.SubElement(root, "threads") - element.text = str(self._threads) - def _create_trace_subelement(self, root): if self._trace is not None: element = ET.SubElement(root, "trace") @@ -979,7 +962,6 @@ class Settings(object): self._create_entropy_mesh_subelement(root_element) self._create_trigger_subelement(root_element) self._create_no_reduce_subelement(root_element) - self._create_threads_subelement(root_element) self._create_verbosity_subelement(root_element) self._create_tabular_legendre_subelements(root_element) self._create_temperature_subelements(root_element) @@ -995,6 +977,11 @@ class Settings(object): # Clean the indentation in the file to be user-readable clean_indentation(root_element) + # Check if path is a directory + p = Path(path) + if p.is_dir(): + p /= 'settings.xml' + # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) - tree.write(path, xml_declaration=True, encoding='utf-8', method="xml") + tree.write(str(p), xml_declaration=True, encoding='utf-8') diff --git a/openmc/statepoint.py b/openmc/statepoint.py index df8560379..3dc653584 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -161,35 +161,35 @@ class StatePoint(object): @property def cmfd_balance(self): - return self._f['cmfd/cmfd_balance'].value if self.cmfd_on else None + return self._f['cmfd/cmfd_balance'][()] if self.cmfd_on else None @property def cmfd_dominance(self): - return self._f['cmfd/cmfd_dominance'].value if self.cmfd_on else None + return self._f['cmfd/cmfd_dominance'][()] if self.cmfd_on else None @property def cmfd_entropy(self): - return self._f['cmfd/cmfd_entropy'].value if self.cmfd_on else None + return self._f['cmfd/cmfd_entropy'][()] if self.cmfd_on else None @property def cmfd_indices(self): - return self._f['cmfd/indices'].value if self.cmfd_on else None + return self._f['cmfd/indices'][()] if self.cmfd_on else None @property def cmfd_src(self): if self.cmfd_on: - data = self._f['cmfd/cmfd_src'].value + data = self._f['cmfd/cmfd_src'][()] return np.reshape(data, tuple(self.cmfd_indices), order='F') else: return None @property def cmfd_srccmp(self): - return self._f['cmfd/cmfd_srccmp'].value if self.cmfd_on else None + return self._f['cmfd/cmfd_srccmp'][()] if self.cmfd_on else None @property def current_batch(self): - return self._f['current_batch'].value + return self._f['current_batch'][()] @property def date_and_time(self): @@ -199,7 +199,7 @@ class StatePoint(object): @property def entropy(self): if self.run_mode == 'eigenvalue': - return self._f['entropy'].value + return self._f['entropy'][()] else: return None @@ -220,14 +220,14 @@ class StatePoint(object): @property def generations_per_batch(self): if self.run_mode == 'eigenvalue': - return self._f['generations_per_batch'].value + return self._f['generations_per_batch'][()] else: return None @property def global_tallies(self): if self._global_tallies is None: - data = self._f['global_tallies'].value + data = self._f['global_tallies'][()] gt = np.zeros(data.shape[0], dtype=[ ('name', 'a14'), ('sum', 'f8'), ('sum_sq', 'f8'), ('mean', 'f8'), ('std_dev', 'f8')]) @@ -248,42 +248,42 @@ class StatePoint(object): @property def k_cmfd(self): if self.cmfd_on: - return self._f['cmfd/k_cmfd'].value + return self._f['cmfd/k_cmfd'][()] else: return None @property def k_generation(self): if self.run_mode == 'eigenvalue': - return self._f['k_generation'].value + return self._f['k_generation'][()] else: return None @property def k_combined(self): if self.run_mode == 'eigenvalue': - return ufloat(*self._f['k_combined'].value) + return ufloat(*self._f['k_combined'][()]) else: return None @property def k_col_abs(self): if self.run_mode == 'eigenvalue': - return self._f['k_col_abs'].value + return self._f['k_col_abs'][()] else: return None @property def k_col_tra(self): if self.run_mode == 'eigenvalue': - return self._f['k_col_tra'].value + return self._f['k_col_tra'][()] else: return None @property def k_abs_tra(self): if self.run_mode == 'eigenvalue': - return self._f['k_abs_tra'].value + return self._f['k_abs_tra'][()] else: return None @@ -303,22 +303,22 @@ class StatePoint(object): @property def n_batches(self): - return self._f['n_batches'].value + return self._f['n_batches'][()] @property def n_inactive(self): if self.run_mode == 'eigenvalue': - return self._f['n_inactive'].value + return self._f['n_inactive'][()] else: return None @property def n_particles(self): - return self._f['n_particles'].value + return self._f['n_particles'][()] @property def n_realizations(self): - return self._f['n_realizations'].value + return self._f['n_realizations'][()] @property def path(self): @@ -330,20 +330,20 @@ class StatePoint(object): @property def run_mode(self): - return self._f['run_mode'].value.decode() + return self._f['run_mode'][()].decode() @property def runtime(self): - return {name: dataset.value + return {name: dataset[()] for name, dataset in self._f['runtime'].items()} @property def seed(self): - return self._f['seed'].value + return self._f['seed'][()] @property def source(self): - return self._f['source_bank'].value if self.source_present else None + return self._f['source_bank'][()] if self.source_present else None @property def source_present(self): @@ -376,24 +376,24 @@ class StatePoint(object): group = tallies_group['tally {}'.format(tally_id)] # Read the number of realizations - n_realizations = group['n_realizations'].value + n_realizations = group['n_realizations'][()] # Create Tally object and assign basic properties tally = openmc.Tally(tally_id) tally._sp_filename = self._f.filename - tally.name = group['name'].value.decode() if 'name' in group else '' - tally.estimator = group['estimator'].value.decode() + tally.name = group['name'][()].decode() if 'name' in group else '' + tally.estimator = group['estimator'][()].decode() tally.num_realizations = n_realizations # Read derivative information. if 'derivative' in group: - deriv_id = group['derivative'].value + deriv_id = group['derivative'][()] tally.derivative = self.tally_derivatives[deriv_id] # Read all filters - n_filters = group['n_filters'].value + n_filters = group['n_filters'][()] if n_filters > 0: - filter_ids = group['filters'].value + filter_ids = group['filters'][()] filters_group = self._f['tallies/filters'] for filter_id in filter_ids: filter_group = filters_group['filter {}'.format( @@ -403,15 +403,15 @@ class StatePoint(object): tally.filters.append(new_filter) # Read nuclide bins - nuclide_names = group['nuclides'].value + nuclide_names = group['nuclides'][()] # Add all nuclides to the Tally for name in nuclide_names: nuclide = openmc.Nuclide(name.decode().strip()) tally.nuclides.append(nuclide) - scores = group['score_bins'].value - n_score_bins = group['n_score_bins'].value + scores = group['score_bins'][()] + n_score_bins = group['n_score_bins'][()] # Add the scores to the Tally for j, score in enumerate(scores): @@ -445,14 +445,14 @@ class StatePoint(object): group = self._f['tallies/derivatives/derivative {}' .format(d_id)] deriv = openmc.TallyDerivative(derivative_id=d_id) - deriv.variable = group['independent variable'].value.decode() + deriv.variable = group['independent variable'][()].decode() if deriv.variable == 'density': - deriv.material = group['material'].value + deriv.material = group['material'][()] elif deriv.variable == 'nuclide_density': - deriv.material = group['material'].value - deriv.nuclide = group['nuclide'].value.decode() + deriv.material = group['material'][()] + deriv.nuclide = group['nuclide'][()].decode() elif deriv.variable == 'temperature': - deriv.material = group['material'].value + deriv.material = group['material'][()] self._derivs[d_id] = deriv self._derivs_read = True diff --git a/openmc/summary.py b/openmc/summary.py index 866dd8288..2ab29c00a 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -85,14 +85,14 @@ class Summary(object): def _read_nuclides(self): if 'nuclides/names' in self._f: - names = self._f['nuclides/names'].value - awrs = self._f['nuclides/awrs'].value + names = self._f['nuclides/names'][()] + awrs = self._f['nuclides/awrs'][()] for name, awr in zip(names, awrs): self._nuclides[name.decode()] = awr def _read_macroscopics(self): if 'macroscopics/names' in self._f: - names = self._f['macroscopics/names'].value + names = self._f['macroscopics/names'][()] for name in names: self._macroscopics = name.decode() @@ -130,34 +130,34 @@ class Summary(object): for key, group in self._f['geometry/cells'].items(): cell_id = int(key.lstrip('cell ')) - name = group['name'].value.decode() if 'name' in group else '' - fill_type = group['fill_type'].value.decode() + name = group['name'][()].decode() if 'name' in group else '' + fill_type = group['fill_type'][()].decode() if fill_type == 'material': - fill = group['material'].value + fill = group['material'][()] elif fill_type == 'universe': - fill = group['fill'].value + fill = group['fill'][()] else: - fill = group['lattice'].value + fill = group['lattice'][()] - region = group['region'].value.decode() if 'region' in group else '' + region = group['region'][()].decode() if 'region' in group else '' # Create this Cell cell = openmc.Cell(cell_id=cell_id, name=name) if fill_type == 'universe': if 'translation' in group: - translation = group['translation'][...] + translation = group['translation'][()] translation = np.asarray(translation, dtype=np.float64) cell.translation = translation if 'rotation' in group: - rotation = group['rotation'][...] + rotation = group['rotation'][()] rotation = np.asarray(rotation, dtype=np.int) cell._rotation = rotation elif fill_type == 'material': - cell.temperature = group['temperature'][...] + cell.temperature = group['temperature'][()] # Store Cell fill information for after Universe/Lattice creation cell_fills[cell.id] = (fill_type, fill) diff --git a/openmc/surface.py b/openmc/surface.py index c861880c5..dc251485d 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -1,9 +1,9 @@ -from abc import ABCMeta +from abc import ABCMeta, abstractmethod from collections import OrderedDict from copy import deepcopy -from functools import partial from numbers import Real, Integral from xml.etree import ElementTree as ET +from warnings import warn import numpy as np @@ -14,8 +14,13 @@ from openmc.mixin import IDManagerMixin _BOUNDARY_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic'] +_WARNING_UPPER = """\ +"{}(...) accepts an argument named '{}', not '{}'. Future versions of OpenMC \ +will not accept the capitalized version.\ +""" -class Surface(IDManagerMixin): + +class Surface(IDManagerMixin, metaclass=ABCMeta): """An implicit surface with an associated boundary condition. An implicit surface is defined as the set of zeros of a function of the @@ -142,7 +147,6 @@ class Surface(IDManagerMixin): desired half-space """ - return (np.array([-np.inf, -np.inf, -np.inf]), np.array([np.inf, np.inf, np.inf])) @@ -175,6 +179,14 @@ class Surface(IDManagerMixin): return memo[self] + @abstractmethod + def evaluate(self, point): + pass + + @abstractmethod + def translate(self, vector): + pass + def to_xml_element(self): """Return XML representation of the surface @@ -257,9 +269,9 @@ class Surface(IDManagerMixin): """ surface_id = int(group.name.split('/')[-1].lstrip('surface ')) - name = group['name'].value.decode() if 'name' in group else '' - surf_type = group['type'].value.decode() - bc = group['boundary_type'].value.decode() + name = group['name'][()].decode() if 'name' in group else '' + surf_type = group['type'][()].decode() + bc = group['boundary_type'][()].decode() coeffs = group['coefficients'][...] # Create the Surface based on its type @@ -280,29 +292,29 @@ class Surface(IDManagerMixin): surface = Plane(surface_id, bc, A, B, C, D, name) elif surf_type == 'x-cylinder': - y0, z0, R = coeffs - surface = XCylinder(surface_id, bc, y0, z0, R, name) + y0, z0, r = coeffs + surface = XCylinder(surface_id, bc, y0, z0, r, name) elif surf_type == 'y-cylinder': - x0, z0, R = coeffs - surface = YCylinder(surface_id, bc, x0, z0, R, name) + x0, z0, r = coeffs + surface = YCylinder(surface_id, bc, x0, z0, r, name) elif surf_type == 'z-cylinder': - x0, y0, R = coeffs - surface = ZCylinder(surface_id, bc, x0, y0, R, name) + x0, y0, r = coeffs + surface = ZCylinder(surface_id, bc, x0, y0, r, name) elif surf_type == 'sphere': - x0, y0, z0, R = coeffs - surface = Sphere(surface_id, bc, x0, y0, z0, R, name) + x0, y0, z0, r = coeffs + surface = Sphere(surface_id, bc, x0, y0, z0, r, name) elif surf_type in ['x-cone', 'y-cone', 'z-cone']: - x0, y0, z0, R2 = coeffs + x0, y0, z0, r2 = coeffs if surf_type == 'x-cone': - surface = XCone(surface_id, bc, x0, y0, z0, R2, name) + surface = XCone(surface_id, bc, x0, y0, z0, r2, name) elif surf_type == 'y-cone': - surface = YCone(surface_id, bc, x0, y0, z0, R2, name) + surface = YCone(surface_id, bc, x0, y0, z0, r2, name) elif surf_type == 'z-cone': - surface = ZCone(surface_id, bc, x0, y0, z0, R2, name) + surface = ZCone(surface_id, bc, x0, y0, z0, r2, name) elif surf_type == 'quadric': a, b, c, d, e, f, g, h, j, k = coeffs @@ -324,13 +336,13 @@ class Plane(Surface): 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 + a : float, optional The 'A' parameter for the plane. Defaults to 1. - B : float, optional + b : float, optional The 'B' parameter for the plane. Defaults to 0. - C : float, optional + c : float, optional The 'C' parameter for the plane. Defaults to 0. - D : float, optional + d : float, optional The 'D' parameter for the plane. Defaults to 0. name : str, optional Name of the plane. If not specified, the name will be the empty string. @@ -363,56 +375,61 @@ class Plane(Surface): """ _type = 'plane' - _coeff_keys = ('A', 'B', 'C', 'D') + _coeff_keys = ('a', 'b', 'c', 'd') def __init__(self, surface_id=None, boundary_type='transmission', - A=1., B=0., C=0., D=0., name=''): + a=1., b=0., c=0., d=0., name='', **kwargs): super().__init__(surface_id, boundary_type, name=name) self._periodic_surface = None - self.a = A - self.b = B - self.c = C - self.d = D + self.a = a + self.b = b + self.c = c + self.d = d + for k, v in kwargs.items(): + if k in 'ABCD': + warn(_WARNING_UPPER.format(type(self).__name__, k.lower(), k), + FutureWarning) + setattr(self, k.lower(), v) @property def a(self): - return self.coefficients['A'] + return self.coefficients['a'] @property def b(self): - return self.coefficients['B'] + return self.coefficients['b'] @property def c(self): - return self.coefficients['C'] + return self.coefficients['c'] @property def d(self): - return self.coefficients['D'] + return self.coefficients['d'] @property def periodic_surface(self): return self._periodic_surface @a.setter - def a(self, A): - check_type('A coefficient', A, Real) - self._coefficients['A'] = A + def a(self, a): + check_type('A coefficient', a, Real) + self._coefficients['a'] = a @b.setter - def b(self, B): - check_type('B coefficient', B, Real) - self._coefficients['B'] = B + def b(self, b): + check_type('B coefficient', b, Real) + self._coefficients['b'] = b @c.setter - def c(self, C): - check_type('C coefficient', C, Real) - self._coefficients['C'] = C + def c(self, c): + check_type('C coefficient', c, Real) + self._coefficients['c'] = c @d.setter - def d(self, D): - check_type('D coefficient', D, Real) - self._coefficients['D'] = D + def d(self, d): + check_type('D coefficient', d, Real) + self._coefficients['d'] = d @periodic_surface.setter def periodic_surface(self, periodic_surface): @@ -432,13 +449,34 @@ class Plane(Surface): Returns ------- float - :math:`Ax' + By' + Cz' - d` + :math:`Ax' + By' + Cz' - D` """ x, y, z = point return self.a*x + self.b*y + self.c*z - self.d + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.Plane + Translated surface + + """ + vx, vy, vz = vector + d = self.d + self.a*vx + self.b*vy + self.c*vz + if d == self.d: + return self + else: + return type(self)(a=self.a, b=self.b, c=self.c, d=d) + def to_xml_element(self): """Return XML representation of the surface @@ -456,6 +494,38 @@ class Plane(Surface): element.set("periodic_surface_id", str(self.periodic_surface.id)) return element + @classmethod + def from_points(cls, p1, p2, p3, **kwargs): + """Return a plane given three points that pass through it. + + Parameters + ---------- + p1, p2, p3 : 3-tuples + Points that pass through the plane + kwargs : dict + Keyword arguments passed to the :class:`Plane` constructor + + Returns + ------- + Plane + Plane that passes through the three points + + """ + # Convert to numpy arrays + p1 = np.asarray(p1) + p2 = np.asarray(p2) + p3 = np.asarray(p3) + + # Find normal vector to plane by taking cross product of two vectors + # connecting p1->p2 and p1->p3 + n = np.cross(p2 - p1, p3 - p1) + + # The equation of the plane will by n·( - p1) = 0. Determine + # coefficients a, b, c, and d based on that + a, b, c = n + d = np.dot(n, p1) + return cls(a=a, b=b, c=c, d=d, **kwargs) + class XPlane(Plane): """A plane perpendicular to the x axis of the form :math:`x - x_0 = 0` @@ -561,6 +631,26 @@ class XPlane(Plane): """ return point[0] - self.x0 + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.XPlane + Translated surface + + """ + vx = vector[0] + if vx == 0: + return self + else: + return type(self)(x0=self.x0 + vx) + class YPlane(Plane): """A plane perpendicular to the y axis of the form :math:`y - y_0 = 0` @@ -667,6 +757,26 @@ class YPlane(Plane): """ return point[1] - self.y0 + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.YPlane + Translated surface + + """ + vy = vector[1] + if vy == 0.0: + return self + else: + return type(self)(y0=self.y0 + vy) + class ZPlane(Plane): """A plane perpendicular to the z axis of the form :math:`z - z_0 = 0` @@ -773,8 +883,28 @@ class ZPlane(Plane): """ return point[2] - self.z0 + def translate(self, vector): + """Translate surface in given direction -class Cylinder(Surface, metaclass=ABCMeta): + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.ZPlane + Translated surface + + """ + vz = vector[2] + if vz == 0.0: + return self + else: + return type(self)(z0=self.z0 + vz) + + +class Cylinder(Surface): """A cylinder whose length is parallel to the x-, y-, or z-axis. Parameters @@ -786,7 +916,7 @@ class Cylinder(Surface, metaclass=ABCMeta): 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 + 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 @@ -810,23 +940,23 @@ class Cylinder(Surface, metaclass=ABCMeta): """ def __init__(self, surface_id=None, boundary_type='transmission', - R=1., name=''): + r=1., name=''): super().__init__(surface_id, boundary_type, name=name) - self.r = R + self.r = r @property def r(self): - return self.coefficients['R'] + return self.coefficients['r'] @r.setter - def r(self, R): - check_type('R coefficient', R, Real) - self._coefficients['R'] = R + def r(self, r): + check_type('r coefficient', r, Real) + self._coefficients['r'] = r class XCylinder(Cylinder): """An infinite cylinder whose length is parallel to the x-axis of the form - :math:`(y - y_0)^2 + (z - z_0)^2 = R^2`. + :math:`(y - y_0)^2 + (z - z_0)^2 = r^2`. Parameters ---------- @@ -841,7 +971,7 @@ class XCylinder(Cylinder): 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 + r : float, optional Radius of the cylinder. Defaults to 0. name : str, optional Name of the cylinder. If not specified, the name will be the empty @@ -868,11 +998,14 @@ class XCylinder(Cylinder): """ _type = 'x-cylinder' - _coeff_keys = ('y0', 'z0', 'R') + _coeff_keys = ('y0', 'z0', 'r') def __init__(self, surface_id=None, boundary_type='transmission', - y0=0., z0=0., R=1., name=''): - super().__init__(surface_id, boundary_type, R, name=name) + y0=0., z0=0., r=1., name='', *, 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) self.y0 = y0 self.z0 = z0 @@ -938,17 +1071,39 @@ class XCylinder(Cylinder): Returns ------- float - :math:`(y' - y_0)^2 + (z' - z_0)^2 - R^2` + :math:`(y' - y_0)^2 + (z' - z_0)^2 - r^2` """ y = point[1] - self.y0 z = point[2] - self.z0 return y**2 + z**2 - self.r**2 + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.XCylinder + Translated surface + + """ + vx, vy, vz = vector + if vy == 0.0 and vz == 0.0: + return self + else: + y0 = self.y0 + vy + z0 = self.z0 + vz + return type(self)(y0=y0, z0=z0, r=self.r) + class YCylinder(Cylinder): """An infinite cylinder whose length is parallel to the y-axis of the form - :math:`(x - x_0)^2 + (z - z_0)^2 = R^2`. + :math:`(x - x_0)^2 + (z - z_0)^2 = r^2`. Parameters ---------- @@ -963,7 +1118,7 @@ class YCylinder(Cylinder): 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 + 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 @@ -990,11 +1145,14 @@ class YCylinder(Cylinder): """ _type = 'y-cylinder' - _coeff_keys = ('x0', 'z0', 'R') + _coeff_keys = ('x0', 'z0', 'r') def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., z0=0., R=1., name=''): - super().__init__(surface_id, boundary_type, R, name=name) + x0=0., z0=0., r=1., name='', *, 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) self.x0 = x0 self.z0 = z0 @@ -1060,17 +1218,39 @@ class YCylinder(Cylinder): Returns ------- float - :math:`(x' - x_0)^2 + (z' - z_0)^2 - R^2` + :math:`(x' - x_0)^2 + (z' - z_0)^2 - r^2` """ x = point[0] - self.x0 z = point[2] - self.z0 return x**2 + z**2 - self.r**2 + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.YCylinder + Translated surface + + """ + vx, vy, vz = vector + if vx == 0.0 and vz == 0.0: + return self + else: + x0 = self.x0 + vx + z0 = self.z0 + vz + return type(self)(x0=x0, z0=z0, r=self.r) + class ZCylinder(Cylinder): """An infinite cylinder whose length is parallel to the z-axis of the form - :math:`(x - x_0)^2 + (y - y_0)^2 = R^2`. + :math:`(x - x_0)^2 + (y - y_0)^2 = r^2`. Parameters ---------- @@ -1085,7 +1265,7 @@ class ZCylinder(Cylinder): x-coordinate of the center of the cylinder. Defaults to 0. y0 : float, optional y-coordinate of the center of the cylinder. Defaults to 0. - R : float, optional + 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 @@ -1112,11 +1292,14 @@ class ZCylinder(Cylinder): """ _type = 'z-cylinder' - _coeff_keys = ('x0', 'y0', 'R') + _coeff_keys = ('x0', 'y0', 'r') def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., R=1., name=''): - super().__init__(surface_id, boundary_type, R, name=name) + x0=0., y0=0., r=1., name='', *, 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) self.x0 = x0 self.y0 = y0 @@ -1182,16 +1365,38 @@ class ZCylinder(Cylinder): Returns ------- float - :math:`(x' - x_0)^2 + (y' - y_0)^2 - R^2` + :math:`(x' - x_0)^2 + (y' - y_0)^2 - r^2` """ x = point[0] - self.x0 y = point[1] - self.y0 return x**2 + y**2 - self.r**2 + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.ZCylinder + Translated surface + + """ + vx, vy, vz = vector + if vx == 0.0 and vy == 0.0: + return self + else: + x0 = self.x0 + vx + y0 = self.y0 + vy + return type(self)(x0=x0, y0=y0, r=self.r) + class Sphere(Surface): - """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = R^2`. + """A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = r^2`. Parameters ---------- @@ -1208,7 +1413,7 @@ class Sphere(Surface): y-coordinate of the center of the sphere. Defaults to 0. z0 : float, optional z-coordinate of the center of the sphere. Defaults to 0. - R : float, optional + r : float, optional Radius of the sphere. Defaults to 1. name : str, optional Name of the sphere. If not specified, the name will be the empty string. @@ -1238,15 +1443,18 @@ class Sphere(Surface): """ _type = 'sphere' - _coeff_keys = ('x0', 'y0', 'z0', 'R') + _coeff_keys = ('x0', 'y0', 'z0', 'r') def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R=1., name=''): + x0=0., y0=0., z0=0., r=1., name='', *, 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, name=name) self.x0 = x0 self.y0 = y0 self.z0 = z0 - self.r = R + self.r = r @property def x0(self): @@ -1262,7 +1470,7 @@ class Sphere(Surface): @property def r(self): - return self.coefficients['R'] + return self.coefficients['r'] @x0.setter def x0(self, x0): @@ -1280,9 +1488,9 @@ class Sphere(Surface): self._coefficients['z0'] = z0 @r.setter - def r(self, R): - check_type('R coefficient', R, Real) - self._coefficients['R'] = R + def r(self, r): + check_type('r coefficient', r, Real) + self._coefficients['r'] = r def bounding_box(self, side): """Determine an axis-aligned bounding box. @@ -1329,7 +1537,7 @@ class Sphere(Surface): Returns ------- float - :math:`(x' - x_0)^2 + (y' - y_0)^2 + (z' - z_0)^2 - R^2` + :math:`(x' - x_0)^2 + (y' - y_0)^2 + (z' - z_0)^2 - r^2` """ x = point[0] - self.x0 @@ -1337,8 +1545,31 @@ class Sphere(Surface): z = point[2] - self.z0 return x**2 + y**2 + z**2 - self.r**2 + def translate(self, vector): + """Translate surface in given direction -class Cone(Surface, metaclass=ABCMeta): + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.Sphere + Translated surface + + """ + vx, vy, vz = vector + if vx == 0.0 and vy == 0.0 and vz == 0.0: + return self + else: + x0 = self.x0 + vx + y0 = self.y0 + vy + z0 = self.z0 + vz + return type(self)(x0=x0, y0=y0, z0=z0, r=self.r) + + +class Cone(Surface): """A conical surface parallel to the x-, y-, or z-axis. Parameters @@ -1356,7 +1587,7 @@ class Cone(Surface, metaclass=ABCMeta): y-coordinate of the apex. Defaults to 0. z0 : float z-coordinate of the apex. Defaults to 0. - R2 : float + 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. @@ -1385,15 +1616,18 @@ class Cone(Surface, metaclass=ABCMeta): """ - _coeff_keys = ('x0', 'y0', 'z0', 'R2') + _coeff_keys = ('x0', 'y0', 'z0', 'r2') def __init__(self, surface_id=None, boundary_type='transmission', - x0=0., y0=0., z0=0., R2=1., name=''): + x0=0., y0=0., z0=0., r2=1., name='', *, R2=None): + if R2 is not None: + warn(_WARNING_UPPER.format(type(self).__name__, 'r2', 'R2'), FutureWarning) + r2 = R2 super().__init__(surface_id, boundary_type, name=name) self.x0 = x0 self.y0 = y0 self.z0 = z0 - self.r2 = R2 + self.r2 = r2 @property def x0(self): @@ -1409,7 +1643,7 @@ class Cone(Surface, metaclass=ABCMeta): @property def r2(self): - return self.coefficients['R2'] + return self.coefficients['r2'] @x0.setter def x0(self, x0): @@ -1427,14 +1661,37 @@ class Cone(Surface, metaclass=ABCMeta): self._coefficients['z0'] = z0 @r2.setter - def r2(self, R2): - check_type('R^2 coefficient', R2, Real) - self._coefficients['R2'] = R2 + def r2(self, r2): + check_type('r^2 coefficient', r2, Real) + self._coefficients['r2'] = r2 + + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.Cone + Translated surface + + """ + vx, vy, vz = vector + if vx == 0.0 and vy == 0.0 and vz == 0.0: + return self + else: + x0 = self.x0 + vx + y0 = self.y0 + vy + z0 = self.z0 + vz + return type(self)(x0=x0, y0=y0, z0=z0, r2=self.r2) class XCone(Cone): """A cone parallel to the x-axis of the form :math:`(y - y_0)^2 + (z - z_0)^2 = - R^2 (x - x_0)^2`. + r^2 (x - x_0)^2`. Parameters ---------- @@ -1451,7 +1708,7 @@ class XCone(Cone): y-coordinate of the apex. Defaults to 0. z0 : float, optional z-coordinate of the apex. Defaults to 0. - R2 : float, optional + r2 : float, optional Parameter related to the aperature. Defaults to 1. name : str, optional Name of the cone. If not specified, the name will be the empty string. @@ -1464,7 +1721,7 @@ class XCone(Cone): y-coordinate of the apex z0 : float z-coordinate of the apex - R2 : float + r2 : float Parameter related to the aperature boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the @@ -1494,7 +1751,7 @@ class XCone(Cone): Returns ------- float - :math:`(y' - y_0)^2 + (z' - z_0)^2 - R^2(x' - x_0)^2` + :math:`(y' - y_0)^2 + (z' - z_0)^2 - r^2(x' - x_0)^2` """ x = point[0] - self.x0 @@ -1505,7 +1762,7 @@ class XCone(Cone): class YCone(Cone): """A cone parallel to the y-axis of the form :math:`(x - x_0)^2 + (z - z_0)^2 = - R^2 (y - y_0)^2`. + r^2 (y - y_0)^2`. Parameters ---------- @@ -1522,7 +1779,7 @@ class YCone(Cone): y-coordinate of the apex. Defaults to 0. z0 : float, optional z-coordinate of the apex. Defaults to 0. - R2 : float, optional + r2 : float, optional Parameter related to the aperature. Defaults to 1. name : str, optional Name of the cone. If not specified, the name will be the empty string. @@ -1535,7 +1792,7 @@ class YCone(Cone): y-coordinate of the apex z0 : float z-coordinate of the apex - R2 : float + r2 : float Parameter related to the aperature boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the @@ -1565,7 +1822,7 @@ class YCone(Cone): Returns ------- float - :math:`(x' - x_0)^2 + (z' - z_0)^2 - R^2(y' - y_0)^2` + :math:`(x' - x_0)^2 + (z' - z_0)^2 - r^2(y' - y_0)^2` """ x = point[0] - self.x0 @@ -1576,7 +1833,7 @@ class YCone(Cone): class ZCone(Cone): """A cone parallel to the x-axis of the form :math:`(x - x_0)^2 + (y - y_0)^2 = - R^2 (z - z_0)^2`. + r^2 (z - z_0)^2`. Parameters ---------- @@ -1593,7 +1850,7 @@ class ZCone(Cone): y-coordinate of the apex. Defaults to 0. z0 : float, optional z-coordinate of the apex. Defaults to 0. - R2 : float, optional + r2 : float, optional Parameter related to the aperature. Defaults to 1. name : str, optional Name of the cone. If not specified, the name will be the empty string. @@ -1606,7 +1863,7 @@ class ZCone(Cone): y-coordinate of the apex z0 : float z-coordinate of the apex - R2 : float + r2 : float Parameter related to the aperature boundary_type : {'transmission, 'vacuum', 'reflective'} Boundary condition that defines the behavior for particles hitting the @@ -1636,7 +1893,7 @@ class ZCone(Cone): Returns ------- float - :math:`(x' - x_0)^2 + (y' - y_0)^2 - R^2(z' - z_0)^2` + :math:`(x' - x_0)^2 + (y' - y_0)^2 - r^2(z' - z_0)^2` """ x = point[0] - self.x0 @@ -1810,6 +2067,30 @@ class Quadric(Surface): y*(self.b*y + self.e*z + self.h) + \ z*(self.c*z + self.f*x + self.j) + self.k + def translate(self, vector): + """Translate surface in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which surface should be translated + + Returns + ------- + openmc.Quadric + Translated surface + + """ + vx, vy, vz = vector + a, b, c, d, e, f, g, h, j, k = (getattr(self, key) for key in + self._coeff_keys) + k = (k + vx*vx + vy*vy + vz*vz + d*vx*vy + e*vy*vz + f*vx*vz + - g*vx - h*vy - j*vz) + g = g - 2*a*vx - d*vy - f*vz + h = h - 2*b*vy - d*vx - e*vz + j = j - 2*c*vz - e*vy - f*vx + return type(self)(a=a, b=b, c=c, d=d, e=e, f=f, g=g, h=h, j=j, k=k) + class Halfspace(Region): """A positive or negative half-space region. @@ -1824,7 +2105,7 @@ class Halfspace(Region): can be created from an existing Surface through the __neg__ and __pos__ operators, as the following example demonstrates: - >>> sphere = openmc.Sphere(surface_id=1, R=10.0) + >>> sphere = openmc.Sphere(surface_id=1, r=10.0) >>> inside_sphere = -sphere >>> outside_sphere = +sphere >>> type(inside_sphere) @@ -1955,3 +2236,30 @@ class Halfspace(Region): clone = deepcopy(self) clone.surface = self.surface.clone(memo) return clone + + def translate(self, vector, memo=None): + """Translate half-space in given direction + + Parameters + ---------- + vector : iterable of float + Direction in which region should be translated + memo : dict or None + Dictionary used for memoization + + Returns + ------- + openmc.Halfspace + Translated half-space + + """ + if memo is None: + memo = {} + + # If translated surface not in memo, add it + key = (self.surface, tuple(vector)) + if key not in memo: + memo[key] = self.surface.translate(vector) + + # Return translated surface + return type(self)(memo[key], self.side) diff --git a/openmc/tallies.py b/openmc/tallies.py index 5bc6494bb..a5f3a17f3 100644 --- a/openmc/tallies.py +++ b/openmc/tallies.py @@ -5,6 +5,7 @@ from functools import partial, reduce from itertools import product from numbers import Integral, Real import operator +from pathlib import Path import warnings from xml.etree import ElementTree as ET @@ -216,7 +217,7 @@ class Tally(IDManagerMixin): f = h5py.File(self._sp_filename, 'r') # Extract Tally data from the file - data = f['tallies/tally {0}/results'.format(self.id)].value + data = f['tallies/tally {0}/results'.format(self.id)] sum = data[:, :, 0] sum_sq = data[:, :, 1] @@ -3189,7 +3190,11 @@ class Tallies(cv.CheckedList): # Clean the indentation in the file to be user-readable clean_indentation(root_element) + # Check if path is a directory + p = Path(path) + if p.is_dir(): + p /= 'tallies.xml' + # Write the XML Tree to the tallies.xml file tree = ET.ElementTree(root_element) - tree.write(path, xml_declaration=True, - encoding='utf-8', method="xml") + tree.write(str(p), xml_declaration=True, encoding='utf-8') diff --git a/openmc/universe.py b/openmc/universe.py index 044da12e3..ee038ebef 100644 --- a/openmc/universe.py +++ b/openmc/universe.py @@ -124,7 +124,7 @@ class Universe(IDManagerMixin): """ universe_id = int(group.name.split('/')[-1].lstrip('universe ')) - cell_ids = group['cells'].value + cell_ids = group['cells'][()] # Create this Universe universe = cls(universe_id) diff --git a/openmc/volume.py b/openmc/volume.py index af7c356ae..7ac4fdd0c 100644 --- a/openmc/volume.py +++ b/openmc/volume.py @@ -211,9 +211,9 @@ class VolumeCalculation(object): domain_id = int(obj_name[7:]) ids.append(domain_id) group = f[obj_name] - volume = ufloat(*group['volume'].value) - nucnames = group['nuclides'].value - atoms_ = group['atoms'].value + volume = ufloat(*group['volume'][()]) + nucnames = group['nuclides'][()] + atoms_ = group['atoms'][()] atom_dict = OrderedDict() for name_i, atoms_i in zip(nucnames, atoms_): diff --git a/scripts/openmc-plot-mesh-tally b/scripts/openmc-plot-mesh-tally index 61ada852c..183fe35c5 100755 --- a/scripts/openmc-plot-mesh-tally +++ b/scripts/openmc-plot-mesh-tally @@ -216,24 +216,40 @@ class MeshPlotter(tk.Frame): index = self.filterBoxes[f.short_name].current() spec_list.append((type(f), (index,))) + dims = (self.nx, self.ny, self.nz) + text = self.basisBox.get() if text == 'xy': - dims = (self.nx, self.ny) + h_ind = 0 + v_ind = 1 elif text == 'yz': - dims = (self.ny, self.nz) + h_ind = 1 + v_ind = 2 else: - dims = (self.nx, self.nz) + h_ind = 0 + v_ind = 2 + + axial_ind = 3 - (h_ind + v_ind) + dims = (dims[h_ind], dims[v_ind]) + + mesh_dim = len(self.mesh.dimension) + if mesh_dim == 3: + mesh_indices = [0,0,0] + else: + mesh_indices = [0,0] + matrix = np.zeros(dims) for i in range(dims[0]): for j in range(dims[1]): - if text == 'xy': - meshtuple = (i + 1, j + 1, axial_level) - elif text == 'yz': - meshtuple = (axial_level, i + 1, j + 1) + if mesh_dim == 3: + mesh_indices[h_ind] = i + 1 + mesh_indices[v_ind] = j + 1 + mesh_indices[axial_ind] = axial_level else: - meshtuple = (i + 1, axial_level, j + 1) + mesh_indices[0] = i + 1 + mesh_indices[1] = j + 1 filters, filter_bins = zip(*spec_list + [ - (type(mesh_filter), (meshtuple,))]) + (type(mesh_filter), (tuple(mesh_indices),))]) mean = selectedTally.get_values( [self.scoreBox.get()], filters, filter_bins) stdev = selectedTally.get_values( diff --git a/src/bank.cpp b/src/bank.cpp index 8b7b43688..172ecf84f 100644 --- a/src/bank.cpp +++ b/src/bank.cpp @@ -16,10 +16,9 @@ namespace openmc { namespace simulation { -int64_t n_bank; - std::vector source_bank; std::vector fission_bank; +std::vector secondary_bank; #ifdef _OPENMP std::vector master_fission_bank; #endif @@ -33,7 +32,7 @@ std::vector master_fission_bank; void free_memory_bank() { simulation::source_bank.clear(); -#pragma omp parallel + #pragma omp parallel { simulation::fission_bank.clear(); } diff --git a/src/bremsstrahlung.cpp b/src/bremsstrahlung.cpp index 3b9d8b591..e6b76d19d 100644 --- a/src/bremsstrahlung.cpp +++ b/src/bremsstrahlung.cpp @@ -45,7 +45,7 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost) auto n_e = data::ttb_e_grid.size(); // Find the lower bounding index of the incident electron energy - int j = lower_bound_index(data::ttb_e_grid.cbegin(), + size_t j = lower_bound_index(data::ttb_e_grid.cbegin(), data::ttb_e_grid.cend(), e); if (j == n_e - 1) --j; diff --git a/src/cell.cpp b/src/cell.cpp index d4712b9d1..59057e084 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "openmc/capi.h" @@ -325,7 +326,6 @@ CSGCell::CSGCell(pugi::xml_node cell_node) // Convert the infix region spec to RPN. rpn_ = generate_rpn(id_, region_); - rpn_.shrink_to_fit(); // Check if this is a simple cell. simple_ = true; @@ -336,6 +336,21 @@ CSGCell::CSGCell(pugi::xml_node cell_node) } } + // If this cell is simple, remove all the superfluous operator tokens. + if (simple_) { + size_t i0 = 0; + size_t i1 = 0; + while (i1 < rpn_.size()) { + if (rpn_[i1] < OP_UNION) { + rpn_[i0] = rpn_[i1]; + ++i0; + } + ++i1; + } + rpn_.resize(i0); + } + rpn_.shrink_to_fit(); + // Read the translation vector. if (check_for_node(cell_node, "translation")) { if (fill_ == C_NONE) { @@ -522,19 +537,17 @@ bool CSGCell::contains_simple(Position r, Direction u, int32_t on_surface) const { for (int32_t token : rpn_) { - if (token < OP_UNION) { - // If the token is not an operator, evaluate the sense of particle with - // respect to the surface and see if the token matches the sense. If the - // particle's surface attribute is set and matches the token, that - // overrides the determination based on sense(). - if (token == on_surface) { - } else if (-token == on_surface) { - return false; - } else { - // Note the off-by-one indexing - bool sense = model::surfaces[abs(token)-1]->sense(r, u); - if (sense != (token > 0)) {return false;} - } + // Assume that no tokens are operators. Evaluate the sense of particle with + // respect to the surface and see if the token matches the sense. If the + // particle's surface attribute is set and matches the token, that + // overrides the determination based on sense(). + if (token == on_surface) { + } else if (-token == on_surface) { + return false; + } else { + // Note the off-by-one indexing + bool sense = model::surfaces[abs(token)-1]->sense(r, u); + if (sense != (token > 0)) {return false;} } } return true; @@ -601,7 +614,7 @@ std::pair DAGCell::distance(Position r, Direction u, int32_t on_surface) const { moab::ErrorCode rval; - moab::EntityHandle vol = dagmc_ptr_->entity_by_id(3, id_); + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); moab::EntityHandle hit_surf; double dist; double pnt[3] = {r.x, r.y, r.z}; @@ -621,7 +634,7 @@ DAGCell::distance(Position r, Direction u, int32_t on_surface) const bool DAGCell::contains(Position r, Direction u, int32_t on_surface) const { moab::ErrorCode rval; - moab::EntityHandle vol = dagmc_ptr_->entity_by_id(3, id_); + moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); int result = 0; double pnt[3] = {r.x, r.y, r.z}; @@ -635,6 +648,142 @@ void DAGCell::to_hdf5(hid_t group_id) const { return; } #endif +//============================================================================== +// UniversePartitioner implementation +//============================================================================== + +UniversePartitioner::UniversePartitioner(const Universe& univ) +{ + // Define an ordered set of surface indices that point to z-planes. Use a + // functor to to order the set by the z0_ values of the corresponding planes. + struct compare_surfs { + bool operator()(const int32_t& i_surf, const int32_t& j_surf) const + { + const auto* surf = model::surfaces[i_surf].get(); + const auto* zplane = dynamic_cast(surf); + double zi = zplane->z0_; + surf = model::surfaces[j_surf].get(); + zplane = dynamic_cast(surf); + double zj = zplane->z0_; + return zi < zj; + } + }; + std::set surf_set; + + // Find all of the z-planes in this universe. A set is used here for the + // O(log(n)) insertions that will ensure entries are not repeated. + for (auto i_cell : univ.cells_) { + for (auto token : model::cells[i_cell]->rpn_) { + if (token < OP_UNION) { + auto i_surf = std::abs(token) - 1; + const auto* surf = model::surfaces[i_surf].get(); + if (const auto* zplane = dynamic_cast(surf)) + surf_set.insert(i_surf); + } + } + } + + // Populate the surfs_ vector from the ordered set. + surfs_.insert(surfs_.begin(), surf_set.begin(), surf_set.end()); + + // Populate the partition lists. + partitions_.resize(surfs_.size() + 1); + for (auto i_cell : univ.cells_) { + // Find the tokens for bounding z-planes. + int32_t lower_token = 0, upper_token = 0; + double min_z, max_z; + for (auto token : model::cells[i_cell]->rpn_) { + if (token < OP_UNION) { + const auto* surf = model::surfaces[std::abs(token) - 1].get(); + if (const auto* zplane = dynamic_cast(surf)) { + if (lower_token == 0 || zplane->z0_ < min_z) { + lower_token = token; + min_z = zplane->z0_; + } + if (upper_token == 0 || zplane->z0_ > max_z) { + upper_token = token; + max_z = zplane->z0_; + } + } + } + } + + // If there are no bounding z-planes, add this cell to all partitions. + if (lower_token == 0) { + for (auto& p : partitions_) p.push_back(i_cell); + continue; + } + + // Find the first partition this cell lies in. If the lower_token indicates + // a negative halfspace, then the cell is unbounded in the lower direction + // and it lies in the first partition onward. Otherwise, it is bounded by + // the positive halfspace given by the lower_token. + int first_partition = 0; + if (lower_token > 0) { + for (int i = 0; i < surfs_.size(); ++i) { + if (lower_token == surfs_[i] + 1) { + first_partition = i + 1; + break; + } + } + } + + // Find the last partition this cell lies in. The logic is analogous to the + // logic for first_partition. + int last_partition = surfs_.size(); + if (upper_token < 0) { + for (int i = first_partition; i < surfs_.size(); ++i) { + if (upper_token == -(surfs_[i] + 1)) { + last_partition = i; + break; + } + } + } + + // Add the cell to all relevant partitions. + for (int i = first_partition; i <= last_partition; ++i) { + partitions_[i].push_back(i_cell); + } + } +} + +const std::vector& +UniversePartitioner::get_cells(Position r, Direction u) const +{ + // Perform a binary search for the partition containing the given coordinates. + int left = 0; + int middle = (surfs_.size() - 1) / 2; + int right = surfs_.size() - 1; + while (true) { + // Check the sense of the coordinates for the current surface. + const auto& surf = *model::surfaces[surfs_[middle]]; + if (surf.sense(r, u)) { + // The coordinates lie in the positive halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the positive + // side of this surface. + int right_leaf = right - (right - middle) / 2; + if (right_leaf != middle) { + left = middle + 1; + middle = right_leaf; + } else { + return partitions_[middle+1]; + } + + } else { + // The coordinates lie in the negative halfspace. Recurse if there are + // more surfaces to check. Otherwise, return the cells on the negative + // side of this surface. + int left_leaf = left + (middle - left) / 2; + if (left_leaf != middle) { + right = middle-1; + middle = left_leaf; + } else { + return partitions_[middle]; + } + } + } +} + //============================================================================== // Non-method functions //============================================================================== @@ -830,9 +979,9 @@ openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end) int32_t next_cell(DAGCell* cur_cell, DAGSurface* surf_xed) { moab::EntityHandle surf = - surf_xed->dagmc_ptr_->entity_by_id(2, surf_xed->id_); + surf_xed->dagmc_ptr_->entity_by_index(2, surf_xed->dag_index_); moab::EntityHandle vol = - cur_cell->dagmc_ptr_->entity_by_id(3, cur_cell->id_); + cur_cell->dagmc_ptr_->entity_by_index(3, cur_cell->dag_index_); moab::EntityHandle new_vol; cur_cell->dagmc_ptr_->next_vol(surf, vol, new_vol); diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index e91cfc875..2216b37da 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -247,7 +247,6 @@ read_ce_cross_sections(const std::vector>& nuc_temps, LibraryKey key {Library::Type::photon, element}; int idx = data::library_map[key]; std::string& filename = data::libraries[idx].path_; - int i_element = data::element_map[element]; write_message("Reading " + element + " from " + filename, 6); // Open file and make sure version is sufficient diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 34027ce7c..8add1f4f3 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -4,10 +4,11 @@ #include "openmc/constants.h" #include "openmc/error.h" #include "openmc/file_utils.h" +#include "openmc/geometry.h" #include "openmc/geometry_aux.h" +#include "openmc/material.h" #include "openmc/string_utils.h" #include "openmc/settings.h" -#include "openmc/geometry.h" #ifdef DAGMC @@ -88,13 +89,60 @@ bool write_uwuw_materials_xml() { return found_uwuw_mats; } +void legacy_assign_material(const std::string& mat_string, DAGCell* c) +{ + bool mat_found_by_name = false; + // attempt to find a material with a matching name + for (const auto& m : model::materials) { + if (mat_string == m->name_) { + // assign the material with that name + if (!mat_found_by_name) { + mat_found_by_name = true; + c->material_.push_back(m->id_); + // report error if more than one material is found + } else { + std::stringstream err_msg; + err_msg << "More than one material found with name " << mat_string + << ". Please ensure materials have unique names if using this" + << " property to assign materials."; + fatal_error(err_msg); + } + } + } + + // if no material was set using a name, assign by id + if (!mat_found_by_name) { + try { + auto id = std::stoi(mat_string); + c->material_.emplace_back(id); + } catch (const std::invalid_argument&) { + std::stringstream err_msg; + err_msg << "No material " << mat_string + << " found for volume (cell) " << c->id_; + fatal_error(err_msg); + } + } + + if (settings::verbosity >= 10) { + Material* m = model::materials[model::material_map[c->material_[0]]].get(); + std::stringstream msg; + msg << "DAGMC material " << mat_string << " was assigned"; + if (mat_found_by_name) { + msg << " using material name: " << m->name_; + } else { + msg << " using material id: " << m->id_; + } + write_message(msg.str(), 10); + } +} + void load_dagmc_geometry() { if (!model::DAG) { model::DAG = new moab::DagMC(); } - /// Materials \\\ + // --- Materials --- // create uwuw instance UWUW uwuw(DAGMC_FILENAME.c_str()); @@ -128,7 +176,7 @@ void load_dagmc_geometry() rval = model::DAG->parse_properties(keywords, dum, delimiters.c_str()); MB_CHK_ERR_CONT(rval); - /// Cells (Volumes) \\\ + // --- Cells (Volumes) --- // initialize cell objects int n_cells = model::DAG->num_entities(3); @@ -138,7 +186,8 @@ void load_dagmc_geometry() // set cell ids using global IDs DAGCell* c = new DAGCell(); - c->id_ = model::DAG->id_by_index(3, i+1); + c->dag_index_ = i+1; + c->id_ = model::DAG->id_by_index(3, c->dag_index_); c->dagmc_ptr_ = model::DAG; c->universe_ = dagmc_univ_id; // set to zero for now c->fill_ = C_NONE; // no fill, single universe @@ -187,7 +236,7 @@ void load_dagmc_geometry() size_t _comp_pos = mat_value.find(_comp); if (_comp_pos != std::string::npos) { mat_value.erase(_comp_pos, _comp.length()); } // assign IC material by id - c->material_.push_back(std::stoi(mat_value)); + legacy_assign_material(mat_value, c); } } else { // if no material is found, the implicit complement is void @@ -234,9 +283,7 @@ void load_dagmc_geometry() fatal_error(err_msg); } } else { - // if not using UWUW materials, we'll find this material - // later in the materials.xml - c->material_.push_back(std::stoi(mat_value)); + legacy_assign_material(mat_value, c); } } } @@ -251,7 +298,7 @@ void load_dagmc_geometry() "This may result in lost particles and rapid simulation failure."); } - /// Surfaces \\\ + // --- Surfaces --- // initialize surface objects int n_surfaces = model::DAG->num_entities(2); @@ -261,7 +308,8 @@ void load_dagmc_geometry() // set cell ids using global IDs DAGSurface* s = new DAGSurface(); - s->id_ = model::DAG->id_by_index(2, i+1); + s->dag_index_ = i+1; + s->id_ = model::DAG->id_by_index(2, s->dag_index_); s->dagmc_ptr_ = model::DAG; // set BCs @@ -303,7 +351,7 @@ void load_dagmc_geometry() // add to global array and map model::surfaces.emplace_back(s); - model::surface_map[s->id_] = s->id_; + model::surface_map[s->id_] = i; } return; diff --git a/src/distribution_energy.cpp b/src/distribution_energy.cpp index e311e322d..1b9be7346 100644 --- a/src/distribution_energy.cpp +++ b/src/distribution_energy.cpp @@ -245,6 +245,9 @@ double ContinuousTabular::sample(double E) const E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k + 2.0*frac*(r1 - c_k))) - p_l_k)/frac; } + } else { + throw std::runtime_error{"Unexpected interpolation for continuous energy " + "distribution."}; } // Now interpolate between incident energy bins i and i + 1 diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index c995a02d7..08a4d3f91 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -20,9 +20,14 @@ #include "openmc/timer.h" #include "openmc/tallies/tally.h" +#ifdef _OPENMP +#include +#endif + #include // for min #include #include // for sqrt, abs, pow +#include // for back_inserter #include namespace openmc { @@ -81,21 +86,22 @@ void synchronize_bank() #ifdef OPENMC_MPI int64_t start = 0; - MPI_Exscan(&simulation::n_bank, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); + int64_t n_bank = simulation::fission_bank.size(); + MPI_Exscan(&n_bank, &start, 1, MPI_INT64_T, MPI_SUM, mpi::intracomm); // While we would expect the value of start on rank 0 to be 0, the MPI // standard says that the receive buffer on rank 0 is undefined and not // significant if (mpi::rank == 0) start = 0; - int64_t finish = start + simulation::n_bank; + int64_t finish = start + simulation::fission_bank.size(); int64_t total = finish; MPI_Bcast(&total, 1, MPI_INT64_T, mpi::n_procs - 1, mpi::intracomm); #else int64_t start = 0; - int64_t finish = simulation::n_bank; - int64_t total = simulation::n_bank; + int64_t finish = simulation::fission_bank.size(); + int64_t total = simulation::fission_bank.size(); #endif // If there are not that many particles per generation, it's possible that no @@ -103,7 +109,7 @@ void synchronize_bank() // extra logic to treat this circumstance, we really want to ensure the user // runs enough particles to avoid this in the first place. - if (simulation::n_bank == 0) { + if (simulation::fission_bank.empty()) { fatal_error("No fission sites banked on MPI rank " + std::to_string(mpi::rank)); } @@ -132,23 +138,23 @@ void synchronize_bank() // Allocate temporary source bank int64_t index_temp = 0; - std::vector temp_sites(3*simulation::work); + std::vector temp_sites(3*simulation::work_per_rank); - for (int64_t i = 0; i < simulation::n_bank; ++i) { + for (const auto& site : simulation::fission_bank) { // If there are less than n_particles particles banked, automatically add // int(n_particles/total) sites to temp_sites. For example, if you need // 1000 and 300 were banked, this would add 3 source sites per banked site // and the remaining 100 would be randomly sampled. if (total < settings::n_particles) { for (int64_t j = 1; j <= settings::n_particles / total; ++j) { - temp_sites[index_temp] = simulation::fission_bank[i]; + temp_sites[index_temp] = site; ++index_temp; } } // Randomly sample sites needed if (prn() < p_sample) { - temp_sites[index_temp] = simulation::fission_bank[i]; + temp_sites[index_temp] = site; ++index_temp; } } @@ -189,7 +195,7 @@ void synchronize_bank() // fission bank sites_needed = settings::n_particles - finish; for (int i = 0; i < sites_needed; ++i) { - int i_bank = simulation::n_bank - sites_needed + i; + int i_bank = simulation::fission_bank.size() - sites_needed + i; temp_sites[index_temp] = simulation::fission_bank[i_bank]; ++index_temp; } @@ -346,38 +352,32 @@ void calculate_average_keff() #ifdef _OPENMP void join_bank_from_threads() { - // Initialize the total number of fission bank sites - int64_t total = 0; + int n_threads = omp_get_max_threads(); -#pragma omp parallel + #pragma omp parallel { // Copy thread fission bank sites to one shared copy -#pragma omp for ordered schedule(static) - for (int i = 0; i < simulation::n_threads; ++i) { -#pragma omp ordered + #pragma omp for ordered schedule(static) + for (int i = 0; i < n_threads; ++i) { + #pragma omp ordered { std::copy( - &simulation::fission_bank[0], - &simulation::fission_bank[0] + simulation::n_bank, - &simulation::master_fission_bank[total] + simulation::fission_bank.cbegin(), + simulation::fission_bank.cend(), + std::back_inserter(simulation::master_fission_bank) ); - total += simulation::n_bank; } } // Make sure all threads have made it to this point -#pragma omp barrier + #pragma omp barrier // Now copy the shared fission bank sites back to the master thread's copy. - if (simulation::thread_id == 0) { - simulation::n_bank = total; - std::copy( - &simulation::master_fission_bank[0], - &simulation::master_fission_bank[0] + simulation::n_bank, - &simulation::fission_bank[0] - ); + if (omp_get_thread_num() == 0) { + simulation::fission_bank = simulation::master_fission_bank; + simulation::master_fission_bank.clear(); } else { - simulation::n_bank = 0; + simulation::fission_bank.clear(); } } } @@ -537,8 +537,8 @@ void shannon_entropy() // Get source weight in each mesh bin bool sites_outside; - xt::xtensor p = m->count_sites(simulation::n_bank, - simulation::fission_bank.data(), 0, nullptr, &sites_outside); + xt::xtensor p = m->count_sites(simulation::fission_bank, + &sites_outside); // display warning message if there were sites outside entropy box if (sites_outside) { @@ -577,8 +577,8 @@ void ufs_count_sites() } else { // count number of source sites in each ufs mesh cell bool sites_outside; - simulation::source_frac = m->count_sites(simulation::work, - simulation::source_bank.data(), 0, nullptr, &sites_outside); + simulation::source_frac = m->count_sites(simulation::source_bank, + &sites_outside); // Check for sites outside of the mesh if (mpi::master && sites_outside) { @@ -597,7 +597,7 @@ void ufs_count_sites() // Since the total starting weight is not equal to n_particles, we need to // renormalize the weight of the source sites - for (int i = 0; i < simulation::work; ++i) { + for (int i = 0; i < simulation::work_per_rank; ++i) { simulation::source_bank[i].wgt *= settings::n_particles / total; } } diff --git a/src/endf.cpp b/src/endf.cpp index 86f08b8a8..464bc974c 100644 --- a/src/endf.cpp +++ b/src/endf.cpp @@ -164,9 +164,8 @@ double Tabulated1D::operator()(double x) const Interpolation interp; if (n_regions_ == 0) { interp = Interpolation::lin_lin; - } else if (n_regions_ == 1) { + } else { interp = int_[0]; - } else if (n_regions_ > 1) { for (int j = 0; j < n_regions_; ++j) { if (i < nbt_[j]) { interp = int_[j]; diff --git a/src/geometry.cpp b/src/geometry.cpp index 3c1c11b0e..aec272ae1 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -22,6 +22,7 @@ namespace openmc { namespace model { int root_universe {-1}; +int n_coord_levels; std::vector overlap_check_count; @@ -31,15 +32,13 @@ std::vector overlap_check_count; // Non-member functions //============================================================================== -extern "C" bool -check_cell_overlap(Particle* p) +bool check_cell_overlap(Particle* p) { int n_coord = p->n_coord_; // Loop through each coordinate level for (int j = 0; j < n_coord; j++) { Universe& univ = *model::universes[p->coord_[j].universe]; - int n = univ.cells_.size(); // Loop through each cell on this level for (auto index_cell : univ.cells_) { @@ -90,7 +89,12 @@ find_cell_inner(Particle* p, const NeighborList* neighbor_list) } else { int i_universe = p->coord_[p->n_coord_-1].universe; - const auto& cells {model::universes[i_universe]->cells_}; + const auto& univ {*model::universes[i_universe]}; + const auto& cells { + !univ.partitioner_ + ? model::universes[i_universe]->cells_ + : univ.partitioner_->get_cells(p->r_local(), p->u_local()) + }; for (auto it = cells.cbegin(); it != cells.cend(); it++) { i_cell = *it; @@ -245,7 +249,7 @@ find_cell_inner(Particle* p, const NeighborList* neighbor_list) //============================================================================== -extern "C" bool +bool find_cell(Particle* p, bool use_neighbor_lists) { // Determine universe (if not yet set, use root universe). @@ -257,7 +261,7 @@ find_cell(Particle* p, bool use_neighbor_lists) } // Reset all the deeper coordinate levels. - for (int i = p->n_coord_; i < MAX_COORD; i++) { + for (int i = p->n_coord_; i < p->coord_.size(); i++) { p->coord_[i].reset(); } @@ -287,8 +291,8 @@ find_cell(Particle* p, bool use_neighbor_lists) //============================================================================== -extern "C" void -cross_lattice(Particle* p, int lattice_translation[3]) +void +cross_lattice(Particle* p, const BoundaryInfo& boundary) { auto& lat {*model::lattices[p->coord_[p->n_coord_-1].lattice]}; @@ -302,9 +306,9 @@ cross_lattice(Particle* p, int lattice_translation[3]) } // Set the lattice indices. - p->coord_[p->n_coord_-1].lattice_x += lattice_translation[0]; - p->coord_[p->n_coord_-1].lattice_y += lattice_translation[1]; - p->coord_[p->n_coord_-1].lattice_z += lattice_translation[2]; + p->coord_[p->n_coord_-1].lattice_x += boundary.lattice_translation[0]; + p->coord_[p->n_coord_-1].lattice_y += boundary.lattice_translation[1]; + p->coord_[p->n_coord_-1].lattice_z += boundary.lattice_translation[2]; std::array i_xyz {p->coord_[p->n_coord_-1].lattice_x, p->coord_[p->n_coord_-1].lattice_y, p->coord_[p->n_coord_-1].lattice_z}; @@ -345,18 +349,13 @@ cross_lattice(Particle* p, int lattice_translation[3]) //============================================================================== -extern "C" void -distance_to_boundary(Particle* p, double* dist, int* surface_crossed, - int lattice_translation[3], int* next_level) +BoundaryInfo distance_to_boundary(Particle* p) { - *dist = INFINITY; + BoundaryInfo info; double d_lat = INFINITY; double d_surf = INFINITY; - lattice_translation[0] = 0; - lattice_translation[1] = 0; - lattice_translation[2] = 0; int32_t level_surf_cross; - std::array level_lat_trans; + std::array level_lat_trans {}; // Loop over each coordinate level. for (int i = 0; i < p->n_coord_; i++) { @@ -401,43 +400,43 @@ distance_to_boundary(Particle* p, double* dist, int* surface_crossed, // If the boundary on this coordinate level is coincident with a boundary on // a higher level then we need to make sure that the higher level boundary // is selected. This logic must consider floating point precision. - if (d_surf < d_lat) { - if (*dist == INFINITY || ((*dist) - d_surf)/(*dist) >= FP_REL_PRECISION) { - *dist = d_surf; + double& d = info.distance; + if (d_surf < d_lat - FP_COINCIDENT) { + if (d == INFINITY || (d - d_surf)/d >= FP_REL_PRECISION) { + d = d_surf; // If the cell is not simple, it is possible that both the negative and // positive half-space were given in the region specification. Thus, we // have to explicitly check which half-space the particle would be // traveling into if the surface is crossed if (c.simple_) { - *surface_crossed = level_surf_cross; + info.surface_index = level_surf_cross; } else { Position r_hit = r + d_surf * u; Surface& surf {*model::surfaces[std::abs(level_surf_cross)-1]}; Direction norm = surf.normal(r_hit); if (u.dot(norm) > 0) { - *surface_crossed = std::abs(level_surf_cross); + info.surface_index = std::abs(level_surf_cross); } else { - *surface_crossed = -std::abs(level_surf_cross); + info.surface_index = -std::abs(level_surf_cross); } } - lattice_translation[0] = 0; - lattice_translation[1] = 0; - lattice_translation[2] = 0; - *next_level = i + 1; + info.lattice_translation[0] = 0; + info.lattice_translation[1] = 0; + info.lattice_translation[2] = 0; + info.coord_level = i + 1; } } else { - if (*dist == INFINITY || ((*dist) - d_lat)/(*dist) >= FP_REL_PRECISION) { - *dist = d_lat; - *surface_crossed = F90_NONE; - lattice_translation[0] = level_lat_trans[0]; - lattice_translation[1] = level_lat_trans[1]; - lattice_translation[2] = level_lat_trans[2]; - *next_level = i + 1; + if (d == INFINITY || (d - d_lat)/d >= FP_REL_PRECISION) { + d = d_lat; + info.surface_index = 0; + info.lattice_translation = level_lat_trans; + info.coord_level = i + 1; } } } + return info; } //============================================================================== diff --git a/src/geometry_aux.cpp b/src/geometry_aux.cpp index c560743f8..f02ee42b1 100644 --- a/src/geometry_aux.cpp +++ b/src/geometry_aux.cpp @@ -120,6 +120,40 @@ adjust_indices() } } +//============================================================================== +//! Partition some universes with many z-planes for faster find_cell searches. + +void +partition_universes() +{ + // Iterate over universes with more than 10 cells. (Fewer than 10 is likely + // not worth partitioning.) + for (const auto& univ : model::universes) { + if (univ->cells_.size() > 10) { + // Collect the set of surfaces in this universe. + std::unordered_set surf_inds; + for (auto i_cell : univ->cells_) { + for (auto token : model::cells[i_cell]->rpn_) { + if (token < OP_UNION) surf_inds.insert(std::abs(token) - 1); + } + } + + // Partition the universe if there are more than 5 z-planes. (Fewer than + // 5 is likely not worth it.) + int n_zplanes = 0; + for (auto i_surf : surf_inds) { + if (dynamic_cast(model::surfaces[i_surf].get())) { + ++n_zplanes; + if (n_zplanes > 5) { + univ->partitioner_ = std::make_unique(*univ); + break; + } + } + } + } + } +} + //============================================================================== void @@ -210,6 +244,7 @@ void finalize_geometry(std::vector>& nuc_temps, // Perform some final operations to set up the geometry adjust_indices(); count_cell_instances(model::root_universe); + partition_universes(); // Assign temperatures to cells that don't have temperatures already assigned assign_temperatures(); @@ -217,14 +252,8 @@ void finalize_geometry(std::vector>& nuc_temps, // Determine desired temperatures for each nuclide and S(a,b) table get_temperatures(nuc_temps, thermal_temps); - // Check to make sure there are not too many nested coordinate levels in the - // geometry since the coordinate list is statically allocated for performance - // reasons - if (maximum_levels(model::root_universe) > MAX_COORD) { - fatal_error("Too many nested coordinate levels in the geometry. " - "Try increasing the maximum number of coordinate levels by " - "providing the CMake -Dmaxcoord= option."); - } + // Determine number of nested coordinate levels in the geometry + model::n_coord_levels = maximum_levels(model::root_universe); } //============================================================================== diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index d85a4cc9e..1fb10b706 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "xtensor/xtensor.hpp" @@ -47,6 +48,8 @@ get_shape(hid_t obj_id, hsize_t* dims) dspace = H5Dget_space(obj_id); } else if (type == H5I_ATTR) { dspace = H5Aget_space(obj_id); + } else { + throw std::runtime_error{"Expected dataset or attribute in call to get_shape."}; } H5Sget_simple_extent_dims(dspace, dims, nullptr); H5Sclose(dspace); @@ -70,6 +73,8 @@ std::vector object_shape(hid_t obj_id) dspace = H5Dget_space(obj_id); } else if (type == H5I_ATTR) { dspace = H5Aget_space(obj_id); + } else { + throw std::runtime_error{"Expected dataset or attribute in call to object_shape."}; } int n = H5Sget_simple_extent_ndims(dspace); diff --git a/src/initialize.cpp b/src/initialize.cpp index 3aa31184f..f8a4f4a39 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -121,7 +121,6 @@ void initialize_mpi(MPI_Comm intracomm) int parse_command_line(int argc, char* argv[]) { - char buffer[256]; // buffer for reading attribute int last_flag = 0; for (int i=1; i < argc; ++i) { std::string arg {argv[i]}; @@ -196,13 +195,13 @@ parse_command_line(int argc, char* argv[]) #ifdef _OPENMP // Read and set number of OpenMP threads - simulation::n_threads = std::stoi(argv[i]); - if (simulation::n_threads < 1) { + int n_threads = std::stoi(argv[i]); + if (n_threads < 1) { std::string msg {"Number of threads must be positive."}; strcpy(openmc_err_msg, msg.c_str()); return OPENMC_E_INVALID_ARGUMENT; } - omp_set_num_threads(simulation::n_threads); + omp_set_num_threads(n_threads); #else if (mpi::master) warning("Ignoring number of threads specified on command line."); diff --git a/src/lattice.cpp b/src/lattice.cpp index 6e0f2331f..b16fecc6e 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -6,6 +6,7 @@ #include "openmc/cell.h" #include "openmc/error.h" +#include "openmc/geometry.h" #include "openmc/geometry_aux.h" #include "openmc/hdf5_interface.h" #include "openmc/string_utils.h" @@ -291,7 +292,7 @@ RectLattice::get_indices(Position r, Direction u) const double ix_ {(r.x - lower_left_.x) / pitch_.x}; long ix_close {std::lround(ix_)}; int ix; - if (std::abs(ix_ - ix_close) < FP_COINCIDENT) { + if (coincident(ix_, ix_close)) { ix = (u.x > 0) ? ix_close : ix_close - 1; } else { ix = std::floor(ix_); @@ -301,7 +302,7 @@ RectLattice::get_indices(Position r, Direction u) const double iy_ {(r.y - lower_left_.y) / pitch_.y}; long iy_close {std::lround(iy_)}; int iy; - if (std::abs(iy_ - iy_close) < FP_COINCIDENT) { + if (coincident(iy_, iy_close)) { iy = (u.y > 0) ? iy_close : iy_close - 1; } else { iy = std::floor(iy_); @@ -312,7 +313,7 @@ RectLattice::get_indices(Position r, Direction u) const if (is_3d_) { double iz_ {(r.z - lower_left_.z) / pitch_.z}; long iz_close {std::lround(iz_)}; - if (std::abs(iz_ - iz_close) < FP_COINCIDENT) { + if (coincident(iz_, iz_close)) { iz = (u.z > 0) ? iz_close : iz_close - 1; } else { iz = std::floor(iz_); @@ -710,69 +711,84 @@ const std::array HexLattice::get_indices(Position r, Direction u) const { - // The implementation for HexLattice currently doesn't use direction - // information. As a result, we move the position slightly forward to - // determine what lattice index the particle is most likely to be in. - r += TINY_BIT * u; - // Offset the xyz by the lattice center. Position r_o {r.x - center_.x, r.y - center_.y, r.z}; if (is_3d_) {r_o.z -= center_.z;} - // Index the z direction. - std::array out; + // Index the z direction, accounting for coincidence + int iz = 0; if (is_3d_) { - out[2] = std::floor(r_o.z / pitch_[1] + 0.5 * n_axial_); - } else { - out[2] = 0; + double iz_ {r_o.z / pitch_[1] + 0.5 * n_axial_}; + long iz_close {std::lround(iz_)}; + if (coincident(iz_, iz_close)) { + iz = (u.z > 0) ? iz_close : iz_close - 1; + } else { + iz = std::floor(iz_); + } } // Convert coordinates into skewed bases. The (x, alpha) basis is used to // find the index of the global coordinates to within 4 cells. double alpha = r_o.y - r_o.x / std::sqrt(3.0); - out[0] = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0])); - out[1] = std::floor(alpha / pitch_[0]); + int ix = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0])); + int ia = std::floor(alpha / pitch_[0]); // Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but // the array is offset so that the indices never go below 0). - out[0] += n_rings_-1; - out[1] += n_rings_-1; + ix += n_rings_-1; + ia += n_rings_-1; // Calculate the (squared) distance between the particle and the centers of // the four possible cells. Regular hexagonal tiles form a Voronoi // tessellation so the xyz should be in the hexagonal cell that it is closest // to the center of. This method is used over a method that uses the // remainders of the floor divisions above because it provides better finite - // precision performance. Squared distances are used becasue they are more + // precision performance. Squared distances are used because they are more // computationally efficient than normal distances. - int k {1}; - int k_min {1}; + + // COINCIDENCE CHECK + // if a distance to center, d, is within the coincidence tolerance of the + // current minimum distance, d_min, the particle is on an edge or vertex. + // In this case, the dot product of the position vector and direction vector + // for the current indices, dp, and the dot product for the currently selected + // indices, dp_min, are compared. The cell which the particle is moving into + // is kept (i.e. the cell with the lowest dot product as the vectors will be + // completely opposed if the particle is moving directly toward the center of + // the cell). + int ix_chg {}; + int ia_chg {}; double d_min {INFTY}; + double dp_min {INFTY}; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { - const std::array i_xyz {out[0] + j, out[1] + i, 0}; + // get local coordinates + const std::array i_xyz {ix + j, ia + i, 0}; Position r_t = get_local_position(r, i_xyz); + // calculate distance double d = r_t.x*r_t.x + r_t.y*r_t.y; - if (d < d_min) { + // check for coincidence + bool on_boundary = coincident(d, d_min); + if (d < d_min || on_boundary) { + // normalize r_t and find dot product + r_t /= std::sqrt(d); + double dp = u.x * r_t.x + u.y * r_t.y; + // do not update values if particle is on a + // boundary and not moving into this cell + if (on_boundary && dp > dp_min) continue; + // update values d_min = d; - k_min = k; + ix_chg = j; + ia_chg = i; + dp_min = dp; } - k++; } } - // Select the minimum squared distance which corresponds to the cell the - // coordinates are in. - if (k_min == 2) { - ++out[0]; - } else if (k_min == 3) { - ++out[1]; - } else if (k_min == 4) { - ++out[0]; - ++out[1]; - } + // update outgoing indices + ix += ix_chg; + ia += ia_chg; - return out; + return {ix, ia, iz}; } //============================================================================== @@ -800,7 +816,6 @@ HexLattice::is_valid_index(int indx) const { int nx {2*n_rings_ - 1}; int ny {2*n_rings_ - 1}; - int nz {n_axial_}; int iz = indx / (nx * ny); int iy = (indx - nx*ny*iz) / nx; int ix = indx - nx*ny*iz - nx*iy; diff --git a/src/material.cpp b/src/material.cpp index e6b2ca458..de0d7e5de 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -451,6 +451,101 @@ void Material::init_thermal() thermal_tables_ = tables; } +void Material::collision_stopping_power(double* s_col, bool positron) +{ + // Average electron number and average atomic weight + double electron_density = 0.0; + double mass_density = 0.0; + + // Log of the mean excitation energy of the material + double log_I = 0.0; + + // Effective number of conduction electrons in the material + double n_conduction = 0.0; + + // Oscillator strength and square of the binding energy for each oscillator + // in material + std::vector f; + std::vector e_b_sq; + + for (int i = 0; i < element_.size(); ++i) { + const auto& elm = data::elements[element_[i]]; + double awr = data::nuclides[nuclide_[i]]->awr_; + + // Get atomic density of nuclide given atom/weight percent + double atom_density = (atom_density_[0] > 0.0) ? + atom_density_[i] : -atom_density_[i] / awr; + + electron_density += atom_density * elm.Z_; + mass_density += atom_density * awr * MASS_NEUTRON; + log_I += atom_density * elm.Z_ * std::log(elm.I_); + + for (int j = 0; j < elm.n_electrons_.size(); ++j) { + if (elm.n_electrons_[j] < 0) { + n_conduction -= elm.n_electrons_[j] * atom_density; + continue; + } + e_b_sq.push_back(elm.ionization_energy_[j] * elm.ionization_energy_[j]); + f.push_back(elm.n_electrons_[j] * atom_density); + } + } + log_I /= electron_density; + n_conduction /= electron_density; + for (auto& f_i : f) f_i /= electron_density; + + // Get density in g/cm^3 if it is given in atom/b-cm + double density = (density_ < 0.0) ? -density_ : mass_density / N_AVOGADRO; + + // Calculate the square of the plasma energy + double e_p_sq = PLANCK_C * PLANCK_C * PLANCK_C * N_AVOGADRO * + electron_density * density / (2.0 * PI * PI * FINE_STRUCTURE * + MASS_ELECTRON_EV * mass_density); + + // Get the Sternheimer adjustment factor + double rho = sternheimer_adjustment(f, e_b_sq, e_p_sq, n_conduction, log_I, + 1.0e-6, 100); + + // Classical electron radius in cm + constexpr double CM_PER_ANGSTROM {1.0e-8}; + constexpr double r_e = CM_PER_ANGSTROM * PLANCK_C / (2.0 * PI * + FINE_STRUCTURE * MASS_ELECTRON_EV); + + // Constant in expression for collision stopping power + constexpr double BARN_PER_CM_SQ {1.0e24}; + double c = BARN_PER_CM_SQ * 2.0 * PI * r_e * r_e * MASS_ELECTRON_EV * + electron_density; + + // Loop over incident charged particle energies + for (int i = 0; i < data::ttb_e_grid.size(); ++i) { + double E = data::ttb_e_grid(i); + + // Get the density effect correction + double delta = density_effect(f, e_b_sq, e_p_sq, n_conduction, rho, E, + 1.0e-6, 100); + + // Square of the ratio of the speed of light to the velocity of the charged + // particle + double beta_sq = E * (E + 2.0 * MASS_ELECTRON_EV) / ((E + MASS_ELECTRON_EV) + * (E + MASS_ELECTRON_EV)); + + double tau = E / MASS_ELECTRON_EV; + + double F; + if (positron) { + double t = tau + 2.0; + F = std::log(4.0) - (beta_sq / 12.0) * (23.0 + 14.0 / t + 10.0 / (t * t) + + 4.0 / (t * t * t)); + } else { + F = (1.0 - beta_sq) * (1.0 + tau * tau / 8.0 - (2.0 * tau + 1.0) * + std::log(2.0)); + } + + // Calculate the collision stopping power for this energy + s_col[i] = c / beta_sq * (2.0 * (std::log(E) - log_I) + std::log(1.0 + tau + / 2.0) + F - delta); + } +} + void Material::init_bremsstrahlung() { // Create new object @@ -481,16 +576,11 @@ void Material::init_bremsstrahlung() double Z_eq_sq = 0.0; double sum_density = 0.0; - // Calculate the molecular DCS and the molecular total stopping power using + // Get the collision stopping power of the material + this->collision_stopping_power(stopping_power_collision.data(), positron); + + // Calculate the molecular DCS and the molecular radiative stopping power using // Bragg's additivity rule. - // TODO: The collision stopping power cannot be accurately calculated using - // Bragg's additivity rule since the mean excitation energies and the - // density effect corrections cannot simply be summed together. Bragg's - // additivity rule fails especially when a higher-density compound is - // composed of elements that are in lower-density form at normal temperature - // and pressure (at which the NIST stopping powers are given). It will be - // used to approximate the collision stopping powers for now, but should be - // fixed in the future. for (int i = 0; i < n; ++i) { // Get pointer to current element const auto& elm = data::elements[element_[i]]; @@ -499,7 +589,6 @@ void Material::init_bremsstrahlung() // Get atomic density and mass density of nuclide given atom/weight percent double atom_density = (atom_density_[0] > 0.0) ? atom_density_[i] : -atom_density_[i] / awr; - double mass_density = atom_density * awr; // Calculate the "equivalent" atomic number Zeq of the material Z_eq_sq += atom_density * elm.Z_ * elm.Z_; @@ -508,13 +597,8 @@ void Material::init_bremsstrahlung() // Accumulate material DCS dcs += (atom_density * elm.Z_ * elm.Z_) * elm.dcs_; - // Accumulate material collision stopping power - stopping_power_collision += (mass_density * MASS_NEUTRON / N_AVOGADRO) - * elm.stopping_power_collision_; - // Accumulate material radiative stopping power - stopping_power_radiative += (mass_density * MASS_NEUTRON / N_AVOGADRO) - * elm.stopping_power_radiative_; + stopping_power_radiative += atom_density * elm.stopping_power_radiative_; } Z_eq_sq /= sum_density; @@ -569,12 +653,13 @@ void Material::init_bremsstrahlung() // photon energy k double x = x_l + (k - k_l)*(x_r - x_l)/(k_r - k_l); - // Ratio of the velocity of the charged particle to the speed of light - double beta = std::sqrt(e*(e + 2.0*MASS_ELECTRON_EV)) / - (e + MASS_ELECTRON_EV); + // Square of the ratio of the speed of light to the velocity of the + // charged particle + double beta_sq = e * (e + 2.0 * MASS_ELECTRON_EV) / ((e + + MASS_ELECTRON_EV) * (e + MASS_ELECTRON_EV)); // Compute the integrand of the PDF - f(j) = x / (beta*beta * stopping_power(j) * w); + f(j) = x / (beta_sq * stopping_power(j) * w); } // Number of points to integrate @@ -644,13 +729,13 @@ void Material::init_nuclide_index() } } -void Material::calculate_xs(const Particle& p) const +void Material::calculate_xs(Particle& p) const { // Set all material macroscopic cross sections to zero - simulation::material_xs.total = 0.0; - simulation::material_xs.absorption = 0.0; - simulation::material_xs.fission = 0.0; - simulation::material_xs.nu_fission = 0.0; + p.macro_xs_.total = 0.0; + p.macro_xs_.absorption = 0.0; + p.macro_xs_.fission = 0.0; + p.macro_xs_.nu_fission = 0.0; if (p.type_ == Particle::Type::neutron) { this->calculate_neutron_xs(p); @@ -659,7 +744,7 @@ void Material::calculate_xs(const Particle& p) const } } -void Material::calculate_neutron_xs(const Particle& p) const +void Material::calculate_neutron_xs(Particle& p) const { // Find energy index on energy grid int neutron = static_cast(Particle::Type::neutron); @@ -707,13 +792,12 @@ void Material::calculate_neutron_xs(const Particle& p) const int i_nuclide = nuclide_[i]; // Calculate microscopic cross section for this nuclide - const auto& micro {simulation::micro_xs[i_nuclide]}; + const auto& micro {p.neutron_xs_[i_nuclide]}; if (p.E_ != micro.last_E || p.sqrtkT_ != micro.last_sqrtkT || i_sab != micro.index_sab || sab_frac != micro.sab_frac) { - data::nuclides[i_nuclide]->calculate_xs(i_sab, p.E_, i_grid, - p.sqrtkT_, sab_frac); + data::nuclides[i_nuclide]->calculate_xs(i_sab, i_grid, sab_frac, p); } // ====================================================================== @@ -723,19 +807,19 @@ void Material::calculate_neutron_xs(const Particle& p) const double atom_density = atom_density_(i); // Add contributions to cross sections - simulation::material_xs.total += atom_density * micro.total; - simulation::material_xs.absorption += atom_density * micro.absorption; - simulation::material_xs.fission += atom_density * micro.fission; - simulation::material_xs.nu_fission += atom_density * micro.nu_fission; + p.macro_xs_.total += atom_density * micro.total; + p.macro_xs_.absorption += atom_density * micro.absorption; + p.macro_xs_.fission += atom_density * micro.fission; + p.macro_xs_.nu_fission += atom_density * micro.nu_fission; } } -void Material::calculate_photon_xs(const Particle& p) const +void Material::calculate_photon_xs(Particle& p) const { - simulation::material_xs.coherent = 0.0; - simulation::material_xs.incoherent = 0.0; - simulation::material_xs.photoelectric = 0.0; - simulation::material_xs.pair_production = 0.0; + p.macro_xs_.coherent = 0.0; + p.macro_xs_.incoherent = 0.0; + p.macro_xs_.photoelectric = 0.0; + p.macro_xs_.pair_production = 0.0; // Add contribution from each nuclide in material for (int i = 0; i < nuclide_.size(); ++i) { @@ -746,9 +830,9 @@ void Material::calculate_photon_xs(const Particle& p) const int i_element = element_[i]; // Calculate microscopic cross section for this nuclide - const auto& micro {simulation::micro_photon_xs[i_element]}; + const auto& micro {p.photon_xs_[i_element]}; if (p.E_ != micro.last_E) { - data::elements[i_element].calculate_xs(p.E_); + data::elements[i_element].calculate_xs(p); } // ======================================================================== @@ -758,11 +842,11 @@ void Material::calculate_photon_xs(const Particle& p) const double atom_density = atom_density_(i); // Add contributions to material macroscopic cross sections - simulation::material_xs.total += atom_density * micro.total; - simulation::material_xs.coherent += atom_density * micro.coherent; - simulation::material_xs.incoherent += atom_density * micro.incoherent; - simulation::material_xs.photoelectric += atom_density * micro.photoelectric; - simulation::material_xs.pair_production += atom_density * micro.pair_production; + p.macro_xs_.total += atom_density * micro.total; + p.macro_xs_.coherent += atom_density * micro.coherent; + p.macro_xs_.incoherent += atom_density * micro.incoherent; + p.macro_xs_.photoelectric += atom_density * micro.photoelectric; + p.macro_xs_.pair_production += atom_density * micro.pair_production; } } @@ -866,6 +950,124 @@ void Material::to_hdf5(hid_t group) const // Non-method functions //============================================================================== +double sternheimer_adjustment(const std::vector& f, const + std::vector& e_b_sq, double e_p_sq, double n_conduction, double + log_I, double tol, int max_iter) +{ + // Get the total number of oscillators + int n = f.size(); + + // Calculate the Sternheimer adjustment factor using Newton's method + double rho = 2.0; + int iter; + for (iter = 0; iter < max_iter; ++iter) { + double rho_0 = rho; + + // Function to find the root of and its derivative + double g = 0.0; + double gp = 0.0; + + for (int i = 0; i < n; ++i) { + // Square of resonance energy of a bound-shell oscillator + double e_r_sq = e_b_sq[i] * rho * rho + 2.0 / 3.0 * f[i] * e_p_sq; + g += f[i] * std::log(e_r_sq); + gp += e_b_sq[i] * f[i] * rho / e_r_sq; + } + // Include conduction electrons + if (n_conduction > 0.0) { + g += n_conduction * std::log(n_conduction * e_p_sq); + } + + // Set the next guess: rho_n+1 = rho_n - g(rho_n)/g'(rho_n) + rho -= (g - 2.0 * log_I) / (2.0 * gp); + + // If the initial guess is too large, rho can be negative + if (rho < 0.0) rho = rho_0 / 2.0; + + // Check for convergence + if (std::abs(rho - rho_0) / rho_0 < tol) break; + } + // Did not converge + if (iter >= max_iter) { + warning("Maximum Newton-Raphson iterations exceeded."); + rho = 1.0e-6; + } + return rho; +} + +double density_effect(const std::vector& f, const std::vector& + e_b_sq, double e_p_sq, double n_conduction, double rho, double E, double tol, + int max_iter) +{ + // Get the total number of oscillators + int n = f.size(); + + // Square of the ratio of the speed of light to the velocity of the charged + // particle + double beta_sq = E * (E + 2.0 * MASS_ELECTRON_EV) / ((E + MASS_ELECTRON_EV) * + (E + MASS_ELECTRON_EV)); + + // For nonmetals, delta = 0 for beta < beta_0, where beta_0 is obtained by + // setting the frequency w = 0. + double beta_0_sq = 0.0; + if (n_conduction == 0.0) { + for (int i = 0; i < n; ++i) { + beta_0_sq += f[i] * e_p_sq / (e_b_sq[i] * rho * rho); + } + beta_0_sq = 1.0 / (1.0 + beta_0_sq); + } + double delta = 0.0; + if (beta_sq < beta_0_sq) return delta; + + // Compute the square of the frequency w^2 using Newton's method, with the + // initial guess of w^2 equal to beta^2 * gamma^2 + double w_sq = E / MASS_ELECTRON_EV * (E / MASS_ELECTRON_EV + 2); + int iter; + for (iter = 0; iter < max_iter; ++iter) { + double w_sq_0 = w_sq; + + // Function to find the root of and its derivative + double g = 0.0; + double gp = 0.0; + + for (int i = 0; i < n; ++i) { + double c = e_b_sq[i] * rho * rho / e_p_sq + w_sq; + g += f[i] / c; + gp -= f[i] / (c * c); + } + // Include conduction electrons + g += n_conduction / w_sq; + gp -= n_conduction / (w_sq * w_sq); + + // Set the next guess: w_n+1 = w_n - g(w_n)/g'(w_n) + w_sq -= (g + 1.0 - 1.0 / beta_sq) / gp; + + // If the initial guess is too large, w can be negative + if (w_sq < 0.0) w_sq = w_sq_0 / 2.0; + + // Check for convergence + if (std::abs(w_sq - w_sq_0) / w_sq_0 < tol) break; + } + // Did not converge + if (iter >= max_iter) { + warning("Maximum Newton-Raphson iterations exceeded: setting density " + "effect correction to zero."); + return delta; + } + + // Solve for the density effect correction + for (int i = 0; i < n; ++i) { + double l_sq = e_b_sq[i] * rho * rho / e_p_sq + 2.0 / 3.0 * f[i]; + delta += f[i] * std::log((l_sq + w_sq)/l_sq); + } + // Include conduction electrons + if (n_conduction > 0.0) { + delta += n_conduction * std::log((n_conduction + w_sq) / n_conduction); + } + + return delta - w_sq * (1.0 - beta_sq); +} + void read_materials_xml() { write_message("Reading materials XML file...", 5); @@ -1081,12 +1283,11 @@ openmc_material_set_densities(int32_t index, int n, const char** name, const dou } // Set total density to the sum of the vector - int err = mat->set_density(sum_density, "atom/b-cm"); // Assign S(a,b) tables mat->init_thermal(); - return 0; + return err; } else { set_errmsg("Index in materials array is out of bounds."); return OPENMC_E_OUT_OF_BOUNDS; diff --git a/src/mesh.cpp b/src/mesh.cpp index 97ec81ba2..ddfa272a6 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -36,6 +36,32 @@ std::unordered_map mesh_map; } // namespace model +//============================================================================== +// Helper functions +//============================================================================== + +//! Update an intersection point if the given candidate is closer. +// +//! The first 6 arguments are coordinates for the starting point of a particle +//! and its intersection with a mesh surface. If the distance between these +//! two points is shorter than the given `min_distance`, then the `r` argument +//! will be updated to match the intersection point, and `min_distance` will +//! also be updated. + +inline bool check_intersection_point(double x1, double x0, double y1, + double y0, double z1, double z0, Position& r, double& min_distance) +{ + double dist = std::pow(x1-x0, 2) + std::pow(y1-y0, 2) + std::pow(z1-z0, 2); + if (dist < min_distance) { + r.x = x1; + r.y = y1; + r.z = z1; + min_distance = dist; + return true; + } + return false; +} + //============================================================================== // RegularMesh implementation //============================================================================== @@ -167,14 +193,14 @@ int RegularMesh::get_bin(Position r) const int RegularMesh::get_bin_from_indices(const int* ijk) const { switch (n_dimension_) { - case 1: - return ijk[0] - 1; - case 2: - return (ijk[1] - 1)*shape_[0] + ijk[0] - 1; - case 3: - return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0] - 1; - default: - throw std::runtime_error{"Invalid number of mesh dimensions"}; + case 1: + return ijk[0] - 1; + case 2: + return (ijk[1] - 1)*shape_[0] + ijk[0] - 1; + case 3: + return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0] - 1; + default: + throw std::runtime_error{"Invalid number of mesh dimensions"}; } } @@ -204,45 +230,72 @@ void RegularMesh::get_indices_from_bin(int bin, int* ijk) const } } -bool RegularMesh::intersects(Position r0, Position r1) const +bool RegularMesh::intersects(Position& r0, Position r1, int* ijk) const { switch(n_dimension_) { - case 1: - return intersects_1d(r0, r1); - case 2: - return intersects_2d(r0, r1); - case 3: - return intersects_3d(r0, r1); - default: - throw std::runtime_error{"Invalid number of mesh dimensions."}; + case 1: + return intersects_1d(r0, r1, ijk); + case 2: + return intersects_2d(r0, r1, ijk); + case 3: + return intersects_3d(r0, r1, ijk); + default: + throw std::runtime_error{"Invalid number of mesh dimensions."}; } } -bool RegularMesh::intersects_1d(Position r0, Position r1) const -{ - // Copy coordinates of mesh lower_left and upper_right - double left = lower_left_[0]; - double right = upper_right_[0]; - - // Check if line intersects either left or right surface - if (r0.x < left) { - return r1.x > left; - } else if (r0.x < right) { - return r1.x < left || r1.x > right; - } else { - return r1.x < right; - } -} - -bool RegularMesh::intersects_2d(Position r0, Position r1) const +bool RegularMesh::intersects_1d(Position& r0, Position r1, int* ijk) const { // Copy coordinates of starting point double x0 = r0.x; double y0 = r0.y; + double z0 = r0.z; // Copy coordinates of ending point double x1 = r1.x; double y1 = r1.y; + double z1 = r1.z; + + // Copy coordinates of mesh lower_left and upper_right + double xm0 = lower_left_[0]; + double xm1 = upper_right_[0]; + + double min_dist = INFTY; + + // Check if line intersects left surface -- calculate the intersection point + // (y,z) + if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { + double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0); + if (check_intersection_point(xm0, x0, yi, yi, zi, zi, r0, min_dist)) { + ijk[0] = 1; + } + } + + // Check if line intersects right surface -- calculate the intersection point + // (y,z) + if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) { + double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0); + if (check_intersection_point(xm1, x0, yi, yi, zi, zi, r0, min_dist)) { + ijk[0] = shape_[0]; + } + } + + return min_dist < INFTY; +} + +bool RegularMesh::intersects_2d(Position& r0, Position r1, int* ijk) const +{ + // Copy coordinates of starting point + double x0 = r0.x; + double y0 = r0.y; + double z0 = r0.z; + + // Copy coordinates of ending point + double x1 = r1.x; + double y1 = r1.y; + double z1 = r1.z; // Copy coordinates of mesh lower_left double xm0 = lower_left_[0]; @@ -252,44 +305,64 @@ bool RegularMesh::intersects_2d(Position r0, Position r1) const double xm1 = upper_right_[0]; double ym1 = upper_right_[1]; - // Check if line intersects left surface -- calculate the intersection point y + double min_dist = INFTY; + + // Check if line intersects left surface -- calculate the intersection point + // (y,z) if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0); if (yi >= ym0 && yi < ym1) { - return true; + if (check_intersection_point(xm0, x0, yi, y0, zi, zi, r0, min_dist)) { + ijk[0] = 1; + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + } } } // Check if line intersects back surface -- calculate the intersection point - // x + // (x,z) if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) { double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0); if (xi >= xm0 && xi < xm1) { - return true; + if (check_intersection_point(xi, x0, ym0, y0, zi, zi, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = 1; + } } } - // Check if line intersects right surface -- calculate the intersection - // point y + // Check if line intersects right surface -- calculate the intersection point + // (y,z) if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) { double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0); if (yi >= ym0 && yi < ym1) { - return true; + if (check_intersection_point(xm1, x0, yi, y0, zi, zi, r0, min_dist)) { + ijk[0] = shape_[0]; + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + } } } // Check if line intersects front surface -- calculate the intersection point - // x + // (x,z) if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) { double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0); if (xi >= xm0 && xi < xm1) { - return true; + if (check_intersection_point(xi, x0, ym1, y0, zi, zi, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = shape_[1]; + } } } - return false; + + return min_dist < INFTY; } -bool RegularMesh::intersects_3d(Position r0, Position r1) const +bool RegularMesh::intersects_3d(Position& r0, Position r1, int* ijk) const { // Copy coordinates of starting point double x0 = r0.x; @@ -311,13 +384,19 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double ym1 = upper_right_[1]; double zm1 = upper_right_[2]; + double min_dist = INFTY; + // Check if line intersects left surface -- calculate the intersection point // (y,z) if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0); if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { - return true; + if (check_intersection_point(xm0, x0, yi, y0, zi, z0, r0, min_dist)) { + ijk[0] = 1; + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]); + } } } @@ -327,7 +406,11 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0); double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0); if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { - return true; + if (check_intersection_point(xi, x0, ym0, y0, zi, z0, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = 1; + ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]); + } } } @@ -337,7 +420,11 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0); double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0); if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { - return true; + if (check_intersection_point(xi, x0, yi, y0, zm0, z0, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + ijk[2] = 1; + } } } @@ -347,7 +434,11 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0); if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { - return true; + if (check_intersection_point(xm1, x0, yi, y0, zi, z0, r0, min_dist)) { + ijk[0] = shape_[0]; + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]); + } } } @@ -357,7 +448,11 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0); double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0); if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { - return true; + if (check_intersection_point(xi, x0, ym1, y0, zi, z0, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = shape_[1]; + ijk[2] = std::ceil((zi - lower_left_[2]) / width_[2]); + } } } @@ -367,32 +462,38 @@ bool RegularMesh::intersects_3d(Position r0, Position r1) const double xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0); double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0); if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { - return true; + if (check_intersection_point(xi, x0, yi, y0, zm1, z0, r0, min_dist)) { + ijk[0] = std::ceil((xi - lower_left_[0]) / width_[0]); + ijk[1] = std::ceil((yi - lower_left_[1]) / width_[1]); + ijk[2] = shape_[2]; + } } } - return false; + + return min_dist < INFTY; } void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, std::vector& lengths) const { - constexpr int MAX_SEARCH_ITER = 100; - // ======================================================================== - // Determine if the track intersects the tally mesh. + // Determine where the track intersects the mesh and if it intersects at all. - // Copy the starting and ending coordinates of the particle. Offset these - // just a bit for the purposes of determining if there was an intersection - // in case the mesh surfaces coincide with lattice/geometric surfaces which - // might produce finite-precision errors. + // Copy the starting and ending coordinates of the particle. Position last_r {p->r_last_}; Position r {p->r()}; Direction u {p->u()}; + // Compute the length of the entire track. + double total_distance = (r - last_r).norm(); + + // While determining if this track intersects the mesh, offset the starting + // and ending coords by a bit. This avoid finite-precision errors that can + // occur when the mesh surfaces coincide with lattice or geometric surfaces. Position r0 = last_r + TINY_BIT*u; Position r1 = r - TINY_BIT*u; - // Determine indices for starting and ending location. + // Determine the mesh indices for the starting and ending coords. int n = n_dimension_; int ijk0[n], ijk1[n]; bool start_in_mesh; @@ -400,99 +501,32 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, bool end_in_mesh; get_indices(r1, ijk1, &end_in_mesh); - // Check if the track intersects any part of the mesh. - if (!start_in_mesh && !end_in_mesh) { - if (!intersects(r0, r1)) return; + // Reset coordinates and check for a mesh intersection if necessary. + if (start_in_mesh) { + // The initial coords lie in the mesh, use those coords for tallying. + r0 = last_r; + } else { + // The initial coords do not lie in the mesh. Check to see if the particle + // eventually intersects the mesh and compute the relevant coords and + // indices. + if (!intersects(r0, r1, ijk0)) return; } - - // ======================================================================== - // Figure out which mesh cell to tally. - - // Copy the un-modified coordinates the particle direction. - r0 = last_r; r1 = r; - // Compute the length of the entire track. - double total_distance = (r1 - r0).norm(); - - // We are looking for the first valid mesh bin. Check to see if the - // particle starts inside the mesh. - if (!start_in_mesh) { - double d[n]; - - // The particle does not start in the mesh. Note that we nudged the - // start and end coordinates by a TINY_BIT each so we will have - // difficulty resolving tracks that are less than 2*TINY_BIT in length. - // If the track is that short, it is also insignificant so we can - // safely ignore it in the tallies. - if (total_distance < 2*TINY_BIT) return; - - // The particle does not start in the mesh so keep iterating the ijk0 - // indices to cross the nearest mesh surface until we've found a valid - // bin. MAX_SEARCH_ITER prevents an infinite loop. - int search_iter = 0; - int j; - bool in_mesh = true; - for (int i = 0; i < n; ++i) { - if (ijk0[i] < 1 || ijk0[i] > shape_[i]) { - in_mesh = false; - break; - } - } - while (!in_mesh) { - if (search_iter == MAX_SEARCH_ITER) { - warning("Failed to find a mesh intersection on a tally mesh filter."); - return; - } - - for (j = 0; j < n; ++j) { - if (std::fabs(u[j]) < FP_PRECISION) { - d[j] = INFTY; - } else if (u[j] > 0.0) { - double xyz_cross = lower_left_[j] + ijk0[j] * width_[j]; - d[j] = (xyz_cross - r0[j]) / u[j]; - } else { - double xyz_cross = lower_left_[j] + (ijk0[j] - 1) * width_[j]; - d[j] = (xyz_cross - r0[j]) / u[j]; - } - } - - j = std::min_element(d, d+n) - d; - if (u[j] > 0.0) { - ++ijk0[j]; - } else { - --ijk0[j]; - } - - ++search_iter; - in_mesh = true; - for (int i = 0; i < n; ++i) { - if (ijk0[i] < 1 || ijk0[i] > shape_[i]) { - in_mesh = false; - break; - } - } - } - - // Advance position - r0 += d[j] * u; - } + // ======================================================================== + // Find which mesh cells are traversed and the length of each traversal. while (true) { - // ======================================================================== - // Compute the length of the track segment in the each mesh cell and return - if (std::equal(ijk0, ijk0+n, ijk1)) { // The track ends in this cell. Use the particle end location rather - // than the mesh surface. + // than the mesh surface and stop iterating. double distance = (r1 - r0).norm(); bins.push_back(get_bin_from_indices(ijk0)); lengths.push_back(distance / total_distance); break; } - // The track exits this cell. Determine the distance to the closest mesh - // surface. + // The track exits this cell. Determine the distance to each mesh surface. double d[n]; for (int k = 0; k < n; ++k) { if (std::fabs(u[k]) < FP_PRECISION) { @@ -506,14 +540,13 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, } } - // Assign the next tally bin and the score. + // Pick the closest mesh surface and append this traversal to the output. auto j = std::min_element(d, d+n) - d; double distance = d[j]; bins.push_back(get_bin_from_indices(ijk0)); lengths.push_back(distance / total_distance); - // Translate the starting coordintes by the distance to the oncoming mesh - // surface. + // Translate to the oncoming mesh surface. r0 += distance * u; // Increment the indices into the next mesh cell. @@ -536,7 +569,8 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, } } -void RegularMesh::surface_bins_crossed(const Particle* p, std::vector& bins) const +void RegularMesh::surface_bins_crossed(const Particle* p, + std::vector& bins) const { // ======================================================================== // Determine if the track intersects the tally mesh. @@ -555,12 +589,15 @@ void RegularMesh::surface_bins_crossed(const Particle* p, std::vector& bins get_indices(r1, ijk1, &end_in_mesh); // Check if the track intersects any part of the mesh. - if (!start_in_mesh && !end_in_mesh) { - if (!intersects(r0, r1)) return; + if (!start_in_mesh) { + Position r0_copy = r0; + int ijk0_copy[n]; + for (int i = 0; i < n; ++i) ijk0_copy[i] = ijk0[i]; + if (!intersects(r0_copy, r1, ijk0_copy)) return; } // ======================================================================== - // Figure out which mesh cell to tally. + // Find which mesh surfaces are crossed. // Calculate number of surface crossings int n_cross = 0; @@ -587,7 +624,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p, std::vector& bins double distance = INFTY; for (int i = 0; i < n; ++i) { if (u[i] == 0) { - d[i] = INFINITY; + d[i] = INFTY; } else { d[i] = (xyz_cross[i] - r0[i])/u[i]; } @@ -695,25 +732,21 @@ void RegularMesh::to_hdf5(hid_t group) const close_group(mesh_group); } -xt::xarray RegularMesh::count_sites(int64_t n, const Particle::Bank* bank, - int n_energy, const double* energies, bool* outside) const +xt::xarray +RegularMesh::count_sites(const std::vector& bank, + bool* outside) const { // Determine shape of array for counts std::size_t m = xt::prod(shape_)(); - std::vector shape; - if (n_energy > 0) { - shape = {m, static_cast(n_energy - 1)}; - } else { - shape = {m}; - } + std::vector shape = {m}; // Create array of zeros xt::xarray cnt {shape, 0.0}; bool outside_ = false; - for (int64_t i = 0; i < n; ++i) { + for (const auto& site : bank) { // determine scoring bin for entropy mesh - int mesh_bin = get_bin(bank[i].r); + int mesh_bin = get_bin(site.r); // if outside mesh, skip particle if (mesh_bin < 0) { @@ -721,19 +754,8 @@ xt::xarray RegularMesh::count_sites(int64_t n, const Particle::Bank* ban continue; } - if (n_energy > 0) { - double E = bank[i].E; - if (E >= energies[0] && E <= energies[n_energy - 1]) { - // determine energy bin - int e_bin = lower_bound_index(energies, energies + n_energy, E); - - // Add to appropriate bin - cnt(mesh_bin, e_bin) += bank[i].wgt; - } - } else { - // Add to appropriate bin - cnt(mesh_bin) += bank[i].wgt; - } + // Add to appropriate bin + cnt(mesh_bin) += site.wgt; } // Create copy of count data diff --git a/src/mgxs.cpp b/src/mgxs.cpp index d345cdb22..efe562fa2 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -329,8 +329,6 @@ Mgxs::Mgxs(const std::string& in_name, const std::vector& mat_kTs, // Force all of the following data to be the same; these will be verified // to be true later int in_scatter_format = micros[0]->scatter_format; - int in_num_groups = micros[0]->num_groups; - int in_num_delayed_groups = micros[0]->num_delayed_groups; bool in_is_isotropic = micros[0]->is_isotropic; std::vector in_polar = micros[0]->polar; std::vector in_azimuthal = micros[0]->azimuthal; diff --git a/src/nuclide.cpp b/src/nuclide.cpp index cd2461186..6094b7983 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -34,11 +34,6 @@ std::vector> nuclides; std::unordered_map nuclide_map; } // namespace data -namespace simulation { -NuclideMicroXS* micro_xs; -MaterialMacroXS material_xs; -} // namespace simulation - //============================================================================== // Nuclide implementation //============================================================================== @@ -102,7 +97,7 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature, int i_nucl // Determine closest temperature double min_delta_T = INFTY; - double T_actual; + double T_actual = 0.0; for (auto T : temps_available) { double delta_T = std::abs(T - T_desired); if (delta_T < min_delta_T) { @@ -459,12 +454,13 @@ double Nuclide::nu(double E, EmissionMode mode, int group) const return (*fission_rx_[0]->products_[0].yield_)(E); } } + UNREACHABLE(); } -void Nuclide::calculate_elastic_xs() const +void Nuclide::calculate_elastic_xs(Particle& p) const { // Get temperature index, grid index, and interpolation factor - auto& micro = simulation::micro_xs[i_nuclide_]; + auto& micro {p.neutron_xs_[i_nuclide_]}; int i_temp = micro.index_temp; int i_grid = micro.index_grid; double f = micro.interp_factor; @@ -498,42 +494,41 @@ double Nuclide::elastic_xs_0K(double E) const return (1.0 - f)*elastic_0K_[i_grid] + f*elastic_0K_[i_grid + 1]; } -void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, - double sqrtkT, double sab_frac) +void Nuclide::calculate_xs(int i_sab, int i_log_union, double sab_frac, Particle& p) { - auto& micro_xs = simulation::micro_xs[i_nuclide_]; + auto& micro {p.neutron_xs_[i_nuclide_]}; // Initialize cached cross sections to zero - micro_xs.elastic = CACHE_INVALID; - micro_xs.thermal = 0.0; - micro_xs.thermal_elastic = 0.0; + micro.elastic = CACHE_INVALID; + micro.thermal = 0.0; + micro.thermal_elastic = 0.0; // Check to see if there is multipole data present at this energy bool use_mp = false; if (multipole_) { - use_mp = (E >= multipole_->E_min_ && E <= multipole_->E_max_); + use_mp = (p.E_ >= multipole_->E_min_ && p.E_ <= multipole_->E_max_); } // Evaluate multipole or interpolate if (use_mp) { // Call multipole kernel double sig_s, sig_a, sig_f; - std::tie(sig_s, sig_a, sig_f) = multipole_->evaluate(E, sqrtkT); + std::tie(sig_s, sig_a, sig_f) = multipole_->evaluate(p.E_, p.sqrtkT_); - micro_xs.total = sig_s + sig_a; - micro_xs.elastic = sig_s; - micro_xs.absorption = sig_a; - micro_xs.fission = sig_f; - micro_xs.nu_fission = fissionable_ ? - sig_f * this->nu(E, EmissionMode::total) : 0.0; + micro.total = sig_s + sig_a; + micro.elastic = sig_s; + micro.absorption = sig_a; + micro.fission = sig_f; + micro.nu_fission = fissionable_ ? + sig_f * this->nu(p.E_, EmissionMode::total) : 0.0; if (simulation::need_depletion_rx) { // Only non-zero reaction is (n,gamma) - micro_xs.reaction[0] = sig_a - sig_f; + micro.reaction[0] = sig_a - sig_f; // Set all other reaction cross sections to zero for (int i = 1; i < DEPLETION_RX.size(); ++i) { - micro_xs.reaction[i] = 0.0; + micro.reaction[i] = 0.0; } } @@ -547,13 +542,13 @@ void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, // resonance range, so the value here does not matter. index_temp is // set to -1 to force a segfault in case a developer messes up and tries // to use it with multipole. - micro_xs.index_temp = -1; - micro_xs.index_grid = -1; - micro_xs.interp_factor = 0.0; + micro.index_temp = -1; + micro.index_grid = -1; + micro.interp_factor = 0.0; } else { // Find the appropriate temperature index. - double kT = sqrtkT*sqrtkT; + double kT = p.sqrtkT_*p.sqrtkT_; double f; int i_temp = -1; switch (settings::temperature_method) { @@ -590,9 +585,9 @@ void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, const auto& xs {xs_[i_temp]}; int i_grid; - if (E < grid.energy.front()) { + if (p.E_ < grid.energy.front()) { i_grid = 0; - } else if (E > grid.energy.back()) { + } else if (p.E_ > grid.energy.back()) { i_grid = grid.energy.size() - 2; } else { // Determine bounding indices based on which equal log-spaced @@ -601,49 +596,49 @@ void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, int i_high = grid.grid_index[i_log_union + 1] + 1; // Perform binary search over reduced range - i_grid = i_low + lower_bound_index(&grid.energy[i_low], &grid.energy[i_high], E); + i_grid = i_low + lower_bound_index(&grid.energy[i_low], &grid.energy[i_high], p.E_); } // check for rare case where two energy points are the same if (grid.energy[i_grid] == grid.energy[i_grid + 1]) ++i_grid; // calculate interpolation factor - f = (E - grid.energy[i_grid]) / + f = (p.E_ - grid.energy[i_grid]) / (grid.energy[i_grid + 1]- grid.energy[i_grid]); - micro_xs.index_temp = i_temp; - micro_xs.index_grid = i_grid; - micro_xs.interp_factor = f; + micro.index_temp = i_temp; + micro.index_grid = i_grid; + micro.interp_factor = f; // Calculate microscopic nuclide total cross section - micro_xs.total = (1.0 - f)*xs(i_grid, XS_TOTAL) + micro.total = (1.0 - f)*xs(i_grid, XS_TOTAL) + f*xs(i_grid + 1, XS_TOTAL); // Calculate microscopic nuclide absorption cross section - micro_xs.absorption = (1.0 - f)*xs(i_grid, XS_ABSORPTION) + micro.absorption = (1.0 - f)*xs(i_grid, XS_ABSORPTION) + f*xs(i_grid + 1, XS_ABSORPTION); if (fissionable_) { // Calculate microscopic nuclide total cross section - micro_xs.fission = (1.0 - f)*xs(i_grid, XS_FISSION) + micro.fission = (1.0 - f)*xs(i_grid, XS_FISSION) + f*xs(i_grid + 1, XS_FISSION); // Calculate microscopic nuclide nu-fission cross section - micro_xs.nu_fission = (1.0 - f)*xs(i_grid, XS_NU_FISSION) + micro.nu_fission = (1.0 - f)*xs(i_grid, XS_NU_FISSION) + f*xs(i_grid + 1, XS_NU_FISSION); } else { - micro_xs.fission = 0.0; - micro_xs.nu_fission = 0.0; + micro.fission = 0.0; + micro.nu_fission = 0.0; } // Calculate microscopic nuclide photon production cross section - micro_xs.photon_prod = (1.0 - f)*xs(i_grid, XS_PHOTON_PROD) + micro.photon_prod = (1.0 - f)*xs(i_grid, XS_PHOTON_PROD) + f*xs(i_grid + 1, XS_PHOTON_PROD); // Depletion-related reactions if (simulation::need_depletion_rx) { // Initialize all reaction cross sections to zero - for (double& xs_i : micro_xs.reaction) { + for (double& xs_i : micro.reaction) { xs_i = 0.0; } @@ -658,14 +653,14 @@ void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, // Physics says that (n,gamma) is not a threshold reaction, so we don't // need to specifically check its threshold index if (j == 0) { - micro_xs.reaction[0] = (1.0 - f)*rx_xs[i_grid] + micro.reaction[0] = (1.0 - f)*rx_xs[i_grid] + f*rx_xs[i_grid + 1]; continue; } int threshold = rx->xs_[i_temp].threshold; if (i_grid >= threshold) { - micro_xs.reaction[j] = (1.0 - f)*rx_xs[i_grid - threshold] + + micro.reaction[j] = (1.0 - f)*rx_xs[i_grid - threshold] + f*rx_xs[i_grid - threshold + 1]; } else if (j >= 3) { // One can show that the the threshold for (n,(x+1)n) is always @@ -680,35 +675,35 @@ void Nuclide::calculate_xs(int i_sab, double E, int i_log_union, } // Initialize sab treatment to false - micro_xs.index_sab = C_NONE; - micro_xs.sab_frac = 0.0; + micro.index_sab = C_NONE; + micro.sab_frac = 0.0; // Initialize URR probability table treatment to false - micro_xs.use_ptable = false; + micro.use_ptable = false; // If there is S(a,b) data for this nuclide, we need to set the sab_scatter // and sab_elastic cross sections and correct the total and elastic cross // sections. - if (i_sab >= 0) this->calculate_sab_xs(i_sab, E, sqrtkT, sab_frac); + if (i_sab >= 0) this->calculate_sab_xs(i_sab, sab_frac, p); // If the particle is in the unresolved resonance range and there are // probability tables, we need to determine cross sections from the table if (settings::urr_ptables_on && urr_present_ && !use_mp) { - int n = urr_data_[micro_xs.index_temp].n_energy_; - if ((E > urr_data_[micro_xs.index_temp].energy_(0)) && - (E < urr_data_[micro_xs.index_temp].energy_(n-1))) { - this->calculate_urr_xs(micro_xs.index_temp, E); + int n = urr_data_[micro.index_temp].n_energy_; + if ((p.E_ > urr_data_[micro.index_temp].energy_(0)) && + (p.E_ < urr_data_[micro.index_temp].energy_(n-1))) { + this->calculate_urr_xs(micro.index_temp, p); } } - micro_xs.last_E = E; - micro_xs.last_sqrtkT = sqrtkT; + micro.last_E = p.E_; + micro.last_sqrtkT = p.sqrtkT_; } -void Nuclide::calculate_sab_xs(int i_sab, double E, double sqrtkT, double sab_frac) +void Nuclide::calculate_sab_xs(int i_sab, double sab_frac, Particle& p) { - auto& micro {simulation::micro_xs[i_nuclide_]}; + auto& micro {p.neutron_xs_[i_nuclide_]}; // Set flag that S(a,b) treatment should be used for scattering micro.index_sab = i_sab; @@ -717,14 +712,14 @@ void Nuclide::calculate_sab_xs(int i_sab, double E, double sqrtkT, double sab_fr int i_temp; double elastic; double inelastic; - data::thermal_scatt[i_sab]->calculate_xs(E, sqrtkT, &i_temp, &elastic, &inelastic); + data::thermal_scatt[i_sab]->calculate_xs(p.E_, p.sqrtkT_, &i_temp, &elastic, &inelastic); // Store the S(a,b) cross sections. micro.thermal = sab_frac * (elastic + inelastic); micro.thermal_elastic = sab_frac * elastic; // Calculate free atom elastic cross section - this->calculate_elastic_xs(); + this->calculate_elastic_xs(p); // Correct total and elastic cross sections micro.total = micro.total + micro.thermal - sab_frac*micro.elastic; @@ -735,9 +730,9 @@ void Nuclide::calculate_sab_xs(int i_sab, double E, double sqrtkT, double sab_fr micro.sab_frac = sab_frac; } -void Nuclide::calculate_urr_xs(int i_temp, double E) const +void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const { - auto& micro = simulation::micro_xs[i_nuclide_]; + auto& micro = p.neutron_xs_[i_nuclide_]; micro.use_ptable = true; // Create a shorthand for the URR data @@ -745,7 +740,7 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const // Determine the energy table int i_energy = 0; - while (E >= urr.energy_(i_energy + 1)) {++i_energy;}; + while (p.E_ >= urr.energy_(i_energy + 1)) {++i_energy;}; // Sample the probability table using the cumulative distribution @@ -773,7 +768,7 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const double f; if (urr.interp_ == Interpolation::lin_lin) { // Determine the interpolation factor on the table - f = (E - urr.energy_(i_energy)) / + f = (p.E_ - urr.energy_(i_energy)) / (urr.energy_(i_energy + 1) - urr.energy_(i_energy)); elastic = (1. - f) * urr.prob_(i_energy, URR_ELASTIC, i_low) + @@ -784,7 +779,7 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const f * urr.prob_(i_energy + 1, URR_N_GAMMA, i_up); } else if (urr.interp_ == Interpolation::log_log) { // Determine interpolation factor on the table - f = std::log(E / urr.energy_(i_energy)) / + f = std::log(p.E_ / urr.energy_(i_energy)) / std::log(urr.energy_(i_energy + 1) / urr.energy_(i_energy)); // Calculate the elastic cross section/factor @@ -838,7 +833,7 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const // Multiply by smooth cross-section if needed if (urr.multiply_smooth_) { - calculate_elastic_xs(); + calculate_elastic_xs(p); elastic *= micro.elastic; capture *= (micro.absorption - micro.fission); fission *= micro.fission; @@ -858,7 +853,7 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const // Determine nu-fission cross-section if (fissionable_) { - micro.nu_fission = nu(E, EmissionMode::total) * micro.fission; + micro.nu_fission = nu(p.E_, EmissionMode::total) * micro.fission; } } diff --git a/src/output.cpp b/src/output.cpp index 92ad14e9d..ee74a27e1 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -11,7 +11,9 @@ #include #include // for pair +#ifdef _OPENMP #include +#endif #include "xtensor/xview.hpp" #include "openmc/capi.h" @@ -189,7 +191,7 @@ extern "C" void print_particle(Particle* p) } // Display miscellaneous info. - if (p->surface_ != ERROR_INT) { + if (p->surface_ != 0) { const Surface& surf {*model::surfaces[std::abs(p->surface_)-1]}; std::cout << " Surface = " << std::copysign(surf.id_, p->surface_) << "\n"; } @@ -281,9 +283,9 @@ print_overlap_check() { #ifdef OPENMC_MPI std::vector temp(model::overlap_check_count); - int err = MPI_Reduce(temp.data(), model::overlap_check_count.data(), - model::overlap_check_count.size(), MPI_INT64_T, - MPI_SUM, 0, mpi::intracomm); + MPI_Reduce(temp.data(), model::overlap_check_count.data(), + model::overlap_check_count.size(), MPI_INT64_T, + MPI_SUM, 0, mpi::intracomm); #endif if (mpi::master) { @@ -480,7 +482,7 @@ void print_runtime() // Calculate particle rate in active/inactive batches int n_active = simulation::current_batch - settings::n_inactive; - double speed_inactive; + double speed_inactive = 0.0; double speed_active; if (settings::restart_run) { if (simulation::restart_batch < settings::n_inactive) { @@ -490,7 +492,6 @@ void print_runtime() speed_active = (settings::n_particles * n_active * settings::gen_per_batch) / time_active.elapsed(); } else { - speed_inactive = 0.0; speed_active = (settings::n_particles * (settings::n_batches - simulation::restart_batch) * settings::gen_per_batch) / time_active.elapsed(); diff --git a/src/particle.cpp b/src/particle.cpp index ca7fccfe7..4e0066b20 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -15,6 +15,7 @@ #include "openmc/message_passing.h" #include "openmc/mgxs_interface.h" #include "openmc/nuclide.h" +#include "openmc/photon.h" #include "openmc/physics.h" #include "openmc/physics_mg.h" #include "openmc/random_lcg.h" @@ -49,12 +50,18 @@ LocalCoord::reset() Particle::Particle() { - // Clear coordinate lists + // Create and clear coordinate levels + coord_.resize(model::n_coord_levels); + cell_last_.resize(model::n_coord_levels); clear(); for (int& n : n_delayed_bank_) { n = 0; } + + // Create microscopic cross section caches + neutron_xs_.resize(data::nuclides.size()); + photon_xs_.resize(data::elements.size()); } void @@ -66,19 +73,16 @@ Particle::clear() } void -Particle::create_secondary(Direction u, double E, Type type) +Particle::create_secondary(Direction u, double E, Type type) const { - if (n_secondary_ == MAX_SECONDARY) { - fatal_error("Too many secondary particles created."); - } + simulation::secondary_bank.emplace_back(); - int64_t n = n_secondary_; - secondary_bank_[n].particle = type; - secondary_bank_[n].wgt = wgt_; - secondary_bank_[n].r = this->r(); - secondary_bank_[n].u = u; - secondary_bank_[n].E = settings::run_CE ? E : g_; - ++n_secondary_; + auto& bank {simulation::secondary_bank.back()}; + bank.particle = type; + bank.wgt = wgt_; + bank.r = this->r(); + bank.u = u; + bank.E = settings::run_CE ? E : g_; } void @@ -130,9 +134,7 @@ Particle::transport() // Force calculation of cross-sections by setting last energy to zero if (settings::run_CE) { - for (int i = 0; i < data::nuclides.size(); ++i) { - simulation::micro_xs[i].last_E = 0.0; - } + for (auto& micro : neutron_xs_) micro.last_E = 0.0; } // Prepare to write out particle track. @@ -186,41 +188,35 @@ Particle::transport() } else { // Get the MG data calculate_xs_c(material_, g_, sqrtkT_, this->u_local(), - simulation::material_xs.total, simulation::material_xs.absorption, - simulation::material_xs.nu_fission); + macro_xs_.total, macro_xs_.absorption, macro_xs_.nu_fission); // Finally, update the particle group while we have already checked // for if multi-group g_last_ = g_; } } else { - simulation::material_xs.total = 0.0; - simulation::material_xs.absorption = 0.0; - simulation::material_xs.fission = 0.0; - simulation::material_xs.nu_fission = 0.0; + macro_xs_.total = 0.0; + macro_xs_.absorption = 0.0; + macro_xs_.fission = 0.0; + macro_xs_.nu_fission = 0.0; } // Find the distance to the nearest boundary - double d_boundary; - int surface_crossed; - int lattice_translation[3]; - int next_level; - distance_to_boundary(this, &d_boundary, &surface_crossed, - lattice_translation, &next_level); + auto boundary = distance_to_boundary(this); // Sample a distance to collision double d_collision; if (type_ == Particle::Type::electron || type_ == Particle::Type::positron) { d_collision = 0.0; - } else if (simulation::material_xs.total == 0.0) { + } else if (macro_xs_.total == 0.0) { d_collision = INFINITY; } else { - d_collision = -std::log(prn()) / simulation::material_xs.total; + d_collision = -std::log(prn()) / macro_xs_.total; } // Select smaller of the two distances - double distance = std::min(d_boundary, d_collision); + double distance = std::min(boundary.distance, d_collision); // Advance particle for (int j = 0; j < n_coord_; ++j) { @@ -235,7 +231,7 @@ Particle::transport() // Score track-length estimate of k-eff if (settings::run_mode == RUN_MODE_EIGENVALUE && type_ == Particle::Type::neutron) { - global_tally_tracklength += wgt_ * distance * simulation::material_xs.nu_fission; + global_tally_tracklength += wgt_ * distance * macro_xs_.nu_fission; } // Score flux derivative accumulators for differential tallies. @@ -243,11 +239,13 @@ Particle::transport() score_track_derivative(this, distance); } - if (d_collision > d_boundary) { + if (d_collision > boundary.distance) { // ==================================================================== // PARTICLE CROSSES SURFACE - if (next_level > 0) n_coord_ = next_level; + // Set surface that particle is on and adjust coordinate levels + surface_ = boundary.surface_index; + n_coord_ = boundary.coord_level; // Saving previous cell data for (int j = 0; j < n_coord_; ++j) { @@ -255,15 +253,14 @@ Particle::transport() } n_coord_last_ = n_coord_; - if (lattice_translation[0] != 0 || lattice_translation[1] != 0 || - lattice_translation[2] != 0) { + if (boundary.lattice_translation[0] != 0 || + boundary.lattice_translation[1] != 0 || + boundary.lattice_translation[2] != 0) { // Particle crosses lattice boundary - surface_ = ERROR_INT; - cross_lattice(this, lattice_translation); + cross_lattice(this, boundary); event_ = EVENT_LATTICE; } else { // Particle crosses surface - surface_ = surface_crossed; this->cross_surface(); event_ = EVENT_SURFACE; } @@ -278,8 +275,8 @@ Particle::transport() // Score collision estimate of keff if (settings::run_mode == RUN_MODE_EIGENVALUE && type_ == Particle::Type::neutron) { - global_tally_collision += wgt_ * simulation::material_xs.nu_fission - / simulation::material_xs.total; + global_tally_collision += wgt_ * macro_xs_.nu_fission + / macro_xs_.total; } // Score surface current tallies -- this has to be done before the collision @@ -290,7 +287,7 @@ Particle::transport() score_surface_tally(this, model::active_meshsurf_tallies); // Clear surface component - surface_ = ERROR_INT; + surface_ = 0; if (settings::run_CE) { collision(this); @@ -356,10 +353,10 @@ Particle::transport() // Check for secondary particles if this particle is dead if (!alive_) { // If no secondary particles, break out of event loop - if (n_secondary_ == 0) break; + if (simulation::secondary_bank.empty()) break; - this->from_source(&secondary_bank_[n_secondary_ - 1]); - --n_secondary_; + this->from_source(&simulation::secondary_bank.back()); + simulation::secondary_bank.pop_back(); n_event = 0; // Enter new particle in particle track file @@ -555,7 +552,7 @@ Particle::cross_surface() // COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS // Remove lower coordinate levels and assignment of surface - surface_ = ERROR_INT; + surface_ = 0; n_coord_ = 1; bool found = find_cell(this, false); @@ -589,11 +586,12 @@ Particle::mark_as_lost(const char* message) // Increment number of lost particles alive_ = false; -#pragma omp atomic + #pragma omp atomic simulation::n_lost_particles += 1; // Count the total number of simulated particles (on this processor) - auto n = simulation::current_batch * settings::gen_per_batch * simulation::work; + auto n = simulation::current_batch * settings::gen_per_batch * + simulation::work_per_rank; // Abort the simulation if the maximum number of lost particles has been // reached @@ -614,7 +612,7 @@ Particle::write_restart() const filename << settings::path_output << "particle_" << simulation::current_batch << '_' << id_ << ".h5"; -#pragma omp critical (WriteParticleRestart) + #pragma omp critical (WriteParticleRestart) { // Create file hid_t file_id = file_open(filename.str(), 'w'); diff --git a/src/particle_restart.cpp b/src/particle_restart.cpp index 0cface319..b2017bbeb 100644 --- a/src/particle_restart.cpp +++ b/src/particle_restart.cpp @@ -14,6 +14,7 @@ #include // for copy #include +#include #include namespace openmc { @@ -71,13 +72,6 @@ void run_particle_restart() // Set verbosity high settings::verbosity = 10; - // Create cross section caches - #pragma omp parallel - { - simulation::micro_xs = new NuclideMicroXS[data::nuclides.size()]; - simulation::micro_photon_xs = new ElementMicroXS[data::elements.size()]; - } - // Initialize the particle to be tracked Particle p; @@ -97,6 +91,9 @@ void run_particle_restart() case RUN_MODE_FIXEDSOURCE: particle_seed = p.id_; break; + default: + throw std::runtime_error{"Unexpected run mode: " + + std::to_string(previous_run_mode)}; } set_particle_seed(particle_seed); @@ -105,13 +102,6 @@ void run_particle_restart() // Write output if particle made it print_particle(&p); - - // Clear cross section caches - #pragma omp parallel - { - delete[] simulation::micro_xs; - delete[] simulation::micro_photon_xs; - } } } // namespace openmc diff --git a/src/photon.cpp b/src/photon.cpp index 2dd0928ba..8a0ddf9aa 100644 --- a/src/photon.cpp +++ b/src/photon.cpp @@ -32,10 +32,6 @@ std::unordered_map element_map; } // namespace data -namespace simulation { -ElementMicroXS* micro_photon_xs; -} // namespace simulation - //============================================================================== // PhotonInteraction implementation //============================================================================== @@ -212,16 +208,12 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element) if (data::ttb_k_grid.size() == 1) { read_dataset(rgroup, "photon_energy", data::ttb_k_grid); } - close_group(rgroup); - // Read stopping power data - if (Z_ < 99) { - rgroup = open_group(group, "stopping_powers"); - read_dataset(rgroup, "s_collision", stopping_power_collision_); - read_dataset(rgroup, "s_radiative", stopping_power_radiative_); - read_attribute(rgroup, "I", I_); - close_group(rgroup); - } + // Get data used for density effect correction + read_dataset(rgroup, "num_electrons", n_electrons_); + read_dataset(rgroup, "ionization_energy", ionization_energy_); + read_attribute(rgroup, "I", I_); + close_group(rgroup); // Truncate the bremsstrahlung data at the cutoff energy int photon = static_cast(Particle::Type::photon); @@ -235,26 +227,10 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element) double f = (std::log(cutoff) - std::log(E(i_grid))) / (std::log(E(i_grid+1)) - std::log(E(i_grid))); - // Interpolate collision stopping power at the cutoff energy and truncate - auto& s_col {stopping_power_collision_}; - double y = std::exp(std::log(s_col(i_grid)) + f*(std::log(s_col(i_grid+1)) - - std::log(s_col(i_grid)))); - xt::xtensor frst {y}; - stopping_power_collision_ = xt::concatenate(xt::xtuple( - frst, xt::view(s_col, xt::range(i_grid+1, n_e)))); - - // Interpolate radiative stopping power at the cutoff energy and truncate - auto& s_rad {stopping_power_radiative_}; - y = std::exp(std::log(s_rad(i_grid)) + f*(std::log(s_rad(i_grid+1)) - - std::log(s_rad(i_grid)))); - frst(0) = y; - stopping_power_radiative_ = xt::concatenate(xt::xtuple( - frst, xt::view(s_rad, xt::range(i_grid+1, n_e)))); - // Interpolate bremsstrahlung DCS at the cutoff energy and truncate xt::xtensor dcs({n_e - i_grid, n_k}); for (int i = 0; i < n_k; ++i) { - y = std::exp(std::log(dcs_(i_grid,i)) + + double y = std::exp(std::log(dcs_(i_grid,i)) + f*(std::log(dcs_(i_grid+1,i)) - std::log(dcs_(i_grid,i)))); auto col_i = xt::view(dcs, xt::all(), i); col_i(0) = y; @@ -264,7 +240,7 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element) } dcs_ = dcs; - frst(0) = cutoff; + xt::xtensor frst {cutoff}; electron_energy = xt::concatenate(xt::xtuple( frst, xt::view(electron_energy, xt::range(i_grid+1, n_e)))); } @@ -274,6 +250,25 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element) if (data::ttb_e_grid.size() == 1) { data::ttb_e_grid = electron_energy; } + + // Calculate the radiative stopping power + stopping_power_radiative_ = xt::empty({data::ttb_e_grid.size()}); + for (int i = 0; i < data::ttb_e_grid.size(); ++i) { + // Integrate over reduced photon energy + double c = 0.0; + for (int j = 0; j < data::ttb_k_grid.size() - 1; ++j) { + c += 0.5 * (dcs_(i, j+1) + dcs_(i, j)) * (data::ttb_k_grid(j+1) - + data::ttb_k_grid(j)); + } + double e = data::ttb_e_grid(i); + + // Square of the ratio of the speed of light to the velocity of the + // charged particle + double beta_sq = e * (e + 2.0 * MASS_ELECTRON_EV) / ((e + + MASS_ELECTRON_EV) * (e + MASS_ELECTRON_EV)); + + stopping_power_radiative_(i) = Z_ * Z_ / beta_sq * e * c; + } } // Take logarithm of energies and cross sections since they are log-log @@ -434,12 +429,12 @@ void PhotonInteraction::compton_doppler(double alpha, double mu, *i_shell = shell; } -void PhotonInteraction::calculate_xs(double E) const +void PhotonInteraction::calculate_xs(Particle& p) const { // Perform binary search on the element energy grid in order to determine // which points to interpolate between int n_grid = energy_.size(); - double log_E = std::log(E); + double log_E = std::log(p.E_); int i_grid; if (log_E <= energy_[0]) { i_grid = 0; @@ -457,7 +452,7 @@ void PhotonInteraction::calculate_xs(double E) const // calculate interpolation factor double f = (log_E - energy_(i_grid)) / (energy_(i_grid+1) - energy_(i_grid)); - auto& xs {simulation::micro_photon_xs[i_element_]}; + auto& xs {p.photon_xs_[i_element_]}; xs.index_grid = i_grid; xs.interp_factor = f; @@ -491,7 +486,7 @@ void PhotonInteraction::calculate_xs(double E) const // Calculate microscopic total cross section xs.total = xs.coherent + xs.incoherent + xs.photoelectric + xs.pair_production; - xs.last_E = E; + xs.last_E = p.E_; } double PhotonInteraction::rayleigh_scatter(double alpha) const @@ -555,11 +550,6 @@ void PhotonInteraction::pair_production(double alpha, double* E_electron, // "PENELOPE-2011: A Code System for Monte Carlo Simulation of Electron and // Photon Transport," OECD-NEA, Issy-les-Moulineaux, France (2011). - // Compute the minimum and maximum values of the electron reduced energy, - // i.e. the fraction of the photon energy that is given to the electron - double e_min = 1.0/alpha; - double e_max = 1.0 - 1.0/alpha; - // Compute the high-energy Coulomb correction double a = Z_ / FINE_STRUCTURE; double c = a*a*(1.0/(1.0 + a*a) + 0.202059 + a*a*(-0.03693 + a*a*(0.00835 + diff --git a/src/physics.cpp b/src/physics.cpp index d898ed788..67af6dc42 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -56,7 +56,6 @@ void collision(Particle* p) if (p->E_ < settings::energy_cutoff[type]) { p->alive_ = false; p->wgt_ = 0.0; - p->wgt_last_ = 0.0; } // Display information about collision @@ -89,14 +88,20 @@ void sample_neutron_reaction(Particle* p) const auto& nuc {data::nuclides[i_nuclide]}; if (nuc->fissionable_) { - Reaction* rx = sample_fission(i_nuclide, p->E_); + Reaction* rx = sample_fission(i_nuclide, p); if (settings::run_mode == RUN_MODE_EIGENVALUE) { - create_fission_sites(p, i_nuclide, rx, simulation::fission_bank.data(), - &simulation::n_bank, simulation::fission_bank.size()); + create_fission_sites(p, i_nuclide, rx, simulation::fission_bank); } else if (settings::run_mode == RUN_MODE_FIXEDSOURCE && settings::create_fission_neutrons) { - create_fission_sites(p, i_nuclide, rx, p->secondary_bank_, - &p->n_secondary_, MAX_SECONDARY); + create_fission_sites(p, i_nuclide, rx, simulation::secondary_bank); + + // Make sure particle population doesn't grow out of control for + // subcritical multiplication problems. + if (simulation::secondary_bank.size() >= 10000) { + fatal_error("The secondary particle bank appears to be growing without " + "bound. You are likely running a subcritical multiplication problem " + "with k-effective close to or greater than one."); + } } } @@ -110,7 +115,7 @@ void sample_neutron_reaction(Particle* p) // If survival biasing is being used, the following subroutine adjusts the // weight of the particle. Otherwise, it checks to see if absorption occurs - if (simulation::micro_xs[i_nuclide].absorption > 0.0) { + if (p->neutron_xs_[i_nuclide].absorption > 0.0) { absorption(p, i_nuclide); } else { p->wgt_absorb_ = 0.0; @@ -137,63 +142,44 @@ void sample_neutron_reaction(Particle* p) void create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx, - Particle::Bank* bank_array, int64_t* size_bank, int64_t bank_capacity) + std::vector& bank) { - // TODO: Heat generation from fission - // If uniform fission source weighting is turned on, we increase or decrease // the expected number of fission sites produced double weight = settings::ufs_on ? ufs_get_weight(p) : 1.0; // Determine the expected number of neutrons produced - double nu_t = p->wgt_ / simulation::keff * weight * simulation::micro_xs[ - i_nuclide].nu_fission / simulation::micro_xs[i_nuclide].total; + double nu_t = p->wgt_ / simulation::keff * weight * p->neutron_xs_[ + i_nuclide].nu_fission / p->neutron_xs_[i_nuclide].total; // Sample the number of neutrons produced int nu = static_cast(nu_t); if (prn() <= (nu_t - nu)) ++nu; - // Check for the bank size getting hit. For fixed source calculations, this - // is a fatal error; for eigenvalue calculations, it just means that k-eff - // was too high for a single batch. - if (*size_bank + nu > bank_capacity) { - if (settings::run_mode == RUN_MODE_FIXEDSOURCE) { - throw std::runtime_error{"Secondary particle bank size limit reached." - " If you are running a subcritical multiplication problem," - " k-effective may be too close to one."}; - } else { - if (mpi::master) { - warning("Maximum number of sites in fission bank reached. This can" - " result in irreproducible results using different numbers of" - " processes/threads."); - } - } - } - // Begin banking the source neutrons // First, if our bank is full then don't continue - if (nu == 0 || *size_bank == bank_capacity) return; + if (nu == 0) return; // Initialize the counter of delayed neutrons encountered for each delayed // group. double nu_d[MAX_DELAYED_GROUPS] = {0.}; p->fission_ = true; - for (size_t i = *size_bank; i < std::min(*size_bank + nu, bank_capacity); ++i) { + for (int i = 0; i < nu; ++i) { + // Create new bank site and get reference to last element + bank.emplace_back(); + auto& site {bank.back()}; + // Bank source neutrons by copying the particle data - bank_array[i].r = p->r(); - - // Set that the bank particle is a neutron - bank_array[i].particle = Particle::Type::neutron; - - // Set the weight of the fission bank site - bank_array[i].wgt = 1. / weight; + site.r = p->r(); + site.particle = Particle::Type::neutron; + site.wgt = 1. / weight; // Sample delayed group and angle/energy for fission reaction - sample_fission_neutron(i_nuclide, rx, p->E_, &bank_array[i]); + sample_fission_neutron(i_nuclide, rx, p->E_, &site); // Set the delayed group on the particle as well - p->delayed_group_ = bank_array[i].delayed_group; + p->delayed_group_ = site.delayed_group; // Increment the number of neutrons born delayed if (p->delayed_group_ > 0) { @@ -201,9 +187,6 @@ create_fission_sites(Particle* p, int i_nuclide, const Reaction* rx, } } - // Increment number of bank sites - *size_bank = std::min(*size_bank + nu, bank_capacity); - // Store the total weight banked for analog fission tallies p->n_bank_ = nu; p->wgt_bank_ = nu / weight; @@ -227,7 +210,7 @@ void sample_photon_reaction(Particle* p) // Sample element within material int i_element = sample_element(p); p->event_nuclide_ = i_element; - const auto& micro {simulation::micro_photon_xs[i_element]}; + const auto& micro {p->photon_xs_[i_element]}; const auto& element {data::elements[i_element]}; // Calculate photon energy over electron rest mass equivalent @@ -264,12 +247,15 @@ void sample_photon_reaction(Particle* p) } // Create Compton electron - double E_electron = (alpha - alpha_out)*MASS_ELECTRON_EV - e_b; - double mu_electron = (alpha - alpha_out*mu) - / std::sqrt(alpha*alpha + alpha_out*alpha_out - 2.0*alpha*alpha_out*mu); double phi = 2.0*PI*prn(); - Direction u = rotate_angle(p->u(), mu_electron, &phi); - p->create_secondary(u, E_electron, Particle::Type::electron); + double E_electron = (alpha - alpha_out)*MASS_ELECTRON_EV - e_b; + int electron = static_cast(Particle::Type::electron); + if (E_electron >= settings::energy_cutoff[electron]) { + double mu_electron = (alpha - alpha_out*mu) + / std::sqrt(alpha*alpha + alpha_out*alpha_out - 2.0*alpha*alpha_out*mu); + Direction u = rotate_angle(p->u(), mu_electron, &phi); + p->create_secondary(u, E_electron, Particle::Type::electron); + } // TODO: Compton subshell data does not match atomic relaxation data // Allow electrons to fill orbital and produce auger electrons @@ -405,7 +391,7 @@ void sample_positron_reaction(Particle* p) int sample_nuclide(const Particle* p) { // Sample cumulative distribution function - double cutoff = prn() * simulation::material_xs.total; + double cutoff = prn() * p->macro_xs_.total; // Get pointers to nuclide/density arrays const auto& mat {model::materials[p->material_]}; @@ -418,7 +404,7 @@ int sample_nuclide(const Particle* p) double atom_density = mat->atom_density_[i]; // Increment probability to compare to cutoff - prob += atom_density * simulation::micro_xs[i_nuclide].total; + prob += atom_density * p->neutron_xs_[i_nuclide].total; if (prob >= cutoff) return i_nuclide; } @@ -430,38 +416,31 @@ int sample_nuclide(const Particle* p) int sample_element(Particle* p) { // Sample cumulative distribution function - double cutoff = prn() * simulation::material_xs.total; + double cutoff = prn() * p->macro_xs_.total; // Get pointers to elements, densities const auto& mat {model::materials[p->material_]}; - int n = mat->nuclide_.size(); - int i = 0; double prob = 0.0; - int i_element; - while (prob < cutoff) { - // Check to make sure that a nuclide was sampled - if (i >= n) { - p->write_restart(); - fatal_error("Did not sample any element during collision."); - } - + for (int i = 0; i < mat->element_.size(); ++i) { // Find atom density - i_element = mat->element_[i]; + int i_element = mat->element_[i]; double atom_density = mat->atom_density_[i]; // Determine microscopic cross section - double sigma = atom_density * simulation::micro_photon_xs[i_element].total; + double sigma = atom_density * p->photon_xs_[i_element].total; // Increment probability to compare to cutoff prob += sigma; - ++i; + if (prob > cutoff) return i_element; } - return i_element; + // If we made it here, no element was sampled + p->write_restart(); + fatal_error("Did not sample any element during collision."); } -Reaction* sample_fission(int i_nuclide, double E) +Reaction* sample_fission(int i_nuclide, const Particle* p) { // Get pointer to nuclide const auto& nuc {data::nuclides[i_nuclide]}; @@ -469,23 +448,23 @@ Reaction* sample_fission(int i_nuclide, double E) // If we're in the URR, by default use the first fission reaction. We also // default to the first reaction if we know that there are no partial fission // reactions - if (simulation::micro_xs[i_nuclide].use_ptable || !nuc->has_partial_fission_) { + if (p->neutron_xs_[i_nuclide].use_ptable || !nuc->has_partial_fission_) { return nuc->fission_rx_[0]; } // Check to see if we are in a windowed multipole range. WMP only supports // the first fission reaction. if (nuc->multipole_) { - if (E >= nuc->multipole_->E_min_ && E <= nuc->multipole_->E_max_) { + if (p->E_ >= nuc->multipole_->E_min_ && p->E_ <= nuc->multipole_->E_max_) { return nuc->fission_rx_[0]; } } // Get grid index and interpolatoin factor and sample fission cdf - int i_temp = simulation::micro_xs[i_nuclide].index_temp; - int i_grid = simulation::micro_xs[i_nuclide].index_grid; - double f = simulation::micro_xs[i_nuclide].interp_factor; - double cutoff = prn() * simulation::micro_xs[i_nuclide].fission; + int i_temp = p->neutron_xs_[i_nuclide].index_temp; + int i_grid = p->neutron_xs_[i_nuclide].index_grid; + double f = p->neutron_xs_[i_nuclide].interp_factor; + double cutoff = prn() * p->neutron_xs_[i_nuclide].fission; double prob = 0.0; // Loop through each partial fission reaction type @@ -501,15 +480,18 @@ Reaction* sample_fission(int i_nuclide, double E) // Create fission bank sites if fission occurs if (prob > cutoff) return rx; } + + // If we reached here, no reaction was sampled + throw std::runtime_error{"No fission reaction was sampled for " + nuc->name_}; } -void sample_photon_product(int i_nuclide, double E, int* i_rx, int* i_product) +void sample_photon_product(int i_nuclide, const Particle* p, int* i_rx, int* i_product) { // Get grid index and interpolation factor and sample photon production cdf - int i_temp = simulation::micro_xs[i_nuclide].index_temp; - int i_grid = simulation::micro_xs[i_nuclide].index_grid; - double f = simulation::micro_xs[i_nuclide].interp_factor; - double cutoff = prn() * simulation::micro_xs[i_nuclide].photon_prod; + int i_temp = p->neutron_xs_[i_nuclide].index_temp; + int i_grid = p->neutron_xs_[i_nuclide].index_grid; + double f = p->neutron_xs_[i_nuclide].interp_factor; + double cutoff = prn() * p->neutron_xs_[i_nuclide].photon_prod; double prob = 0.0; // Loop through each reaction type @@ -528,7 +510,7 @@ void sample_photon_product(int i_nuclide, double E, int* i_rx, int* i_product) for (int j = 0; j < rx->products_.size(); ++j) { if (rx->products_[j].particle_ == Particle::Type::photon) { // add to cumulative probability - prob += (*rx->products_[j].yield_)(E) * xs; + prob += (*rx->products_[j].yield_)(p->E_) * xs; *i_rx = i; *i_product = j; @@ -542,8 +524,8 @@ void absorption(Particle* p, int i_nuclide) { if (settings::survival_biasing) { // Determine weight absorbed in survival biasing - p->wgt_absorb_ = p->wgt_ * simulation::micro_xs[i_nuclide].absorption / - simulation::micro_xs[i_nuclide].total; + p->wgt_absorb_ = p->wgt_ * p->neutron_xs_[i_nuclide].absorption / + p->neutron_xs_[i_nuclide].total; // Adjust weight of particle by probability of absorption p->wgt_ -= p->wgt_absorb_; @@ -551,17 +533,17 @@ void absorption(Particle* p, int i_nuclide) // Score implicit absorption estimate of keff if (settings::run_mode == RUN_MODE_EIGENVALUE) { - global_tally_absorption += p->wgt_absorb_ * simulation::micro_xs[ - i_nuclide].nu_fission / simulation::micro_xs[i_nuclide].absorption; + global_tally_absorption += p->wgt_absorb_ * p->neutron_xs_[ + i_nuclide].nu_fission / p->neutron_xs_[i_nuclide].absorption; } } else { // See if disappearance reaction happens - if (simulation::micro_xs[i_nuclide].absorption > - prn() * simulation::micro_xs[i_nuclide].total) { + if (p->neutron_xs_[i_nuclide].absorption > + prn() * p->neutron_xs_[i_nuclide].total) { // Score absorption estimate of keff if (settings::run_mode == RUN_MODE_EIGENVALUE) { - global_tally_absorption += p->wgt_ * simulation::micro_xs[ - i_nuclide].nu_fission / simulation::micro_xs[i_nuclide].absorption; + global_tally_absorption += p->wgt_ * p->neutron_xs_[ + i_nuclide].nu_fission / p->neutron_xs_[i_nuclide].absorption; } p->alive_ = false; @@ -578,7 +560,7 @@ void scatter(Particle* p, int i_nuclide) // Get pointer to nuclide and grid index/interpolation factor const auto& nuc {data::nuclides[i_nuclide]}; - const auto& micro {simulation::micro_xs[i_nuclide]}; + const auto& micro {p->neutron_xs_[i_nuclide]}; int i_temp = micro.index_temp; int i_grid = micro.index_grid; double f = micro.interp_factor; @@ -590,7 +572,7 @@ void scatter(Particle* p, int i_nuclide) // Calculate elastic cross section if it wasn't precalculated if (micro.elastic == CACHE_INVALID) { - nuc->calculate_elastic_xs(); + nuc->calculate_elastic_xs(*p); } double prob = micro.elastic - micro.thermal; @@ -602,8 +584,7 @@ void scatter(Particle* p, int i_nuclide) double kT = nuc->multipole_ ? p->sqrtkT_*p->sqrtkT_ : nuc->kTs_[i_temp]; // Perform collision physics for elastic scattering - elastic_scatter(i_nuclide, *nuc->reactions_[0], kT, - p->E_, p->u(), p->mu_); + elastic_scatter(i_nuclide, *nuc->reactions_[0], kT, p); p->event_mt_ = ELASTIC; sampled = true; @@ -614,7 +595,7 @@ void scatter(Particle* p, int i_nuclide) // ======================================================================= // S(A,B) SCATTERING - sab_scatter(i_nuclide, micro.index_sab, p->E_, p->u(), p->mu_); + sab_scatter(i_nuclide, micro.index_sab, p); p->event_mt_ = ELASTIC; sampled = true; @@ -625,7 +606,7 @@ void scatter(Particle* p, int i_nuclide) // INELASTIC SCATTERING int j = 0; - int i; + int i = 0; while (prob < cutoff) { i = nuc->index_inelastic_scatter_[j]; ++j; @@ -672,23 +653,23 @@ void scatter(Particle* p, int i_nuclide) } } -void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, double& E, - Direction& u, double& mu_lab) +void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, + Particle* p) { // get pointer to nuclide const auto& nuc {data::nuclides[i_nuclide]}; - double vel = std::sqrt(E); + double vel = std::sqrt(p->E_); double awr = nuc->awr_; // Neutron velocity in LAB - Direction v_n = vel*u; + Direction v_n = vel*p->u(); // Sample velocity of target nucleus Direction v_t {}; - if (!simulation::micro_xs[i_nuclide].use_ptable) { - v_t = sample_target_velocity(nuc.get(), E, u, v_n, - simulation::micro_xs[i_nuclide].elastic, kT); + if (!p->neutron_xs_[i_nuclide].use_ptable) { + v_t = sample_target_velocity(nuc.get(), p->E_, p->u(), v_n, + p->neutron_xs_[i_nuclide].elastic, kT); } // Velocity of center-of-mass @@ -706,7 +687,7 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, double& E, auto& d = rx.products_[0].distribution_[0]; auto d_ = dynamic_cast(d.get()); if (d_) { - mu_cm = d_->angle().sample(E); + mu_cm = d_->angle().sample(p->E_); } else { mu_cm = 2.0*prn() - 1.0; } @@ -722,35 +703,35 @@ void elastic_scatter(int i_nuclide, const Reaction& rx, double kT, double& E, // Transform back to LAB frame v_n += v_cm; - E = v_n.dot(v_n); - vel = std::sqrt(E); + p->E_ = v_n.dot(v_n); + vel = std::sqrt(p->E_); // compute cosine of scattering angle in LAB frame by taking dot product of // neutron's pre- and post-collision angle - mu_lab = u.dot(v_n) / vel; + p->mu_ = p->u().dot(v_n) / vel; // Set energy and direction of particle in LAB frame - u = v_n / vel; + p->u() = v_n / vel; // Because of floating-point roundoff, it may be possible for mu_lab to be // outside of the range [-1,1). In these cases, we just set mu_lab to exactly // -1 or 1 - if (std::abs(mu_lab) > 1.0) mu_lab = std::copysign(1.0, mu_lab); + if (std::abs(p->mu_) > 1.0) p->mu_ = std::copysign(1.0, p->mu_); } -void sab_scatter(int i_nuclide, int i_sab, double& E, Direction& u, double& mu) +void sab_scatter(int i_nuclide, int i_sab, Particle* p) { // Determine temperature index - const auto& micro {simulation::micro_xs[i_nuclide]}; + const auto& micro {p->neutron_xs_[i_nuclide]}; int i_temp = micro.index_temp_sab; // Sample energy and angle double E_out; - data::thermal_scatt[i_sab]->data_[i_temp].sample(micro, E, &E_out, &mu); + data::thermal_scatt[i_sab]->data_[i_temp].sample(micro, p->E_, &E_out, &p->mu_); // Set energy to outgoing, change direction of particle - E = E_out; - u = rotate_angle(u, mu, nullptr); + p->E_ = E_out; + p->u() = rotate_angle(p->u(), p->mu_, nullptr); } Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u, @@ -898,8 +879,10 @@ Direction sample_target_velocity(const Nuclide* nuc, double E, Direction u, } } } - } // case RVS, DBRC + } // case RVS, DBRC } // switch (sampling_method) + + UNREACHABLE(); } Direction @@ -1096,8 +1079,8 @@ void inelastic_scatter(const Nuclide* nuc, const Reaction* rx, Particle* p) void sample_secondary_photons(Particle* p, int i_nuclide) { // Sample the number of photons produced - double y_t = p->wgt_ * simulation::micro_xs[i_nuclide].photon_prod / - simulation::micro_xs[i_nuclide].total; + double y_t = p->wgt_ * p->neutron_xs_[i_nuclide].photon_prod / + p->neutron_xs_[i_nuclide].total; int y = static_cast(y_t); if (prn() <= y_t - y) ++y; @@ -1106,7 +1089,7 @@ void sample_secondary_photons(Particle* p, int i_nuclide) // Sample the reaction and product int i_rx; int i_product; - sample_photon_product(i_nuclide, p->E_, &i_rx, &i_product); + sample_photon_product(i_nuclide, p, &i_rx, &i_product); // Sample the outgoing energy and angle auto& rx = data::nuclides[i_nuclide]->reactions_[i_rx]; diff --git a/src/physics_mg.cpp b/src/physics_mg.cpp index fa6365a3a..f99d15aac 100644 --- a/src/physics_mg.cpp +++ b/src/physics_mg.cpp @@ -48,19 +48,16 @@ sample_reaction(Particle* p) if (model::materials[p->material_]->fissionable_) { if (settings::run_mode == RUN_MODE_EIGENVALUE) { - create_fission_sites( - p, simulation::fission_bank.data(), &simulation::n_bank, - simulation::fission_bank.size()); + create_fission_sites(p, simulation::fission_bank); } else if ((settings::run_mode == RUN_MODE_FIXEDSOURCE) && (settings::create_fission_neutrons)) { - create_fission_sites(p, p->secondary_bank_, &(p->n_secondary_), - MAX_SECONDARY); + create_fission_sites(p, simulation::secondary_bank); } } // If survival biasing is being used, the following subroutine adjusts the // weight of the particle. Otherwise, it checks to see if absorption occurs. - if (simulation::material_xs.absorption > 0.) { + if (p->macro_xs_.absorption > 0.) { absorption(p); } else { p->wgt_absorb_ = 0.; @@ -102,18 +99,15 @@ scatter(Particle* p) } void -create_fission_sites(Particle* p, Particle::Bank* bank_array, int64_t* size_bank, - int64_t bank_array_size) +create_fission_sites(Particle* p, std::vector& bank) { - // TODO: Heat generation from fission - // If uniform fission source weighting is turned on, we increase or decrease // the expected number of fission sites produced double weight = settings::ufs_on ? ufs_get_weight(p) : 1.0; // Determine the expected number of neutrons produced double nu_t = p->wgt_ / simulation::keff * weight * - simulation::material_xs.nu_fission / simulation::material_xs.total; + p->macro_xs_.nu_fission / p->macro_xs_.total; // Sample the number of neutrons produced int nu = static_cast(nu_t); @@ -121,62 +115,42 @@ create_fission_sites(Particle* p, Particle::Bank* bank_array, int64_t* size_bank nu++; } - // Check for the bank size getting hit. For fixed source calculations, this - // is a fatal error; for eigenvalue calculations, it just means that k-eff - // was too high for a single batch. - if (*size_bank + nu > bank_array_size) { - if (settings::run_mode == RUN_MODE_FIXEDSOURCE) { - throw std::runtime_error{"Secondary particle bank size limit reached." - " If you are running a subcritical multiplication problem," - " k-effective may be too close to one."}; - } else { - if (mpi::master) { - std::stringstream msg; - msg << "Maximum number of sites in fission bank reached. This can" - " result in irreproducible results using different numbers of" - " processes/threads."; - warning(msg); - } - } - } - // Begin banking the source neutrons // First, if our bank is full then don't continue - if ((nu == 0) || (*size_bank == bank_array_size)) return; + if (nu == 0) return; // Initialize the counter of delayed neutrons encountered for each delayed // group. double nu_d[MAX_DELAYED_GROUPS] = {0.}; p->fission_ = true; - for (size_t i = static_cast(*size_bank); - i < static_cast(std::min(*size_bank + nu, bank_array_size)); i++) { + for (int i = 0; i < nu; ++i) { + // Create new bank site and get reference to last element + bank.emplace_back(); + auto& site {bank.back()}; + // Bank source neutrons by copying the particle data - bank_array[i].r = p->r(); - - // Set that the bank particle is a neutron - bank_array[i].particle = Particle::Type::neutron; - - // Set the weight of the fission bank site - bank_array[i].wgt = 1. / weight; + site.r = p->r(); + site.particle = Particle::Type::neutron; + site.wgt = 1. / weight; // Sample the cosine of the angle, assuming fission neutrons are emitted // isotropically - double mu = 2. * prn() - 1.; + double mu = 2.*prn() - 1.; // Sample the azimuthal angle uniformly in [0, 2.pi) double phi = 2. * PI * prn(); - bank_array[i].u.x = mu; - bank_array[i].u.y = std::sqrt(1. - mu * mu) * std::cos(phi); - bank_array[i].u.z = std::sqrt(1. - mu * mu) * std::sin(phi); + site.u.x = mu; + site.u.y = std::sqrt(1. - mu * mu) * std::cos(phi); + site.u.z = std::sqrt(1. - mu * mu) * std::sin(phi); // Sample secondary energy distribution for the fission reaction and set // the energy in the fission bank int dg; int gout; data::macro_xs[p->material_].sample_fission_energy(p->g_ - 1, dg, gout); - bank_array[i].E = gout + 1; - bank_array[i].delayed_group = dg + 1; + site.E = gout + 1; + site.delayed_group = dg + 1; // Set the delayed group on the particle as well p->delayed_group_ = dg + 1; @@ -187,9 +161,6 @@ create_fission_sites(Particle* p, Particle::Bank* bank_array, int64_t* size_bank } } - // Increment number of bank sites - *size_bank = std::min(*size_bank + nu, bank_array_size); - // Store the total weight banked for analog fission tallies p->n_bank_ = nu; p->wgt_bank_ = nu / weight; @@ -203,24 +174,21 @@ absorption(Particle* p) { if (settings::survival_biasing) { // Determine weight absorbed in survival biasing - p->wgt_absorb_ = p->wgt_ * - simulation::material_xs.absorption / simulation::material_xs.total; + p->wgt_absorb_ = p->wgt_ * p->macro_xs_.absorption / p->macro_xs_.total; // Adjust weight of particle by the probability of absorption p->wgt_ -= p->wgt_absorb_; p->wgt_last_ = p->wgt_; // Score implicit absorpion estimate of keff -#pragma omp atomic - global_tally_absorption += p->wgt_absorb_ * - simulation::material_xs.nu_fission / - simulation::material_xs.absorption; + #pragma omp atomic + global_tally_absorption += p->wgt_absorb_ * p->macro_xs_.nu_fission / + p->macro_xs_.absorption; } else { - if (simulation::material_xs.absorption > - prn() * simulation::material_xs.total) { -#pragma omp atomic - global_tally_absorption += p->wgt_ * simulation::material_xs.nu_fission / - simulation::material_xs.absorption; + if (p->macro_xs_.absorption > prn() * p->macro_xs_.total) { + #pragma omp atomic + global_tally_absorption += p->wgt_ * p->macro_xs_.nu_fission / + p->macro_xs_.absorption; p->alive_ = false; p->event_ = EVENT_ABSORB; } diff --git a/src/plot.cpp b/src/plot.cpp index ae2f00023..57466eca1 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -1,9 +1,11 @@ #include "openmc/plot.h" +#include #include #include -#include "openmc/cell.h" +#include "xtensor/xview.hpp" + #include "openmc/constants.h" #include "openmc/file_utils.h" #include "openmc/geometry.h" @@ -27,6 +29,38 @@ namespace openmc { const RGBColor WHITE {255, 255, 255}; constexpr int PLOT_LEVEL_LOWEST {-1}; //!< lower bound on plot universe level +constexpr int32_t NOT_FOUND {-2}; + +IdData::IdData(size_t h_res, size_t v_res) + : data_({v_res, h_res, 2}, NOT_FOUND) +{ } + +void +IdData::set_value(size_t y, size_t x, const Particle& p, int level) { + Cell* c = model::cells[p.coord_[level].cell].get(); + data_(y,x,0) = c->id_; + if (p.material_ == MATERIAL_VOID) { + data_(y,x,1) = MATERIAL_VOID; + return; + } else if (c->type_ != FILL_UNIVERSE) { + Material* m = model::materials[p.material_].get(); + data_(y,x,1) = m->id_; + } +} + +PropertyData::PropertyData(size_t h_res, size_t v_res) + : data_({v_res, h_res, 2}, NOT_FOUND) +{ } + +void +PropertyData::set_value(size_t y, size_t x, const Particle& p, int level) { + Cell* c = model::cells[p.coord_[level].cell].get(); + data_(y,x,0) = (p.sqrtkT_ * p.sqrtkT_) / K_BOLTZMANN; + if (c->type_ != FILL_UNIVERSE && p.material_ != MATERIAL_VOID) { + Material* m = model::materials[p.material_].get(); + data_(y,x,1) = m->density_gpcc_; + } +} //============================================================================== // Global variables @@ -46,8 +80,6 @@ std::unordered_map plot_map; extern "C" int openmc_plot_geometry() { - int err; - for (auto pl : model::plots) { std::stringstream ss; ss << "Processing plot " << pl.id_ << ": " @@ -99,60 +131,29 @@ void create_ppm(Plot pl) size_t width = pl.pixels_[0]; size_t height = pl.pixels_[1]; - double in_pixel = (pl.width_[0])/static_cast(width); - double out_pixel = (pl.width_[1])/static_cast(height); + ImageData data({width, height}, pl.not_found_); - ImageData data; - data.resize({width, height}); + // generate ids for the plot + auto ids = pl.get_map(); - int in_i, out_i; - Position r; - switch(pl.basis_) { - case PlotBasis::xy : - in_i = 0; - out_i = 1; - r.x = pl.origin_[0] - pl.width_[0] / 2.; - r.y = pl.origin_[1] + pl.width_[1] / 2.; - r.z = pl.origin_[2]; - break; - case PlotBasis::xz : - in_i = 0; - out_i = 2; - r.x = pl.origin_[0] - pl.width_[0] / 2.; - r.y = pl.origin_[1]; - r.z = pl.origin_[2] + pl.width_[1] / 2.; - break; - case PlotBasis::yz : - in_i = 1; - out_i = 2; - r.x = pl.origin_[0]; - r.y = pl.origin_[1] - pl.width_[0] / 2.; - r.z = pl.origin_[2] + pl.width_[1] / 2.; - break; - } + // assign colors + for (size_t y = 0; y < height; y++) { + for (size_t x = 0; x < width; x++) { + auto id = ids.data_(y, x, pl.color_by_); + // no setting needed if not found + if (id == NOT_FOUND) { continue; } + if (PlotColorBy::cells == pl.color_by_) { + data(x,y) = pl.colors_[model::cell_map[id]]; + } else if (PlotColorBy::mats == pl.color_by_) { + if (id == MATERIAL_VOID) { + data(x,y) = WHITE; + continue; + } + data(x,y) = pl.colors_[model::material_map[id]]; + } // color_by if-else + } // x for loop + } // y for loop - Direction u {0.5, 0.5, 0.5}; - -#pragma omp parallel -{ - Particle p; - p.r() = r; - p.u() = u; - p.coord_[0].universe = model::root_universe; - -#pragma omp for - for (int y = 0; y < height; y++) { - p.r()[out_i] = r[out_i] - out_pixel * y; - for (int x = 0; x < width; x++) { - // local variables - RGBColor rgb; - int id; - p.r()[in_i] = r[in_i] + in_pixel * x; - position_rgb(p, pl, rgb, id); - data(x,y) = rgb; - } - } -} // draw mesh lines if present if (pl.index_meshlines_mesh_ >= 0) {draw_mesh_lines(pl, data);} @@ -618,8 +619,8 @@ Plot::set_mask(pugi::xml_node plot_node) } } -Plot::Plot(pugi::xml_node plot_node): -index_meshlines_mesh_(-1) +Plot::Plot(pugi::xml_node plot_node) + : index_meshlines_mesh_{-1} { set_id(plot_node); set_type(plot_node); @@ -635,53 +636,6 @@ index_meshlines_mesh_(-1) set_mask(plot_node); } // End Plot constructor -//============================================================================== -// POSITION_RGB computes the red/green/blue values for a given plot with the -// current particle's position -//============================================================================== - - -void position_rgb(Particle p, Plot pl, RGBColor& rgb, int& id) -{ - p.n_coord_ = 1; - - bool found_cell = find_cell(&p, 0); - - int j = p.n_coord_ - 1; - - if (settings::check_overlaps) {check_cell_overlap(&p);} - - // Set coordinate level if specified - if (pl.level_ >= 0) {j = pl.level_ + 1;} - - if (!found_cell) { - // If no cell, revert to default color - rgb = pl.not_found_; - id = -1; - } else { - if (PlotColorBy::mats == pl.color_by_) { - // Assign color based on material - const auto& c = model::cells[p.coord_[j].cell]; - if (c->type_ == FILL_UNIVERSE) { - // If we stopped on a middle universe level, treat as if not found - rgb = pl.not_found_; - id = -1; - } else if (p.material_ == MATERIAL_VOID) { - // By default, color void cells white - rgb = WHITE; - id = -1; - } else { - rgb = pl.colors_[p.material_]; - id = model::materials[p.material_]->id_; - } - } else if (PlotColorBy::cells == pl.color_by_) { - // Assign color based on cell - rgb = pl.colors_[p.coord_[j].cell]; - id = model::cells[p.coord_[j].cell]->id_; - } - } // endif found_cell -} - //============================================================================== // OUTPUT_PPM writes out a previously generated image to a PPM file //============================================================================== @@ -739,6 +693,8 @@ void draw_mesh_lines(Plot pl, ImageData& data) outer = 1; inner = 2; break; + default: + UNREACHABLE(); } Position ll_plot {pl.origin_}; @@ -813,18 +769,17 @@ void draw_mesh_lines(Plot pl, ImageData& data) // CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D // geometry visualization. It works the same way as create_ppm by dragging a // particle across the geometry for the specified number of voxels. The first 3 -// int(4)'s in the binary are the number of x, y, and z voxels. The next 3 -// real(8)'s are the widths of the voxels in the x, y, and z directions. The -// next 3 real(8)'s are the x, y, and z coordinates of the lower left -// point. Finally the binary is filled with entries of four int(4)'s each. Each -// 'row' in the binary contains four int(4)'s: 3 for x,y,z position and 1 for +// int's in the binary are the number of x, y, and z voxels. The next 3 +// double's are the widths of the voxels in the x, y, and z directions. The +// next 3 double's are the x, y, and z coordinates of the lower left +// point. Finally the binary is filled with entries of four int's each. Each +// 'row' in the binary contains four int's: 3 for x,y,z position and 1 for // cell or material id. For 1 million voxels this produces a file of // approximately 15MB. // ============================================================================= void create_voxel(Plot pl) { - // compute voxel widths in each direction std::array vox; vox[0] = pl.width_[0]/(double)pl.pixels_[0]; @@ -834,13 +789,6 @@ void create_voxel(Plot pl) // initial particle position Position ll = pl.origin_ - pl.width_ / 2.; - // allocate and initialize particle - Direction u {0.5, 0.5, 0.5}; - Particle p; - p.r() = ll; - p.u() = u; - p.coord_[0].universe = model::root_universe; - // Open binary plot file for writing std::ofstream of; std::string fname = std::string(pl.path_plot_); @@ -858,8 +806,9 @@ void create_voxel(Plot pl) // Write current date and time write_attribute(file_id, "date_and_time", time_stamp().c_str()); - hsize_t three = 3; - write_attribute(file_id, "num_voxels", pl.pixels_); + std::array pixels; + std::copy(pl.pixels_.begin(), pl.pixels_.end(), pixels.begin()); + write_attribute(file_id, "num_voxels", pixels); write_attribute(file_id, "voxel_width", vox); write_attribute(file_id, "lower_left", ll); @@ -872,43 +821,33 @@ void create_voxel(Plot pl) hid_t dspace, dset, memspace; voxel_init(file_id, &(dims[0]), &dspace, &dset, &memspace); - // move to center of voxels - ll.x += vox[0] / 2.; - ll.y += vox[1] / 2.; - ll.z += vox[2] / 2.; - - int data[pl.pixels_[1]][pl.pixels_[0]]; + PlotBase pltbase; + pltbase.width_ = pl.width_; + pltbase.origin_ = pl.origin_; + pltbase.basis_ = PlotBasis::xy; + pltbase.pixels_ = pl.pixels_; + pltbase.level_ = -1; // all universes for voxel files ProgressBar pb; - - RGBColor rgb; - int id; for (int z = 0; z < pl.pixels_[2]; z++) { + // update progress bar pb.set_value(100.*(double)z/(double)(pl.pixels_[2]-1)); - for (int y = 0; y < pl.pixels_[1]; y++) { - for (int x = 0; x < pl.pixels_[0]; x++) { - // get voxel color - position_rgb(p, pl, rgb, id); - // write to plot data - data[y][x] = id; - // advance particle in x direction - p.r().x += vox[0]; - } - // advance particle in y direction - p.r().y += vox[1]; - p.r().x = ll[0]; - } - // advance particle in z direction - p.r().z += vox[2]; - p.r().y = ll[1]; - p.r().x = ll[0]; + + // update z coordinate + pltbase.origin_.z = ll.z + z * vox[2]; + + // 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); + // Write to HDF5 dataset - voxel_write_slice(z, dspace, dset, memspace, &(data[0])); + voxel_write_slice(z, dspace, dset, memspace, &(data1(0,0))); } voxel_finalize(dspace, dset, memspace); file_close(file_id); - } void @@ -952,4 +891,38 @@ RGBColor random_color() { return {int(prn()*255), int(prn()*255), int(prn()*255)}; } +extern "C" int openmc_id_map(const void* plot, int32_t* data_out) +{ + + auto plt = reinterpret_cast(plot); + if (!plt) { + set_errmsg("Invalid slice pointer passed to openmc_id_map"); + return OPENMC_E_INVALID_ARGUMENT; + } + + auto ids = plt->get_map(); + + // write id data to array + std::copy(ids.data_.begin(), ids.data_.end(), data_out); + + return 0; +} + +extern "C" int openmc_property_map(const void* plot, double* data_out) { + + auto plt = reinterpret_cast(plot); + if (!plt) { + set_errmsg("Invalid slice pointer passed to openmc_id_map"); + return OPENMC_E_INVALID_ARGUMENT; + } + + auto props = plt->get_map(); + + // write id data to array + std::copy(props.data_.begin(), props.data_.end(), data_out); + + return 0; +} + + } // namespace openmc diff --git a/src/random_lcg.cpp b/src/random_lcg.cpp index cf5d7e34b..5002b4279 100644 --- a/src/random_lcg.cpp +++ b/src/random_lcg.cpp @@ -141,7 +141,7 @@ extern "C" void openmc_set_seed(int64_t new_seed) { seed = new_seed; -#pragma omp parallel + #pragma omp parallel { for (int i = 0; i < N_STREAMS; i++) { prn_seed[i] = seed + i; diff --git a/src/reaction.cpp b/src/reaction.cpp index 1f997f078..d2405d899 100644 --- a/src/reaction.cpp +++ b/src/reaction.cpp @@ -229,8 +229,10 @@ std::string reaction_name(int mt) return "(n,X3He)"; } else if (mt == 207) { return "(n,Xa)"; + } else if (mt == 301) { + return "heating"; } else if (mt == 444) { - return "(damage)"; + return "damage-energy"; } else if (mt == COHERENT) { return "coherent scatter"; } else if (mt == INCOHERENT) { diff --git a/src/secondary_kalbach.cpp b/src/secondary_kalbach.cpp index cec5344de..e3974f357 100644 --- a/src/secondary_kalbach.cpp +++ b/src/secondary_kalbach.cpp @@ -199,7 +199,7 @@ void KalbachMann::sample(double E_in, double& E_out, double& mu) const km_r = distribution_[l].r[k]; km_a = distribution_[l].a[k]; - } else if (distribution_[l].interpolation == Interpolation::lin_lin) { + } else { // Linear-linear interpolation double E_l_k1 = distribution_[l].e_out[k+1]; double p_l_k1 = distribution_[l].p[k+1]; diff --git a/src/secondary_nbody.cpp b/src/secondary_nbody.cpp index dabd9de3a..564fca73d 100644 --- a/src/secondary_nbody.cpp +++ b/src/secondary_nbody.cpp @@ -55,6 +55,8 @@ void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu) const r6 = prn(); y = -std::log(r1*r2*r3*r4) - std::log(r5) * std::pow(std::cos(PI/2.0*r6), 2); break; + default: + throw std::runtime_error{"N-body phase space with >5 bodies."}; } // Now determine v and E_out diff --git a/src/settings.cpp b/src/settings.cpp index dfa5ebb7e..4c041a080 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -383,20 +383,8 @@ void read_settings_xml() // Number of OpenMP threads if (check_for_node(root, "threads")) { -#ifdef _OPENMP - if (simulation::n_threads == 0) { - simulation::n_threads = std::stoi(get_node_value(root, "threads")); - if (simulation::n_threads < 1) { - std::stringstream msg; - msg << "Invalid number of threads: " << simulation::n_threads; - fatal_error(msg); - } - omp_set_num_threads(simulation::n_threads); - } -#else - if (mpi::master) warning("OpenMC was not compiled with OpenMP support; " - "ignoring number of threads."); -#endif + if (mpi::master) warning("The element has been deprecated. Use " + "the OMP_NUM_THREADS environment variable to set the number of threads."); } // ========================================================================== diff --git a/src/simulation.cpp b/src/simulation.cpp index c0e534fc3..fa565b575 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -20,7 +20,9 @@ #include "openmc/tallies/tally.h" #include "openmc/tallies/trigger.h" +#ifdef _OPENMP #include +#endif #include "xtensor/xview.hpp" #include @@ -59,7 +61,7 @@ int openmc_simulation_init() calculate_work(); // Allocate array for matching filter bins -#pragma omp parallel + #pragma omp parallel { simulation::filter_matches.resize(model::tally_filters.size()); } @@ -78,13 +80,6 @@ int openmc_simulation_init() mat->init_nuclide_index(); } - // Create cross section caches - #pragma omp parallel - { - simulation::micro_xs = new NuclideMicroXS[data::nuclides.size()]; - simulation::micro_photon_xs = new ElementMicroXS[data::elements.size()]; - } - // Reset global variables -- this is done before loading state point (as that // will potentially populate k_generation and entropy) simulation::current_batch = 0; @@ -133,13 +128,6 @@ int openmc_simulation_finalize() mat->mat_nuclide_index_.clear(); } - // Clear cross section caches - #pragma omp parallel - { - delete[] simulation::micro_xs; - delete[] simulation::micro_photon_xs; - } - // Increment total number of generations simulation::total_gen += simulation::current_batch*settings::gen_per_batch; @@ -150,7 +138,7 @@ int openmc_simulation_finalize() // Write tally results to tallies.out if (settings::output_tallies && mpi::master) write_tallies(); -#pragma omp parallel + #pragma omp parallel { simulation::filter_matches.clear(); } @@ -200,8 +188,8 @@ int openmc_next_batch(int* status) // ==================================================================== // LOOP OVER PARTICLES -#pragma omp parallel for schedule(runtime) - for (int64_t i_work = 1; i_work <= simulation::work; ++i_work) { + #pragma omp parallel for schedule(runtime) + for (int64_t i_work = 1; i_work <= simulation::work_per_rank; ++i_work) { simulation::current_work = i_work; // grab source particle from bank @@ -267,17 +255,13 @@ int restart_batch; bool satisfy_triggers {false}; int total_gen {0}; double total_weight; -int64_t work; +int64_t work_per_rank; std::vector k_generation; std::vector work_index; // Threadprivate variables bool trace; //!< flag to show debug information -#ifdef _OPENMP -int n_threads {-1}; //!< number of OpenMP threads -int thread_id; //!< ID of a given thread -#endif } // namespace simulation @@ -288,7 +272,7 @@ int thread_id; //!< ID of a given thread void allocate_banks() { // Allocate source bank - simulation::source_bank.resize(simulation::work); + simulation::source_bank.resize(simulation::work_per_rank); if (settings::run_mode == RUN_MODE_EIGENVALUE) { #ifdef _OPENMP @@ -297,20 +281,18 @@ void allocate_banks() // a generation, there is also a 'master_fission_bank' that is used to // collect the sites from each thread. - simulation::n_threads = omp_get_max_threads(); - -#pragma omp parallel + #pragma omp parallel { - simulation::thread_id = omp_get_thread_num(); - if (simulation::thread_id == 0) { - simulation::fission_bank.resize(3*simulation::work); + if (omp_get_thread_num() == 0) { + simulation::fission_bank.reserve(3*simulation::work_per_rank); } else { - simulation::fission_bank.resize(3*simulation::work / simulation::n_threads); + int n_threads = omp_get_num_threads(); + simulation::fission_bank.reserve(3*simulation::work_per_rank / n_threads); } } - simulation::master_fission_bank.resize(3*simulation::work); + simulation::master_fission_bank.reserve(3*simulation::work_per_rank); #else - simulation::fission_bank.resize(3*simulation::work); + simulation::fission_bank.reserve(3*simulation::work_per_rank); #endif } } @@ -410,8 +392,8 @@ void finalize_batch() void initialize_generation() { if (settings::run_mode == RUN_MODE_EIGENVALUE) { - // Reset number of fission bank sites - simulation::n_bank = 0; + // Clear out the fission bank + simulation::fission_bank.clear(); // Count source sites if using uniform fission source weighting if (settings::ufs_on) ufs_count_sites(); @@ -427,9 +409,9 @@ void finalize_generation() auto& gt = simulation::global_tallies; // Update global tallies with the omp private accumulation variables -#pragma omp parallel + #pragma omp parallel { -#pragma omp critical(increment_global_tallies) + #pragma omp critical(increment_global_tallies) { if (settings::run_mode == RUN_MODE_EIGENVALUE) { gt(K_COLLISION, RESULT_VALUE) += global_tally_collision; @@ -535,7 +517,7 @@ void calculate_work() int64_t work_i = i < remainder ? min_work + 1 : min_work; // Set number of particles - if (mpi::rank == i) simulation::work = work_i; + if (mpi::rank == i) simulation::work_per_rank = work_i; // Set index into source bank for rank i i_bank += work_i; diff --git a/src/source.cpp b/src/source.cpp index 36d66394c..0baa21b17 100644 --- a/src/source.cpp +++ b/src/source.cpp @@ -264,7 +264,7 @@ void initialize_source() } else { // Generation source sites from specified distribution in user input - for (int64_t i = 0; i < simulation::work; ++i) { + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { // initialize random number seed int64_t id = simulation::total_gen*settings::n_particles + simulation::work_index[mpi::rank] + i + 1; @@ -330,7 +330,7 @@ void free_memory_source() void fill_source_bank_fixedsource() { if (settings::path_source.empty()) { - for (int64_t i = 0; i < simulation::work; ++i) { + for (int64_t i = 0; i < simulation::work_per_rank; ++i) { // initialize random number seed int64_t id = (simulation::total_gen + overall_generation()) * settings::n_particles + simulation::work_index[mpi::rank] + i + 1; diff --git a/src/state_point.cpp b/src/state_point.cpp index 9fe5a960f..66eaf7ffd 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -512,6 +512,7 @@ hid_t h5banktype() { H5Tinsert(banktype, "E", HOFFSET(Particle::Bank, E), H5T_NATIVE_DOUBLE); H5Tinsert(banktype, "wgt", HOFFSET(Particle::Bank, wgt), H5T_NATIVE_DOUBLE); H5Tinsert(banktype, "delayed_group", HOFFSET(Particle::Bank, delayed_group), H5T_NATIVE_INT); + H5Tinsert(banktype, "particle", HOFFSET(Particle::Bank, particle), H5T_NATIVE_INT); H5Tclose(postype); return banktype; @@ -568,7 +569,7 @@ write_source_bank(hid_t group_id) H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); // Create another data space but for each proc individually - hsize_t count[] {static_cast(simulation::work)}; + hsize_t count[] {static_cast(simulation::work_per_rank)}; hid_t memspace = H5Screate_simple(1, count, nullptr); // Select hyperslab for this dataspace @@ -600,7 +601,7 @@ write_source_bank(hid_t group_id) // Save source bank sites since the souce_bank array is overwritten below #ifdef OPENMC_MPI std::vector temp_source {simulation::source_bank.begin(), - simulation::source_bank.begin() + simulation::work}; + simulation::source_bank.begin() + simulation::work_per_rank}; #endif for (int i = 0; i < mpi::n_procs; ++i) { @@ -638,7 +639,7 @@ write_source_bank(hid_t group_id) #endif } else { #ifdef OPENMC_MPI - MPI_Send(simulation::source_bank.data(), simulation::work, mpi::bank, + MPI_Send(simulation::source_bank.data(), simulation::work_per_rank, mpi::bank, 0, mpi::rank, mpi::intracomm); #endif } @@ -656,7 +657,7 @@ void read_source_bank(hid_t group_id) hid_t dset = H5Dopen(group_id, "source_bank", H5P_DEFAULT); // Create another data space but for each proc individually - hsize_t dims[] {static_cast(simulation::work)}; + hsize_t dims[] {static_cast(simulation::work_per_rank)}; hid_t memspace = H5Screate_simple(1, dims, nullptr); // Make sure source bank is big enough diff --git a/src/surface.cpp b/src/surface.cpp index bf0315698..f30b44431 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -241,7 +241,7 @@ double DAGSurface::distance(Position r, Direction u, bool coincident) const { moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); moab::EntityHandle hit_surf; double dist; double pnt[3] = {r.x, r.y, r.z}; @@ -256,7 +256,7 @@ Direction DAGSurface::normal(Position r) const { moab::ErrorCode rval; Direction u; - moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); double pnt[3] = {r.x, r.y, r.z}; double dir[3] = {u.x, u.y, u.z}; rval = dagmc_ptr_->get_angle(surf, pnt, dir); @@ -267,7 +267,7 @@ Direction DAGSurface::normal(Position r) const BoundingBox DAGSurface::bounding_box() const { moab::ErrorCode rval; - moab::EntityHandle surf = dagmc_ptr_->entity_by_id(2, id_); + moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); double min[3], max[3]; rval = dagmc_ptr_->getobb(surf, min, max); MB_CHK_ERR_CONT(rval); diff --git a/src/tallies/derivative.cpp b/src/tallies/derivative.cpp index 315cc081e..290d09384 100644 --- a/src/tallies/derivative.cpp +++ b/src/tallies/derivative.cpp @@ -221,12 +221,12 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, switch (score_bin) { case SCORE_TOTAL: - if (i_nuclide == -1 && simulation::material_xs.total > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.total > 0.0) { score *= flux_deriv - + simulation::micro_xs[deriv.diff_nuclide].total - / simulation::material_xs.total; + + p->neutron_xs_[deriv.diff_nuclide].total + / p->macro_xs_.total; } else if (i_nuclide == deriv.diff_nuclide - && simulation::micro_xs[i_nuclide].total) { + && p->neutron_xs_[i_nuclide].total) { score *= flux_deriv + 1. / atom_density; } else { score *= flux_deriv; @@ -234,13 +234,13 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, break; case SCORE_SCATTER: - if (i_nuclide == -1 && (simulation::material_xs.total - - simulation::material_xs.absorption) > 0.0) { + if (i_nuclide == -1 && (p->macro_xs_.total + - p->macro_xs_.absorption) > 0.0) { score *= flux_deriv - + (simulation::micro_xs[deriv.diff_nuclide].total - - simulation::micro_xs[deriv.diff_nuclide].absorption) - / (simulation::material_xs.total - - simulation::material_xs.absorption); + + (p->neutron_xs_[deriv.diff_nuclide].total + - p->neutron_xs_[deriv.diff_nuclide].absorption) + / (p->macro_xs_.total + - p->macro_xs_.absorption); } else if (i_nuclide == deriv.diff_nuclide) { score *= flux_deriv + 1. / atom_density; } else { @@ -249,12 +249,12 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, break; case SCORE_ABSORPTION: - if (i_nuclide == -1 && simulation::material_xs.absorption > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.absorption > 0.0) { score *= flux_deriv - + simulation::micro_xs[deriv.diff_nuclide].absorption - / simulation::material_xs.absorption; + + p->neutron_xs_[deriv.diff_nuclide].absorption + / p->macro_xs_.absorption; } else if (i_nuclide == deriv.diff_nuclide - && simulation::micro_xs[i_nuclide].absorption) { + && p->neutron_xs_[i_nuclide].absorption) { score *= flux_deriv + 1. / atom_density; } else { score *= flux_deriv; @@ -262,12 +262,12 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, break; case SCORE_FISSION: - if (i_nuclide == -1 && simulation::material_xs.fission > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.fission > 0.0) { score *= flux_deriv - + simulation::micro_xs[deriv.diff_nuclide].fission - / simulation::material_xs.fission; + + p->neutron_xs_[deriv.diff_nuclide].fission + / p->macro_xs_.fission; } else if (i_nuclide == deriv.diff_nuclide - && simulation::micro_xs[i_nuclide].fission) { + && p->neutron_xs_[i_nuclide].fission) { score *= flux_deriv + 1. / atom_density; } else { score *= flux_deriv; @@ -275,12 +275,12 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, break; case SCORE_NU_FISSION: - if (i_nuclide == -1 && simulation::material_xs.nu_fission > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.nu_fission > 0.0) { score *= flux_deriv - + simulation::micro_xs[deriv.diff_nuclide].nu_fission - / simulation::material_xs.nu_fission; + + p->neutron_xs_[deriv.diff_nuclide].nu_fission + / p->macro_xs_.nu_fission; } else if (i_nuclide == deriv.diff_nuclide - && simulation::micro_xs[i_nuclide].nu_fission) { + && p->neutron_xs_[i_nuclide].nu_fission) { score *= flux_deriv + 1. / atom_density; } else { score *= flux_deriv; @@ -331,64 +331,64 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, switch (score_bin) { case SCORE_TOTAL: - if (simulation::micro_xs[p->event_nuclide_].total) { + if (p->neutron_xs_[p->event_nuclide_].total) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv + (dsig_s + dsig_a) * material.atom_density_(i) - / simulation::material_xs.total; + / p->macro_xs_.total; } else { score *= flux_deriv; } break; case SCORE_SCATTER: - if (simulation::micro_xs[p->event_nuclide_].total - - simulation::micro_xs[p->event_nuclide_].absorption) { + if (p->neutron_xs_[p->event_nuclide_].total + - p->neutron_xs_[p->event_nuclide_].absorption) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv + dsig_s * material.atom_density_(i) - / (simulation::material_xs.total - - simulation::material_xs.absorption); + / (p->macro_xs_.total + - p->macro_xs_.absorption); } else { score *= flux_deriv; } break; case SCORE_ABSORPTION: - if (simulation::micro_xs[p->event_nuclide_].absorption) { + if (p->neutron_xs_[p->event_nuclide_].absorption) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv + dsig_a * material.atom_density_(i) - / simulation::material_xs.absorption; + / p->macro_xs_.absorption; } else { score *= flux_deriv; } break; case SCORE_FISSION: - if (simulation::micro_xs[p->event_nuclide_].fission) { + if (p->neutron_xs_[p->event_nuclide_].fission) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv + dsig_f * material.atom_density_(i) - / simulation::material_xs.fission; + / p->macro_xs_.fission; } else { score *= flux_deriv; } break; case SCORE_NU_FISSION: - if (simulation::micro_xs[p->event_nuclide_].fission) { - double nu = simulation::micro_xs[p->event_nuclide_].nu_fission - / simulation::micro_xs[p->event_nuclide_].fission; + if (p->neutron_xs_[p->event_nuclide_].fission) { + double nu = p->neutron_xs_[p->event_nuclide_].nu_fission + / p->neutron_xs_[p->event_nuclide_].fission; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv + nu * dsig_f * material.atom_density_(i) - / simulation::material_xs.nu_fission; + / p->macro_xs_.nu_fission; } else { score *= flux_deriv; } @@ -413,141 +413,141 @@ apply_derivative_to_score(const Particle* p, int i_tally, int i_nuclide, switch (score_bin) { case SCORE_TOTAL: - if (i_nuclide == -1 && simulation::material_xs.total > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.total > 0.0) { double cum_dsig = 0; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto i_nuc = material.nuclide_[i]; const auto& nuc {*data::nuclides[i_nuc]}; if (multipole_in_range(&nuc, p->E_last_) - && simulation::micro_xs[i_nuc].total) { + && p->neutron_xs_[i_nuc].total) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); cum_dsig += (dsig_s + dsig_a) * material.atom_density_(i); } } - score *= flux_deriv + cum_dsig / simulation::material_xs.total; - } else if (simulation::micro_xs[i_nuclide].total) { + score *= flux_deriv + cum_dsig / p->macro_xs_.total; + } else if (p->neutron_xs_[i_nuclide].total) { const auto& nuc {*data::nuclides[i_nuclide]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv - + (dsig_s + dsig_a) / simulation::micro_xs[i_nuclide].total; + + (dsig_s + dsig_a) / p->neutron_xs_[i_nuclide].total; } else { score *= flux_deriv; } break; case SCORE_SCATTER: - if (i_nuclide == -1 && (simulation::material_xs.total - - simulation::material_xs.absorption)) { + if (i_nuclide == -1 && (p->macro_xs_.total + - p->macro_xs_.absorption)) { double cum_dsig = 0; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto i_nuc = material.nuclide_[i]; const auto& nuc {*data::nuclides[i_nuc]}; if (multipole_in_range(&nuc, p->E_last_) - && (simulation::micro_xs[i_nuc].total - - simulation::micro_xs[i_nuc].absorption)) { + && (p->neutron_xs_[i_nuc].total + - p->neutron_xs_[i_nuc].absorption)) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); cum_dsig += dsig_s * material.atom_density_(i); } } - score *= flux_deriv + cum_dsig / (simulation::material_xs.total - - simulation::material_xs.absorption); - } else if (simulation::micro_xs[i_nuclide].total - - simulation::micro_xs[i_nuclide].absorption) { + score *= flux_deriv + cum_dsig / (p->macro_xs_.total + - p->macro_xs_.absorption); + } else if (p->neutron_xs_[i_nuclide].total + - p->neutron_xs_[i_nuclide].absorption) { const auto& nuc {*data::nuclides[i_nuclide]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); - score *= flux_deriv + dsig_s / (simulation::micro_xs[i_nuclide].total - - simulation::micro_xs[i_nuclide].absorption); + score *= flux_deriv + dsig_s / (p->neutron_xs_[i_nuclide].total + - p->neutron_xs_[i_nuclide].absorption); } else { score *= flux_deriv; } break; case SCORE_ABSORPTION: - if (i_nuclide == -1 && simulation::material_xs.absorption > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.absorption > 0.0) { double cum_dsig = 0; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto i_nuc = material.nuclide_[i]; const auto& nuc {*data::nuclides[i_nuc]}; if (multipole_in_range(&nuc, p->E_last_) - && simulation::micro_xs[i_nuc].absorption) { + && p->neutron_xs_[i_nuc].absorption) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); cum_dsig += dsig_a * material.atom_density_(i); } } - score *= flux_deriv + cum_dsig / simulation::material_xs.absorption; - } else if (simulation::micro_xs[i_nuclide].absorption) { + score *= flux_deriv + cum_dsig / p->macro_xs_.absorption; + } else if (p->neutron_xs_[i_nuclide].absorption) { const auto& nuc {*data::nuclides[i_nuclide]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv - + dsig_a / simulation::micro_xs[i_nuclide].absorption; + + dsig_a / p->neutron_xs_[i_nuclide].absorption; } else { score *= flux_deriv; } break; case SCORE_FISSION: - if (i_nuclide == -1 && simulation::material_xs.fission > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.fission > 0.0) { double cum_dsig = 0; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto i_nuc = material.nuclide_[i]; const auto& nuc {*data::nuclides[i_nuc]}; if (multipole_in_range(&nuc, p->E_last_) - && simulation::micro_xs[i_nuc].fission) { + && p->neutron_xs_[i_nuc].fission) { double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); cum_dsig += dsig_f * material.atom_density_(i); } } - score *= flux_deriv + cum_dsig / simulation::material_xs.fission; - } else if (simulation::micro_xs[i_nuclide].fission) { + score *= flux_deriv + cum_dsig / p->macro_xs_.fission; + } else if (p->neutron_xs_[i_nuclide].fission) { const auto& nuc {*data::nuclides[i_nuclide]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv - + dsig_f / simulation::micro_xs[i_nuclide].fission; + + dsig_f / p->neutron_xs_[i_nuclide].fission; } else { score *= flux_deriv; } break; case SCORE_NU_FISSION: - if (i_nuclide == -1 && simulation::material_xs.nu_fission > 0.0) { + if (i_nuclide == -1 && p->macro_xs_.nu_fission > 0.0) { double cum_dsig = 0; for (auto i = 0; i < material.nuclide_.size(); ++i) { auto i_nuc = material.nuclide_[i]; const auto& nuc {*data::nuclides[i_nuc]}; if (multipole_in_range(&nuc, p->E_last_) - && simulation::micro_xs[i_nuc].fission) { - double nu = simulation::micro_xs[i_nuc].nu_fission - / simulation::micro_xs[i_nuc].fission; + && p->neutron_xs_[i_nuc].fission) { + double nu = p->neutron_xs_[i_nuc].nu_fission + / p->neutron_xs_[i_nuc].fission; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); cum_dsig += nu * dsig_f * material.atom_density_(i); } } - score *= flux_deriv + cum_dsig / simulation::material_xs.nu_fission; - } else if (simulation::micro_xs[i_nuclide].fission) { + score *= flux_deriv + cum_dsig / p->macro_xs_.nu_fission; + } else if (p->neutron_xs_[i_nuclide].fission) { const auto& nuc {*data::nuclides[i_nuclide]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); score *= flux_deriv - + dsig_f / simulation::micro_xs[i_nuclide].fission; + + dsig_f / p->neutron_xs_[i_nuclide].fission; } else { score *= flux_deriv; } @@ -582,7 +582,7 @@ score_track_derivative(const Particle* p, double distance) // phi is proportional to e^(-Sigma_tot * dist) // (1 / phi) * (d_phi / d_rho) = - (d_Sigma_tot / d_rho) * dist // (1 / phi) * (d_phi / d_rho) = - Sigma_tot / rho * dist - deriv.flux_deriv -= distance * simulation::material_xs.total + deriv.flux_deriv -= distance * p->macro_xs_.total / material.density_gpcc_; break; @@ -591,7 +591,7 @@ score_track_derivative(const Particle* p, double distance) // (1 / phi) * (d_phi / d_N) = - (d_Sigma_tot / d_N) * dist // (1 / phi) * (d_phi / d_N) = - sigma_tot * dist deriv.flux_deriv -= distance - * simulation::micro_xs[deriv.diff_nuclide].total; + * p->neutron_xs_[deriv.diff_nuclide].total; break; case DIFF_TEMPERATURE: @@ -660,7 +660,7 @@ void score_collision_derivative(const Particle* p) // phi is proportional to Sigma_s // (1 / phi) * (d_phi / d_T) = (d_Sigma_s / d_T) / Sigma_s // (1 / phi) * (d_phi / d_T) = (d_sigma_s / d_T) / sigma_s - const auto& micro_xs {simulation::micro_xs[i_nuc]}; + const auto& micro_xs {p->neutron_xs_[i_nuc]}; double dsig_s, dsig_a, dsig_f; std::tie(dsig_s, dsig_a, dsig_f) = nuc.multipole_->evaluate_deriv(p->E_last_, p->sqrtkT_); diff --git a/src/tallies/filter_particle.cpp b/src/tallies/filter_particle.cpp index a2902785a..23a305b8d 100644 --- a/src/tallies/filter_particle.cpp +++ b/src/tallies/filter_particle.cpp @@ -50,6 +50,7 @@ ParticleFilter::text_label(int bin) const case Particle::Type::positron: return "Particle: positron"; } + UNREACHABLE(); } } // namespace openmc diff --git a/src/tallies/filter_sph_harm.cpp b/src/tallies/filter_sph_harm.cpp index 9aae06bee..1e97e64c1 100644 --- a/src/tallies/filter_sph_harm.cpp +++ b/src/tallies/filter_sph_harm.cpp @@ -80,9 +80,10 @@ SphericalHarmonicsFilter::text_label(int bin) const if (bin < (n + 1) * (n + 1)) { int m = (bin - n*n) - n; out << "Spherical harmonic expansion, Y" << n << "," << m; - return out.str(); + break; } } + return out.str(); } //============================================================================== diff --git a/src/tallies/filter_zernike.cpp b/src/tallies/filter_zernike.cpp index c4e620c76..9f66a68f9 100644 --- a/src/tallies/filter_zernike.cpp +++ b/src/tallies/filter_zernike.cpp @@ -65,9 +65,10 @@ ZernikeFilter::text_label(int bin) const int first = last - (n + 1); int m = -n + (bin - first) * 2; out << "Zernike expansion, Z" << n << "," << m; - return out.str(); + break; } } + return out.str(); } void diff --git a/src/tallies/tally.cpp b/src/tallies/tally.cpp index 5bae26b75..84ec223ed 100644 --- a/src/tallies/tally.cpp +++ b/src/tallies/tally.cpp @@ -197,6 +197,20 @@ score_str_to_int(std::string score_str) return N_PT; if (score_str == "(n,da)") return N_DA; + if (score_str == "(n,Xp)" || score_str == "H1-production") + return N_XP; + if (score_str == "(n,Xd)" || score_str == "H2-production") + return N_XD; + if (score_str == "(n,Xt)" || score_str == "H3-production") + return N_XT; + if (score_str == "(n,X3He)" || score_str == "He3-production") + return N_X3HE; + if (score_str == "(n,Xa)" || score_str == "He4-production") + return N_XA; + if (score_str == "heating") + return HEATING; + if (score_str == "damage-energy") + return DAMAGE_ENERGY; // So far we have not identified this score string. Check to see if it is a // deprecated score. @@ -314,7 +328,6 @@ Tally::set_scores(std::vector scores) // Iterate over the given scores. for (auto score_str : scores) { // Make sure a delayed group filter wasn't used with an incompatible score. - bool has_delayedgroup = delayedgroup_filter_ != C_NONE; if (delayedgroup_filter_ != C_NONE) { if (score_str != "delayed-nu-fission" && score_str != "decay-rate") fatal_error("Cannot tally " + score_str + "with a delayedgroup filter"); @@ -704,7 +717,7 @@ void read_tallies_xml() const auto& f = model::tally_filters[i_filter].get(); auto pf = dynamic_cast(f); - if (pf) particle_filter_index = j; + if (pf) particle_filter_index = i_filter; // Change the tally estimator if a filter demands it std::string filt_type = f->type(); diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index ed4d09254..8d51aa7f2 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -192,7 +192,7 @@ score_fission_eout(const Particle* p, int i_tally, int i_score, int score_bin) // loop over number of particles banked for (auto i = 0; i < p->n_bank_; ++i) { - auto i_bank = simulation::n_bank - p->n_bank_ + i; + auto i_bank = simulation::fission_bank.size() - p->n_bank_ + i; const auto& bank = simulation::fission_bank[i_bank]; // get the delayed group @@ -259,12 +259,13 @@ score_fission_eout(const Particle* p, int i_tally, int i_score, int score_bin) } else if (score_bin == SCORE_DELAYED_NU_FISSION && g != 0) { - // Get the index of the delayed group filter - auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; - // If the delayed group filter is present, tally to corresponding delayed // group bin if it exists - if (i_dg_filt >= 0) { + if (tally.delayedgroup_filter_ >= 0) { + + // Get the index of the delayed group filter + auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; + const DelayedGroupFilter& dg_filt {*dynamic_cast( model::tally_filters[i_dg_filt].get())}; @@ -317,7 +318,7 @@ score_fission_eout(const Particle* p, int i_tally, int i_score, int score_bin) //! is not used for analog tallies. void -score_general_ce(const Particle* p, int i_tally, int start_index, +score_general_ce(Particle* p, int i_tally, int start_index, int filter_index, int i_nuclide, double atom_density, double flux) { auto& tally {*model::tallies[i_tally]}; @@ -349,7 +350,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, if (p->type_ == Particle::Type::neutron || p->type_ == Particle::Type::photon) { - score *= flux / simulation::material_xs.total; + score *= flux / p->macro_xs_.total; } else { score = 0.; } @@ -373,9 +374,9 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } else { if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].total * atom_density * flux; + score = p->neutron_xs_[i_nuclide].total * atom_density * flux; } else { - score = simulation::material_xs.total * flux; + score = p->macro_xs_.total * flux; } } break; @@ -393,7 +394,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } else { score = p->wgt_last_; } - score *= flux / simulation::material_xs.total; + score *= flux / p->macro_xs_.total; } else { score = flux; } @@ -411,11 +412,11 @@ score_general_ce(const Particle* p, int i_tally, int start_index, score = p->wgt_last_ * flux; } else { if (i_nuclide >= 0) { - score = (simulation::micro_xs[i_nuclide].total - - simulation::micro_xs[i_nuclide].absorption) * atom_density * flux; + score = (p->neutron_xs_[i_nuclide].total + - p->neutron_xs_[i_nuclide].absorption) * atom_density * flux; } else { - score = (simulation::material_xs.total - - simulation::material_xs.absorption) * flux; + score = (p->macro_xs_.total + - p->macro_xs_.absorption) * flux; } } break; @@ -458,26 +459,26 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } } else { if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].absorption * atom_density + score = p->neutron_xs_[i_nuclide].absorption * atom_density * flux; } else { - score = simulation::material_xs.absorption * flux; + score = p->macro_xs_.absorption * flux; } } break; case SCORE_FISSION: - if (simulation::material_xs.absorption == 0) continue; + if (p->macro_xs_.absorption == 0) continue; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing) { // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // fission - if (simulation::micro_xs[p->event_nuclide_].absorption > 0) { + if (p->neutron_xs_[p->event_nuclide_].absorption > 0) { score = p->wgt_absorb_ - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } else { score = 0.; } @@ -488,21 +489,21 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // weight entering the collision as the estimate for the fission // reaction rate score = p->wgt_last_ - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } else { if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].fission * atom_density * flux; + score = p->neutron_xs_[i_nuclide].fission * atom_density * flux; } else { - score = simulation::material_xs.fission * flux; + score = p->macro_xs_.fission * flux; } } break; case SCORE_NU_FISSION: - if (simulation::material_xs.absorption == 0) continue; + if (p->macro_xs_.absorption == 0) continue; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing || p->fission_) { if (tally.energyout_filter_ != C_NONE) { @@ -516,10 +517,10 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // nu-fission - if (simulation::micro_xs[p->event_nuclide_].absorption > 0) { + if (p->neutron_xs_[p->event_nuclide_].absorption > 0) { score = p->wgt_absorb_ - * simulation::micro_xs[p->event_nuclide_].nu_fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].nu_fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } else { score = 0.; } @@ -535,17 +536,17 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } } else { if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].nu_fission * atom_density + score = p->neutron_xs_[i_nuclide].nu_fission * atom_density * flux; } else { - score = simulation::material_xs.nu_fission * flux; + score = p->macro_xs_.nu_fission * flux; } } break; case SCORE_PROMPT_NU_FISSION: - if (simulation::material_xs.absorption == 0) continue; + if (p->macro_xs_.absorption == 0) continue; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing || p->fission_) { if (tally.energyout_filter_ != C_NONE) { @@ -559,12 +560,12 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // prompt-nu-fission - if (simulation::micro_xs[p->event_nuclide_].absorption > 0) { + if (p->neutron_xs_[p->event_nuclide_].absorption > 0) { score = p->wgt_absorb_ - * simulation::micro_xs[p->event_nuclide_].fission + * p->neutron_xs_[p->event_nuclide_].fission * data::nuclides[p->event_nuclide_] ->nu(E, ReactionProduct::EmissionMode::prompt) - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } else { score = 0.; } @@ -583,7 +584,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } } else { if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].fission + score = p->neutron_xs_[i_nuclide].fission * data::nuclides[i_nuclide] ->nu(E, ReactionProduct::EmissionMode::prompt) * atom_density * flux; @@ -595,7 +596,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; auto atom_density = material.atom_density_(i); - score += simulation::micro_xs[j_nuclide].fission + score += p->neutron_xs_[j_nuclide].fission * data::nuclides[j_nuclide] ->nu(E, ReactionProduct::EmissionMode::prompt) * atom_density * flux; @@ -607,7 +608,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, case SCORE_DELAYED_NU_FISSION: - if (simulation::material_xs.absorption == 0) continue; + if (p->macro_xs_.absorption == 0) continue; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing || p->fission_) { if (tally.energyout_filter_ != C_NONE) { @@ -621,7 +622,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // delayed-nu-fission - if (simulation::micro_xs[p->event_nuclide_].absorption > 0 + if (p->neutron_xs_[p->event_nuclide_].absorption > 0 && data::nuclides[p->event_nuclide_]->fissionable_) { if (tally.delayedgroup_filter_ != C_NONE) { auto i_dg_filt = tally.filters()[tally.delayedgroup_filter_]; @@ -634,8 +635,8 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto yield = data::nuclides[p->event_nuclide_] ->nu(E, ReactionProduct::EmissionMode::delayed, d); score = p->wgt_absorb_ * yield - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; score_fission_delayed_dg(i_tally, d_bin, score, score_index); } @@ -645,10 +646,10 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // by multiplying the absorbed weight by the fraction of the // delayed-nu-fission xs to the absorption xs score = p->wgt_absorb_ - * simulation::micro_xs[p->event_nuclide_].fission + * p->neutron_xs_[p->event_nuclide_].fission * data::nuclides[p->event_nuclide_] ->nu(E, ReactionProduct::EmissionMode::delayed) - / simulation::micro_xs[p->event_nuclide_].absorption *flux; + / p->neutron_xs_[p->event_nuclide_].absorption *flux; } } } else { @@ -694,7 +695,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto d = filt.groups_[d_bin]; auto yield = data::nuclides[i_nuclide] ->nu(E, ReactionProduct::EmissionMode::delayed, d); - score = simulation::micro_xs[i_nuclide].fission * yield + score = p->neutron_xs_[i_nuclide].fission * yield * atom_density * flux; score_fission_delayed_dg(i_tally, d_bin, score, score_index); } @@ -702,7 +703,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, } else { // If the delayed group filter is not present, compute the score // by multiplying the delayed-nu-fission macro xs by the flux - score = simulation::micro_xs[i_nuclide].fission + score = p->neutron_xs_[i_nuclide].fission * data::nuclides[i_nuclide] ->nu(E, ReactionProduct::EmissionMode::delayed) * atom_density * flux; @@ -723,7 +724,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto d = filt.groups_[d_bin]; auto yield = data::nuclides[j_nuclide] ->nu(E, ReactionProduct::EmissionMode::delayed, d); - score = simulation::micro_xs[j_nuclide].fission * yield + score = p->neutron_xs_[j_nuclide].fission * yield * atom_density * flux; score_fission_delayed_dg(i_tally, d_bin, score, score_index); @@ -738,7 +739,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; auto atom_density = material.atom_density_(i); - score += simulation::micro_xs[j_nuclide].fission + score += p->neutron_xs_[j_nuclide].fission * data::nuclides[j_nuclide] ->nu(E, ReactionProduct::EmissionMode::delayed) * atom_density * flux; @@ -751,14 +752,14 @@ score_general_ce(const Particle* p, int i_tally, int start_index, case SCORE_DECAY_RATE: - if (simulation::material_xs.absorption == 0) continue; + if (p->macro_xs_.absorption == 0) continue; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing) { // No fission events occur if survival biasing is on -- need to // calculate fraction of absorptions that would have resulted in // delayed-nu-fission const auto& nuc {*data::nuclides[p->event_nuclide_]}; - if (simulation::micro_xs[p->event_nuclide_].absorption > 0 + if (p->neutron_xs_[p->event_nuclide_].absorption > 0 && nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; if (tally.delayedgroup_filter_ != C_NONE) { @@ -773,8 +774,8 @@ score_general_ce(const Particle* p, int i_tally, int start_index, = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); auto rate = rxn.products_[d].decay_rate_; score = p->wgt_absorb_ * yield - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * rate * flux; score_fission_delayed_dg(i_tally, d_bin, score, score_index); @@ -796,8 +797,8 @@ score_general_ce(const Particle* p, int i_tally, int start_index, = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d+1); auto rate = rxn.products_[d+1].decay_rate_; score += rate * p->wgt_absorb_ - * simulation::micro_xs[p->event_nuclide_].fission * yield - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission * yield + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } } @@ -813,7 +814,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // contribution to the fission bank to the score. score = 0.; for (auto i = 0; i < p->n_bank_; ++i) { - auto i_bank = simulation::n_bank - p->n_bank_ + i; + auto i_bank = simulation::fission_bank.size() - p->n_bank_ + i; const auto& bank = simulation::fission_bank[i_bank]; auto g = bank.delayed_group; if (g != 0) { @@ -854,7 +855,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); auto rate = rxn.products_[d].decay_rate_; - score = simulation::micro_xs[i_nuclide].fission * yield * flux + score = p->neutron_xs_[i_nuclide].fission * yield * flux * atom_density * rate; score_fission_delayed_dg(i_tally, d_bin, score, score_index); } @@ -870,7 +871,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d+1); auto rate = rxn.products_[d+1].decay_rate_; - score += simulation::micro_xs[i_nuclide].fission * flux + score += p->neutron_xs_[i_nuclide].fission * flux * yield * atom_density * rate; } } @@ -894,7 +895,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d); auto rate = rxn.products_[d].decay_rate_; - score = simulation::micro_xs[j_nuclide].fission * yield + score = p->neutron_xs_[j_nuclide].fission * yield * flux * atom_density * rate; score_fission_delayed_dg(i_tally, d_bin, score, score_index); @@ -922,7 +923,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto yield = nuc.nu(E, ReactionProduct::EmissionMode::delayed, d+1); auto rate = rxn.products_[d+1].decay_rate_; - score += simulation::micro_xs[j_nuclide].fission + score += p->neutron_xs_[j_nuclide].fission * yield * atom_density * flux * rate; } } @@ -935,7 +936,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, case SCORE_KAPPA_FISSION: - if (simulation::material_xs.absorption == 0.) continue; + if (p->macro_xs_.absorption == 0.) continue; score = 0.; // Kappa-fission values are determined from the Q-value listed for the // fission cross section. @@ -945,12 +946,12 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // calculate fraction of absorptions that would have resulted in // fission scaled by the Q-value const auto& nuc {*data::nuclides[p->event_nuclide_]}; - if (simulation::micro_xs[p->event_nuclide_].absorption > 0 + if (p->neutron_xs_[p->event_nuclide_].absorption > 0 && nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; score = p->wgt_absorb_ * rxn.q_value_ - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } else { // Skip any non-absorption events @@ -959,12 +960,12 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // weight entering the collision as the estimate for the fission // reaction rate const auto& nuc {*data::nuclides[p->event_nuclide_]}; - if (simulation::micro_xs[p->event_nuclide_].absorption > 0 + if (p->neutron_xs_[p->event_nuclide_].absorption > 0 && nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; score = p->wgt_last_ * rxn.q_value_ - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } } else { @@ -972,7 +973,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, const auto& nuc {*data::nuclides[i_nuclide]}; if (nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; - score = rxn.q_value_ * simulation::micro_xs[i_nuclide].fission + score = rxn.q_value_ * p->neutron_xs_[i_nuclide].fission * atom_density * flux; } } else { @@ -984,7 +985,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, const auto& nuc {*data::nuclides[j_nuclide]}; if (nuc.fissionable_) { const auto& rxn {*nuc.fission_rx_[0]}; - score += rxn.q_value_ * simulation::micro_xs[j_nuclide].fission + score += rxn.q_value_ * p->neutron_xs_[j_nuclide].fission * atom_density * flux; } } @@ -1007,9 +1008,9 @@ score_general_ce(const Particle* p, int i_tally, int start_index, score = p->wgt_last_ * flux; } else { if (i_nuclide >= 0) { - if (simulation::micro_xs[i_nuclide].elastic == CACHE_INVALID) - data::nuclides[i_nuclide]->calculate_elastic_xs(); - score = simulation::micro_xs[i_nuclide].elastic * atom_density * flux; + if (p->neutron_xs_[i_nuclide].elastic == CACHE_INVALID) + data::nuclides[i_nuclide]->calculate_elastic_xs(*p); + score = p->neutron_xs_[i_nuclide].elastic * atom_density * flux; } else { score = 0.; if (p->material_ != MATERIAL_VOID) { @@ -1017,9 +1018,9 @@ score_general_ce(const Particle* p, int i_tally, int start_index, for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; auto atom_density = material.atom_density_(i); - if (simulation::micro_xs[j_nuclide].elastic == CACHE_INVALID) - data::nuclides[j_nuclide]->calculate_elastic_xs(); - score += simulation::micro_xs[j_nuclide].elastic * atom_density + if (p->neutron_xs_[j_nuclide].elastic == CACHE_INVALID) + data::nuclides[j_nuclide]->calculate_elastic_xs(*p); + score += p->neutron_xs_[j_nuclide].elastic * atom_density * flux; } } @@ -1030,7 +1031,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, case SCORE_FISS_Q_PROMPT: case SCORE_FISS_Q_RECOV: //continue; - if (simulation::material_xs.absorption == 0.) continue; + if (p->macro_xs_.absorption == 0.) continue; score = 0.; if (tally.estimator_ == ESTIMATOR_ANALOG) { if (settings::survival_biasing) { @@ -1038,7 +1039,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // calculate fraction of absorptions that would have resulted in // fission scaled by the Q-value const auto& nuc {*data::nuclides[p->event_nuclide_]}; - if (simulation::micro_xs[p->event_nuclide_].absorption > 0) { + if (p->neutron_xs_[p->event_nuclide_].absorption > 0) { double q_value = 0.; if (score_bin == SCORE_FISS_Q_PROMPT) { if (nuc.fission_q_prompt_) @@ -1048,8 +1049,8 @@ score_general_ce(const Particle* p, int i_tally, int start_index, q_value = (*nuc.fission_q_recov_)(p->E_last_); } score = p->wgt_absorb_ * q_value - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } else { // Skip any non-absorption events @@ -1058,7 +1059,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, // weight entering the collision as the estimate for the fission // reaction rate const auto& nuc {*data::nuclides[p->event_nuclide_]}; - if (simulation::micro_xs[p->event_nuclide_].absorption > 0) { + if (p->neutron_xs_[p->event_nuclide_].absorption > 0) { double q_value = 0.; if (score_bin == SCORE_FISS_Q_PROMPT) { if (nuc.fission_q_prompt_) @@ -1068,8 +1069,8 @@ score_general_ce(const Particle* p, int i_tally, int start_index, q_value = (*nuc.fission_q_recov_)(p->E_last_); } score = p->wgt_last_ * q_value - * simulation::micro_xs[p->event_nuclide_].fission - / simulation::micro_xs[p->event_nuclide_].absorption * flux; + * p->neutron_xs_[p->event_nuclide_].fission + / p->neutron_xs_[p->event_nuclide_].absorption * flux; } } } else { @@ -1083,7 +1084,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, if (nuc.fission_q_recov_) q_value = (*nuc.fission_q_recov_)(p->E_last_); } - score = q_value * simulation::micro_xs[i_nuclide].fission + score = q_value * p->neutron_xs_[i_nuclide].fission * atom_density * flux; } else { if (p->material_ != MATERIAL_VOID) { @@ -1100,7 +1101,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, if (nuc.fission_q_recov_) q_value = (*nuc.fission_q_recov_)(p->E_last_); } - score += q_value * simulation::micro_xs[j_nuclide].fission + score += q_value * p->neutron_xs_[j_nuclide].fission * atom_density * flux; } } @@ -1130,7 +1131,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, case N_4N: m = 5; break; } if (i_nuclide >= 0) { - score = simulation::micro_xs[i_nuclide].reaction[m] * atom_density + score = p->neutron_xs_[i_nuclide].reaction[m] * atom_density * flux; } else { score = 0.; @@ -1139,7 +1140,7 @@ score_general_ce(const Particle* p, int i_tally, int start_index, for (auto i = 0; i < material.nuclide_.size(); ++i) { auto j_nuclide = material.nuclide_[i]; auto atom_density = material.atom_density_(i); - score += simulation::micro_xs[j_nuclide].reaction[m] + score += p->neutron_xs_[j_nuclide].reaction[m] * atom_density * flux; } } @@ -1164,10 +1165,10 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto m = nuc.reaction_index_[score_bin]; if (m == C_NONE) continue; const auto& rxn {*nuc.reactions_[m]}; - auto i_temp = simulation::micro_xs[i_nuclide].index_temp; + auto i_temp = p->neutron_xs_[i_nuclide].index_temp; if (i_temp >= 0) { // Can be false due to multipole - auto i_grid = simulation::micro_xs[i_nuclide].index_grid; - auto f = simulation::micro_xs[i_nuclide].interp_factor; + auto i_grid = p->neutron_xs_[i_nuclide].index_grid; + auto f = p->neutron_xs_[i_nuclide].interp_factor; const auto& xs {rxn.xs_[i_temp]}; if (i_grid >= xs.threshold) { score = ((1.0 - f) * xs.value[i_grid-xs.threshold] @@ -1184,10 +1185,10 @@ score_general_ce(const Particle* p, int i_tally, int start_index, auto m = nuc.reaction_index_[score_bin]; if (m == C_NONE) continue; const auto& rxn {*nuc.reactions_[m]}; - auto i_temp = simulation::micro_xs[j_nuclide].index_temp; + auto i_temp = p->neutron_xs_[j_nuclide].index_temp; if (i_temp >= 0) { // Can be false due to multipole - auto i_grid = simulation::micro_xs[j_nuclide].index_grid; - auto f = simulation::micro_xs[j_nuclide].interp_factor; + auto i_grid = p->neutron_xs_[j_nuclide].index_grid; + auto f = p->neutron_xs_[j_nuclide].interp_factor; const auto& xs {rxn.xs_[i_temp]}; if (i_grid >= xs.threshold) { score += ((1.0 - f) * xs.value[i_grid-xs.threshold] @@ -1291,7 +1292,7 @@ score_general_mg(const Particle* p, int i_tally, int start_index, } else { score = p->wgt_last_; } - score *= flux / simulation::material_xs.total; + score *= flux / p->macro_xs_.total; } else { score = flux; } @@ -1320,7 +1321,7 @@ score_general_mg(const Particle* p, int i_tally, int start_index, score = get_nuclide_xs(i_nuclide, MG_GET_XS_TOTAL, p_g) * atom_density * flux; } else { - score = simulation::material_xs.total * flux; + score = p->macro_xs_.total * flux; } } break; @@ -1438,7 +1439,7 @@ score_general_mg(const Particle* p, int i_tally, int start_index, score = atom_density * flux * get_nuclide_xs(i_nuclide, MG_GET_XS_ABSORPTION, p_g); } else { - score = simulation::material_xs.absorption * flux; + score = p->macro_xs_.absorption * flux; } } break; @@ -1785,7 +1786,7 @@ score_general_mg(const Particle* p, int i_tally, int start_index, // contribution to the fission bank to the score. score = 0.; for (auto i = 0; i < p->n_bank_; ++i) { - auto i_bank = simulation::n_bank - p->n_bank_ + i; + auto i_bank = simulation::fission_bank.size() - p->n_bank_ + i; const auto& bank = simulation::fission_bank[i_bank]; auto g = bank.delayed_group; if (g != 0) { @@ -1920,7 +1921,7 @@ score_general_mg(const Particle* p, int i_tally, int start_index, //! Tally rates for when the user requests a tally on all nuclides. void -score_all_nuclides(const Particle* p, int i_tally, double flux, +score_all_nuclides(Particle* p, int i_tally, double flux, int filter_index) { const Tally& tally {*model::tallies[i_tally]}; @@ -1955,7 +1956,7 @@ score_all_nuclides(const Particle* p, int i_tally, double flux, } } -void score_analog_tally_ce(const Particle* p) +void score_analog_tally_ce(Particle* p) { for (auto i_tally : model::active_analog_tallies) { const Tally& tally {*model::tallies[i_tally]}; @@ -2059,7 +2060,7 @@ void score_analog_tally_mg(const Particle* p) } void -score_tracklength_tally(const Particle* p, double distance) +score_tracklength_tally(Particle* p, double distance) { // Determine the tracklength estimate of the flux double flux = p->wgt_ * distance; @@ -2122,14 +2123,14 @@ score_tracklength_tally(const Particle* p, double distance) match.bins_present_ = false; } -void score_collision_tally(const Particle* p) +void score_collision_tally(Particle* p) { // Determine the collision estimate of the flux double flux; if (!settings::survival_biasing) { - flux = p->wgt_last_ / simulation::material_xs.total; + flux = p->wgt_last_ / p->macro_xs_.total; } else { - flux = (p->wgt_last_ + p->wgt_absorb_) / simulation::material_xs.total; + flux = (p->wgt_last_ + p->wgt_absorb_) / p->macro_xs_.total; } for (auto i_tally : model::active_collision_tallies) { diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 2f7f36920..d43be8b9c 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -83,6 +83,9 @@ check_tally_triggers(double& ratio, int& tally_id, int& score) case TriggerMetric::relative_error: uncertainty = rel_err; break; + case TriggerMetric::not_active: + uncertainty = 0.0; + break; } // Compute the uncertainty / threshold ratio. @@ -109,10 +112,9 @@ double check_keff_trigger() { if (settings::run_mode != RUN_MODE_EIGENVALUE) return 0.; - if (settings::keff_trigger.metric == TriggerMetric::not_active) return 0.; double k_combined[2]; - int err = openmc_get_keff(k_combined); + openmc_get_keff(k_combined); double uncertainty = 0.; switch (settings::keff_trigger.metric) { @@ -124,6 +126,9 @@ check_keff_trigger() break; case TriggerMetric::relative_error: uncertainty = k_combined[1] / k_combined[0]; + break; + case TriggerMetric::not_active: + break; } double ratio = uncertainty / settings::keff_trigger.threshold; diff --git a/tests/regression_tests/dagmc/dagmc.h5m b/tests/regression_tests/dagmc/dagmc.h5m index a55e061dc..c90b6d674 100644 Binary files a/tests/regression_tests/dagmc/dagmc.h5m and b/tests/regression_tests/dagmc/dagmc.h5m differ diff --git a/tests/regression_tests/dagmc/inputs_true.dat b/tests/regression_tests/dagmc/inputs_true.dat index df8e3267e..8ca49c324 100644 --- a/tests/regression_tests/dagmc/inputs_true.dat +++ b/tests/regression_tests/dagmc/inputs_true.dat @@ -1,10 +1,10 @@ - + - + diff --git a/tests/regression_tests/dagmc/test.py b/tests/regression_tests/dagmc/test.py index 6c9be63bf..b6f2f55e2 100644 --- a/tests/regression_tests/dagmc/test.py +++ b/tests/regression_tests/dagmc/test.py @@ -30,12 +30,12 @@ def test_dagmc(): model.tallies = [tally] # materials - u235 = openmc.Material() + u235 = openmc.Material(name="fuel") u235.add_nuclide('U235', 1.0, 'ao') u235.set_density('g/cc', 11) u235.id = 40 - water = openmc.Material() + water = openmc.Material(name="water") water.add_nuclide('H1', 2.0, 'ao') water.add_nuclide('O16', 1.0, 'ao') water.set_density('g/cc', 1.0) diff --git a/tests/regression_tests/deplete/example_geometry.py b/tests/regression_tests/deplete/example_geometry.py index e54c087f6..d4fdf585d 100644 --- a/tests/regression_tests/deplete/example_geometry.py +++ b/tests/regression_tests/deplete/example_geometry.py @@ -182,14 +182,14 @@ def segment_pin(n_rings, n_wedges, r_fuel, r_gap, r_clad): theta = np.linspace(0, 2*math.pi, n_wedges + 1) # Compute surfaces - fuel_rings = [openmc.ZCylinder(x0=0, y0=0, R=r_rings[i]) + fuel_rings = [openmc.ZCylinder(x0=0, y0=0, r=r_rings[i]) for i in range(n_rings)] - fuel_wedges = [openmc.Plane(A=math.cos(theta[i]), B=math.sin(theta[i])) + fuel_wedges = [openmc.Plane(a=math.cos(theta[i]), b=math.sin(theta[i])) for i in range(n_wedges)] - gap_ring = openmc.ZCylinder(x0=0, y0=0, R=r_gap) - clad_ring = openmc.ZCylinder(x0=0, y0=0, R=r_clad) + gap_ring = openmc.ZCylinder(x0=0, y0=0, r=r_gap) + clad_ring = openmc.ZCylinder(x0=0, y0=0, r=r_clad) # Create cells fuel_cells = [] diff --git a/tests/regression_tests/distribmat/test.py b/tests/regression_tests/distribmat/test.py index cb598b9d3..33f64ba03 100644 --- a/tests/regression_tests/distribmat/test.py +++ b/tests/regression_tests/distribmat/test.py @@ -32,7 +32,7 @@ class DistribmatTestHarness(PyAPITestHarness): c1 = openmc.Cell(cell_id=1, fill=moderator) mod_univ = openmc.Universe(universe_id=1, cells=[c1]) - r0 = openmc.ZCylinder(R=0.3) + r0 = openmc.ZCylinder(r=0.3) c11 = openmc.Cell(cell_id=11, region=-r0) c11.fill = [dense_fuel, None, light_fuel, dense_fuel] c12 = openmc.Cell(cell_id=12, region=+r0, fill=moderator) diff --git a/tests/regression_tests/fixed_source/test.py b/tests/regression_tests/fixed_source/test.py index 7d98de9fc..74908f87c 100644 --- a/tests/regression_tests/fixed_source/test.py +++ b/tests/regression_tests/fixed_source/test.py @@ -37,7 +37,7 @@ def test_fixed_source(): mat.add_nuclide('U238', 0.0001) mat.set_density('g/cc', 7.5) - surf = openmc.Sphere(R=10.0, boundary_type='vacuum') + surf = openmc.Sphere(r=10.0, boundary_type='vacuum') cell = openmc.Cell(fill=mat, region=-surf) model = openmc.model.Model() diff --git a/tests/regression_tests/lattice_hex_coincident/__init__.py b/tests/regression_tests/lattice_hex_coincident/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/lattice_hex_coincident/inputs_true.dat b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat new file mode 100644 index 000000000..8d73f6374 --- /dev/null +++ b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat @@ -0,0 +1,81 @@ + + + + + + + + + + + + 1.4 + 11 +
0.0 0.0
+ + 10 +10 10 + 9 +10 10 + 10 +
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + -0.9899494936611666 -0.9899494936611666 0.0 0.9899494936611666 0.9899494936611666 10.0 + + + + false + + 22 + diff --git a/tests/regression_tests/lattice_hex_coincident/results_true.dat b/tests/regression_tests/lattice_hex_coincident/results_true.dat new file mode 100644 index 000000000..ba047f674 --- /dev/null +++ b/tests/regression_tests/lattice_hex_coincident/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.741370E+00 1.384609E-03 diff --git a/tests/regression_tests/lattice_hex_coincident/test.py b/tests/regression_tests/lattice_hex_coincident/test.py new file mode 100644 index 000000000..65786e2ec --- /dev/null +++ b/tests/regression_tests/lattice_hex_coincident/test.py @@ -0,0 +1,150 @@ +import numpy as np +import openmc + +from tests.testing_harness import PyAPITestHarness + + +class HexLatticeCoincidentTestHarness(PyAPITestHarness): + def _build_inputs(self): + materials = openmc.Materials() + + fuel_mat = openmc.Material() + fuel_mat.add_nuclide('U235', 4.9817E-03, 'ao') + materials.append(fuel_mat) + + matrix = openmc.Material() + matrix.set_density('atom/b-cm', 8.7742E-02) + matrix.add_element('C', 1.0, 'ao') + matrix.add_s_alpha_beta('c_Graphite') + materials.append(matrix) + + lead = openmc.Material(name="Lead") + lead.set_density('g/cm3', 10.32) + lead.add_nuclide('Pb204', 0.014, 'ao') + lead.add_nuclide('Pb206', 0.241, 'ao') + lead.add_nuclide('Pb207', 0.221, 'ao') + lead.add_nuclide('Pb208', 0.524, 'ao') + materials.append(lead) + + coolant = openmc.Material() + coolant.set_density('atom/b-cm', 5.4464E-04) + coolant.add_nuclide('He4', 1.0, 'ao') + materials.append(coolant) + + zirc = openmc.Material(name="Zirc4") + zirc.add_nuclide('Zr90', 2.217E-02, 'ao') + zirc.add_nuclide('Zr91', 4.781E-03, 'ao') + zirc.add_nuclide('Zr92', 7.228E-03, 'ao') + zirc.add_nuclide('Zr94', 7.169E-03, 'ao') + zirc.add_nuclide('Zr96', 1.131E-03, 'ao') + materials.append(zirc) + + materials.export_to_xml() + + ### Geometry ### + pin_rad = 0.7 # cm + assembly_pitch = 1.4 # cm + + cool_rad = 0.293 # cm + zirc_clad_thickness = 0.057 # cm + zirc_ir = cool_rad # cm + zirc_or = cool_rad + zirc_clad_thickness # cm + lead_thickness = 0.002 # cm + lead_ir = zirc_or # cm + lead_or = zirc_or + lead_thickness # cm + + cyl = openmc.ZCylinder(x0=0., y0=0., r=pin_rad) + fuel_btm = openmc.ZPlane(z0=0.0, boundary_type = 'reflective') + fuel_top = openmc.ZPlane(z0=10.0, boundary_type = 'reflective') + region = -cyl & +fuel_btm & -fuel_top + + container = openmc.Cell(region=region) + container.fill = fuel_mat + + fuel_outside = openmc.Cell() + fuel_outside.region = +cyl + fuel_outside.fill = matrix + + fuel_ch_univ = openmc.Universe(cells=[container, fuel_outside]) + + # Coolant Channel + cool_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=cool_rad) + zirc_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=zirc_or) + lead_outer = openmc.ZCylinder(x0=0.0, y0=0.0, r=lead_or) + + coolant_ch = openmc.Cell(name="coolant") + coolant_ch.region = -cool_outer & +fuel_btm & -fuel_top + coolant_ch.fill = coolant + + zirc_shell = openmc.Cell(name="zirconium_shell") + zirc_shell.region = +cool_outer & -zirc_outer & +fuel_btm & -fuel_top + zirc_shell.fill = zirc + + lead_shell = openmc.Cell(name="lead_shell") + lead_shell.region = +zirc_outer & -lead_outer & +fuel_btm & -fuel_top + lead_shell.fill = lead + + coolant_matrix = openmc.Cell(name="matrix coolant surround") + coolant_matrix.region = +lead_outer & +fuel_btm & -fuel_top + coolant_matrix.fill = matrix + + coolant_channel = [coolant_ch, zirc_shell, lead_shell, coolant_matrix] + + coolant_univ = openmc.Universe(name="coolant universe") + coolant_univ.add_cells(coolant_channel) + + half_width = assembly_pitch # cm + edge_length = (2./np.sqrt(3.0)) * half_width + + inf_mat = openmc.Cell() + inf_mat.fill = matrix + + inf_mat_univ = openmc.Universe(cells=[inf_mat,]) + + # a hex surface for the core to go inside of + hexprism = openmc.model.get_hexagonal_prism(edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type = 'reflective', + orientation='x') + + pincell_only_lattice = openmc.HexLattice(name="regular fuel assembly") + pincell_only_lattice.center = (0., 0.) + pincell_only_lattice.pitch = (assembly_pitch,) + pincell_only_lattice.outer = inf_mat_univ + + # setup hex rings + ring0 = [fuel_ch_univ] + ring1 = [coolant_univ] * 6 + pincell_only_lattice.universes = [ring1, ring0] + + pincell_only_cell = openmc.Cell(name="container cell") + pincell_only_cell.region = hexprism & +fuel_btm & -fuel_top + pincell_only_cell.fill = pincell_only_lattice + + root_univ = openmc.Universe(name="root universe", cells=[pincell_only_cell,]) + + geom = openmc.Geometry(root_univ) + geom.export_to_xml() + + ### Settings ### + + settings = openmc.Settings() + settings.run_mode = 'eigenvalue' + + source = openmc.Source() + corner_dist = np.sqrt(2) * pin_rad + ll = [-corner_dist, -corner_dist, 0.0] + ur = [corner_dist, corner_dist, 10.0] + source.space = openmc.stats.Box(ll, ur) + source.strength = 1.0 + settings.source = source + settings.output = {'summary' : False} + settings.batches = 10 + settings.inactive = 5 + settings.particles = 1000 + settings.seed = 22 + settings.export_to_xml() + +def test_lattice_hex_coincident_surf(): + harness = HexLatticeCoincidentTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/multipole/test.py b/tests/regression_tests/multipole/test.py index c826ff0d7..4c4353c84 100644 --- a/tests/regression_tests/multipole/test.py +++ b/tests/regression_tests/multipole/test.py @@ -27,7 +27,7 @@ def make_model(): c1 = openmc.Cell(cell_id=1, fill=moderator) mod_univ = openmc.Universe(universe_id=1, cells=(c1,)) - r0 = openmc.ZCylinder(R=0.3) + r0 = openmc.ZCylinder(r=0.3) c11 = openmc.Cell(cell_id=11, fill=dense_fuel, region=-r0) c11.temperature = [500, 700, 0, 800] c12 = openmc.Cell(cell_id=12, fill=moderator, region=+r0) diff --git a/tests/regression_tests/periodic/test.py b/tests/regression_tests/periodic/test.py index d746ebdd5..5df935b22 100644 --- a/tests/regression_tests/periodic/test.py +++ b/tests/regression_tests/periodic/test.py @@ -30,7 +30,7 @@ class PeriodicTest(PyAPITestHarness): 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_cyl = openmc.ZCylinder(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/inputs_true.dat b/tests/regression_tests/photon_production/inputs_true.dat index 32e3bdcf3..1d43c8913 100644 --- a/tests/regression_tests/photon_production/inputs_true.dat +++ b/tests/regression_tests/photon_production/inputs_true.dat @@ -3,7 +3,7 @@ - + @@ -26,7 +26,7 @@ - 14.0 1.0 + 14000000.0 1.0 ttb @@ -47,4 +47,19 @@ 1 2 current + + 2 + total + tracklength + + + 2 + total + collision + + + 2 + total + analog + diff --git a/tests/regression_tests/photon_production/results_true.dat b/tests/regression_tests/photon_production/results_true.dat index 6f53e2d33..7df7e5dbc 100644 --- a/tests/regression_tests/photon_production/results_true.dat +++ b/tests/regression_tests/photon_production/results_true.dat @@ -1,3 +1,12 @@ tally 1: -sum = 1.550000E-02 -sum_sq = 2.402500E-04 +9.403000E-01 +8.841641E-01 +tally 2: +8.281718E-01 +6.858685E-01 +tally 3: +8.242000E-01 +6.793056E-01 +tally 4: +8.242000E-01 +6.793056E-01 diff --git a/tests/regression_tests/photon_production/test.py b/tests/regression_tests/photon_production/test.py index 68992c867..47215fff3 100644 --- a/tests/regression_tests/photon_production/test.py +++ b/tests/regression_tests/photon_production/test.py @@ -14,7 +14,7 @@ class SourceTestHarness(PyAPITestHarness): materials = openmc.Materials([mat]) materials.export_to_xml() - cyl = openmc.XCylinder(boundary_type='vacuum', R=1.0e-6) + 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) @@ -33,7 +33,7 @@ class SourceTestHarness(PyAPITestHarness): source = openmc.Source() source.space = openmc.stats.Point((0,0,0)) source.angle = openmc.stats.Monodirectional() - source.energy = openmc.stats.Discrete([14.0], [1.0]) + source.energy = openmc.stats.Discrete([14.0e6], [1.0]) source.particle = 'neutron' settings = openmc.Settings() @@ -48,20 +48,37 @@ class SourceTestHarness(PyAPITestHarness): surface_filter = openmc.SurfaceFilter(cyl) particle_filter = openmc.ParticleFilter('photon') - tally = openmc.Tally() - tally.filters = [surface_filter, particle_filter] - tally.scores = ['current'] - tallies = openmc.Tallies([tally]) + current_tally = openmc.Tally() + current_tally.filters = [surface_filter, particle_filter] + current_tally.scores = ['current'] + total_tally_tracklength = openmc.Tally() + total_tally_tracklength.filters = [particle_filter] + total_tally_tracklength.scores = ['total'] + total_tally_tracklength.estimator = 'tracklength' + total_tally_collision = openmc.Tally() + total_tally_collision.filters = [particle_filter] + total_tally_collision.scores = ['total'] + total_tally_collision.estimator = 'collision' + total_tally_analog = openmc.Tally() + total_tally_analog.filters = [particle_filter] + total_tally_analog.scores = ['total'] + total_tally_analog.estimator = 'analog' + tallies = openmc.Tallies([current_tally, total_tally_tracklength, + total_tally_collision, total_tally_analog]) tallies.export_to_xml() def _get_results(self): with openmc.StatePoint(self._sp_name) as sp: outstr = '' - t = sp.get_tally() - outstr += 'tally {}:\n'.format(t.id) - outstr += 'sum = {:12.6E}\n'.format(t.sum[0, 0, 0]) - outstr += 'sum_sq = {:12.6E}\n'.format(t.sum_sq[0, 0, 0]) + for i, tally_ind in enumerate(sp.tallies): + tally = sp.tallies[tally_ind] + results = np.zeros((tally.sum.size * 2, )) + results[0::2] = tally.sum.ravel() + results[1::2] = tally.sum_sq.ravel() + results = ['{0:12.6E}'.format(x) for x in results] + outstr += 'tally {}:\n'.format(i + 1) + outstr += '\n'.join(results) + '\n' return outstr diff --git a/tests/regression_tests/photon_source/results_true.dat b/tests/regression_tests/photon_source/results_true.dat index 609f887bd..1448ad6db 100644 --- a/tests/regression_tests/photon_source/results_true.dat +++ b/tests/regression_tests/photon_source/results_true.dat @@ -1,3 +1,3 @@ tally 1: -sum = 2.254985E+02 -sum_sq = 5.084955E+04 +sum = 2.275713E+02 +sum_sq = 5.178870E+04 diff --git a/tests/regression_tests/photon_source/test.py b/tests/regression_tests/photon_source/test.py index 61367d375..94b028003 100644 --- a/tests/regression_tests/photon_source/test.py +++ b/tests/regression_tests/photon_source/test.py @@ -15,19 +15,19 @@ class SourceTestHarness(PyAPITestHarness): materials = openmc.Materials([mat]) materials.export_to_xml() - sphere = openmc.Sphere(R=1.0e9, boundary_type='reflective') + sphere = openmc.Sphere(r=1.0e9, boundary_type='reflective') inside_sphere = openmc.Cell() inside_sphere.region = -sphere inside_sphere.fill = mat geometry = openmc.Geometry([inside_sphere]) geometry.export_to_xml() - + source = openmc.Source() source.space = openmc.stats.Point((0, 0, 0)) source.angle = openmc.stats.Isotropic() source.energy = openmc.stats.Discrete([10.0e6], [1.0]) source.particle = 'photon' - + settings = openmc.Settings() settings.particles = 10000 settings.batches = 1 @@ -37,7 +37,7 @@ class SourceTestHarness(PyAPITestHarness): settings.run_mode = 'fixed source' settings.source = source settings.export_to_xml() - + particle_filter = openmc.ParticleFilter('photon') tally = openmc.Tally() tally.filters = [particle_filter] diff --git a/tests/regression_tests/plot/results_true.dat b/tests/regression_tests/plot/results_true.dat index 7f8fcc6a8..18251e8d0 100644 --- a/tests/regression_tests/plot/results_true.dat +++ b/tests/regression_tests/plot/results_true.dat @@ -1 +1 @@ -30f435795c755b6fb28565a33c68e95415e5c09bca59f5549a6cbd2dd165bac9d90f5a599e12a5041cb927698abde562b7bac14175441c57b5c0dccd00544506 \ No newline at end of file +6b3eb36488b995b42233e6b7c0164cf6c92a1823b3c91985b97fd076f86c0aad93fbdb74e70026f5f12c61a6aee52a09588171a7fd839511ee7d9efc3952beb2 \ No newline at end of file diff --git a/tests/regression_tests/plot_voxel/results_true.dat b/tests/regression_tests/plot_voxel/results_true.dat index 801621784..52222000f 100644 --- a/tests/regression_tests/plot_voxel/results_true.dat +++ b/tests/regression_tests/plot_voxel/results_true.dat @@ -1 +1 @@ -76274390783bf0fd54a63e3ee141e072e797a564935a16f22a97788bc051e33fd7160dd311f0c50e45b0b498701f6735a37fabacbf9f36903381d9e18759a684 \ No newline at end of file +ff596b17d2cd33f964a952279b824292272ab73f57ded94812286e6bce12b5c54852d5ccd1dd137fa6312101568c06510cd89f822469d6c904a17f96259e4d03 \ No newline at end of file diff --git a/tests/regression_tests/source/test.py b/tests/regression_tests/source/test.py index 00171a255..cd468c5b8 100644 --- a/tests/regression_tests/source/test.py +++ b/tests/regression_tests/source/test.py @@ -14,7 +14,7 @@ class SourceTestHarness(PyAPITestHarness): materials = openmc.Materials([mat1]) materials.export_to_xml() - sphere = openmc.Sphere(surface_id=1, R=10.0, boundary_type='vacuum') + sphere = openmc.Sphere(surface_id=1, r=10.0, boundary_type='vacuum') inside_sphere = openmc.Cell(cell_id=1) inside_sphere.region = -sphere inside_sphere.fill = mat1 diff --git a/tests/regression_tests/surface_tally/test.py b/tests/regression_tests/surface_tally/test.py index 24dd66314..96199ec2a 100644 --- a/tests/regression_tests/surface_tally/test.py +++ b/tests/regression_tests/surface_tally/test.py @@ -25,8 +25,8 @@ class SurfaceTallyTestHarness(PyAPITestHarness): materials_file.export_to_xml() # Instantiate ZCylinder surfaces - fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=1, \ - name='Fuel OR') + fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, r=1, + name='Fuel OR') left = openmc.XPlane(surface_id=2, x0=-2, name='left') right = openmc.XPlane(surface_id=3, x0=2, name='right') bottom = openmc.YPlane(y0=-2, name='bottom') diff --git a/tests/regression_tests/tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat index 80f55b034..3ec8d8d4c 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -511,4 +511,7 @@ total tracklength + + H1-production H2-production H3-production He3-production He4-production heating damage-energy + diff --git a/tests/regression_tests/tallies/results_true.dat b/tests/regression_tests/tallies/results_true.dat index 08a3ba110..de1a18eff 100644 --- a/tests/regression_tests/tallies/results_true.dat +++ b/tests/regression_tests/tallies/results_true.dat @@ -1 +1 @@ -b5edf87cb58db29aa1c38203141df2f055b88aca675aee9673879b579391412abd232cb3d404f60840ac86b936384050576cccb1e3a1fc5c8290b6e890dda74c \ No newline at end of file +c37e0468f1684f7810429332721762884f3cdf0429d38caa99500bbde04ad84cf1e1639adc46b6db44b28b6f31028ed029f85920c8164d77463b6d89336043a8 \ No newline at end of file diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index eeb74636f..543d0acd2 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -165,6 +165,10 @@ def test_tallies(): all_nuclide_tallies[3].filters = [mesh_filter] all_nuclide_tallies[3].nuclides = ['U235'] + fusion_tally = Tally() + fusion_tally.scores = ['H1-production', 'H2-production', 'H3-production', + 'He3-production', 'He4-production', 'heating', 'damage-energy'] + model.tallies += [ azimuthal_tally1, azimuthal_tally2, azimuthal_tally3, cellborn_tally, dg_tally, energy_tally, energyout_tally, @@ -174,5 +178,6 @@ def test_tallies(): model.tallies += score_tallies model.tallies += flux_tallies model.tallies += all_nuclide_tallies + model.tallies.append(fusion_tally) harness.main() diff --git a/tests/regression_tests/triso/test.py b/tests/regression_tests/triso/test.py index 9ede6dbb0..3b858e6a7 100644 --- a/tests/regression_tests/triso/test.py +++ b/tests/regression_tests/triso/test.py @@ -44,7 +44,7 @@ class TRISOTestHarness(PyAPITestHarness): graphite.add_s_alpha_beta('c_Graphite') # Create TRISO particles - spheres = [openmc.Sphere(R=r*1e-4) + spheres = [openmc.Sphere(r=r*1e-4) for r in [212.5, 312.5, 347.5, 382.5]] c1 = openmc.Cell(fill=fuel, region=-spheres[0]) c2 = openmc.Cell(fill=porous_carbon, region=+spheres[0] & -spheres[1]) diff --git a/tests/regression_tests/volume_calc/test.py b/tests/regression_tests/volume_calc/test.py index fda4b3321..da8399c1f 100644 --- a/tests/regression_tests/volume_calc/test.py +++ b/tests/regression_tests/volume_calc/test.py @@ -25,10 +25,10 @@ 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') + 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_sphere = openmc.Sphere(4, z0=-5., r=1., boundary_type='vacuum') bottom_plane = openmc.ZPlane(5, z0=-5.) # Define geometry diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py index 94fa6abc7..aa28be9c3 100644 --- a/tests/unit_tests/conftest.py +++ b/tests/unit_tests/conftest.py @@ -46,7 +46,7 @@ def cell_with_lattice(): m_inside = [openmc.Material(), openmc.Material(), None, openmc.Material()] m_outside = openmc.Material() - cyl = openmc.ZCylinder(R=1.0) + cyl = openmc.ZCylinder(r=1.0) inside_cyl = openmc.Cell(fill=m_inside, region=-cyl) outside_cyl = openmc.Cell(fill=m_outside, region=+cyl) univ = openmc.Universe(cells=[inside_cyl, outside_cyl]) @@ -63,7 +63,7 @@ def cell_with_lattice(): @pytest.fixture def mixed_lattice_model(uo2, water): - cyl = openmc.ZCylinder(R=0.4) + cyl = openmc.ZCylinder(r=0.4) c1 = openmc.Cell(fill=uo2, region=-cyl) c1.temperature = 600.0 c2 = openmc.Cell(fill=water, region=+cyl) diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 0ebc898b4..be88072d9 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -400,3 +400,54 @@ def test_load_nuclide(capi_init): # load non-existent nuclide with pytest.raises(exc.DataError): openmc.capi.load_nuclide('Pu3') + + +def test_id_map(capi_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.width = 1.26 + s.height = 1.26 + s.v_res = 3 + s.h_res = 3 + s.origin = (0.0, 0.0, 0.0) + s.basis = 'xy' + s.level = -1 + + ids = openmc.capi.plot.id_map(s) + assert np.array_equal(expected_ids, ids) + +def test_property_map(capi_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.width = 1.26 + s.height = 1.26 + s.v_res = 3 + s.h_res = 3 + s.origin = (0.0, 0.0, 0.0) + s.basis = 'xy' + s.level = -1 + + properties = openmc.capi.plot.property_map(s) + assert np.allclose(expected_properties, properties, atol=1e-04) + + +def test_position(capi_init): + + pos = openmc.capi.plot._Position(1.0, 2.0, 3.0) + + assert tuple(pos) == (1.0, 2.0, 3.0) + + pos[0] = 1.3 + pos[1] = 2.3 + pos[2] = 3.3 + + assert tuple(pos) == (1.3, 2.3, 3.3) diff --git a/tests/unit_tests/test_data_photon.py b/tests/unit_tests/test_data_photon.py index 5036d08fd..9bbbcc8ec 100644 --- a/tests/unit_tests/test_data_photon.py +++ b/tests/unit_tests/test_data_photon.py @@ -71,10 +71,20 @@ def test_transitions(element): assert sum(matrix['probability']) == pytest.approx(1.0) -@pytest.mark.parametrize('element', ['H', 'Al', 'Ag'], indirect=True) -def test_bremsstrahlung(element): +@pytest.mark.parametrize( + 'element, I, i_shell, ionization_energy, num_electrons', [ + ('H', 19.2, 0, 13.6, 1), + ('O', 95.0, 2, 13.62, 4), + ('U', 890.0, 25, 6.033, -3) + ], + indirect=['element'] +) +def test_bremsstrahlung(element, I, i_shell, ionization_energy, num_electrons): brems = element.bremsstrahlung assert isinstance(brems, Mapping) + assert brems['I'] == I + assert brems['num_electrons'][i_shell] == num_electrons + assert brems['ionization_energy'][i_shell] == ionization_energy assert np.all(np.diff(brems['electron_energy']) > 0.0) assert np.all(np.diff(brems['photon_energy']) > 0.0) assert brems['photon_energy'][0] == 0.0 @@ -114,25 +124,25 @@ def test_reactions(element, reaction): reactions[18] -@pytest.mark.parametrize( - 'element, I', [ - ('H', 19.2), - ('O', 95.0), - ('U', 890.0) - ], - indirect=['element'] -) -def test_stopping_powers(element, I): - stopping_powers = element.stopping_powers - assert isinstance(stopping_powers, Mapping) - assert stopping_powers['I'] == I - assert np.all(np.diff(stopping_powers['energy']) > 0.0) - assert len(stopping_powers['s_collision']) == 200 - assert len(stopping_powers['s_radiative']) == 200 - - @pytest.mark.parametrize('element', ['Pu'], indirect=True) def test_export_to_hdf5(tmpdir, element): filename = str(tmpdir.join('tmp.h5')) element.export_to_hdf5(filename) assert os.path.exists(filename) + # Read in data from hdf5 + element2 = openmc.data.IncidentPhoton.from_hdf5(filename) + # Check for some cross section and datasets of element and element2 + energy = np.logspace(np.log10(1.0), np.log10(1.0e10), num=100) + for mt in (502, 504, 515, 517, 522, 541, 570): + xs = element[mt].xs(energy) + xs2 = element2[mt].xs(energy) + assert np.allclose(xs, xs2) + assert element[502].scattering_factor == element2[502].scattering_factor + assert element.atomic_relaxation.transitions['O3'].equals( + element2.atomic_relaxation.transitions['O3']) + assert (element.compton_profiles['binding_energy'] == + element2.compton_profiles['binding_energy']).all() + assert (element.bremsstrahlung['electron_energy'] == + element2.bremsstrahlung['electron_energy']).all() + # Export to hdf5 again + element2.export_to_hdf5(filename, 'w') diff --git a/tests/unit_tests/test_endf.py b/tests/unit_tests/test_endf.py new file mode 100644 index 000000000..147e2db87 --- /dev/null +++ b/tests/unit_tests/test_endf.py @@ -0,0 +1,16 @@ +from openmc.data import endf +from pytest import approx + + +def test_float_endf(): + assert endf.float_endf('+3.2146') == approx(3.2146) + assert endf.float_endf('.12345') == approx(0.12345) + assert endf.float_endf('6.022+23') == approx(6.022e23) + assert endf.float_endf('6.022-23') == approx(6.022e-23) + assert endf.float_endf(' +1.01+ 2') == approx(101.0) + assert endf.float_endf(' -1.01- 2') == approx(-0.0101) + + +def test_int_endf(): + assert endf.int_endf(' ') == 0 + assert endf.int_endf('+4032') == 4032 diff --git a/tests/unit_tests/test_geometry.py b/tests/unit_tests/test_geometry.py index b5d6076bb..f3718630a 100644 --- a/tests/unit_tests/test_geometry.py +++ b/tests/unit_tests/test_geometry.py @@ -10,8 +10,8 @@ def test_volume(run_in_tmpdir, uo2): # Create model with nested spheres model = openmc.model.Model() model.materials.append(uo2) - inner = openmc.Sphere(R=1.) - outer = openmc.Sphere(R=2., boundary_type='vacuum') + inner = openmc.Sphere(r=1.) + outer = openmc.Sphere(r=2., boundary_type='vacuum') c1 = openmc.Cell(fill=uo2, region=-inner) c2 = openmc.Cell(region=+inner & -outer) u = openmc.Universe(cells=[c1, c2]) @@ -44,8 +44,8 @@ def test_volume(run_in_tmpdir, uo2): def test_export_xml(run_in_tmpdir, uo2): - s1 = openmc.Sphere(R=1.) - s2 = openmc.Sphere(R=2., boundary_type='reflective') + s1 = openmc.Sphere(r=1.) + s2 = openmc.Sphere(r=2., boundary_type='reflective') c1 = openmc.Cell(fill=uo2, region=-s1) c2 = openmc.Cell(fill=uo2, region=+s1 & -s2) geom = openmc.Geometry([c1, c2]) @@ -203,6 +203,30 @@ def test_get_by_name(): assert not univs +def test_hex_prism(): + hex_prism = openmc.model.get_hexagonal_prism(edge_length=5.0, + origin=(0.0, 0.0), + orientation='y') + # clear checks + assert (0.0, 0.0, 0.0) in hex_prism + assert (10.0, 10.0, 10.0) not in hex_prism + # edge checks + assert (0.0, 5.01, 0.0) not in hex_prism + assert (0.0, 4.99, 0.0) in hex_prism + + rounded_hex_prism = openmc.model.get_hexagonal_prism(edge_length=5.0, + origin=(0.0, 0.0), + orientation='y', + corner_radius=1.0) + + # clear checks + assert (0.0, 0.0, 0.0) in rounded_hex_prism + assert (10.0, 10.0, 10.0) not in rounded_hex_prism + # edge checks + assert (0.0, 5.01, 0.0) not in rounded_hex_prism + assert (0.0, 4.99, 0.0) not in rounded_hex_prism + + def test_get_lattice_by_name(cell_with_lattice): cells, _, _, lattice = cell_with_lattice geom = openmc.Geometry([cells[-1]]) @@ -245,6 +269,7 @@ def test_determine_paths(cell_with_lattice): assert geom.get_instances(cells[0].paths[i]) == i assert geom.get_instances(mats[-1].paths[i]) == i + def test_from_xml(run_in_tmpdir, mixed_lattice_model): # Export model mixed_lattice_model.export_to_xml() diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py index 47dc1c029..fe86e2a78 100644 --- a/tests/unit_tests/test_lattice.py +++ b/tests/unit_tests/test_lattice.py @@ -7,7 +7,7 @@ import pytest @pytest.fixture(scope='module') def pincell1(uo2, water): - cyl = openmc.ZCylinder(R=0.35) + cyl = openmc.ZCylinder(r=0.35) fuel = openmc.Cell(fill=uo2, region=-cyl) moderator = openmc.Cell(fill=water, region=+cyl) @@ -19,7 +19,7 @@ def pincell1(uo2, water): @pytest.fixture(scope='module') def pincell2(uo2, water): - cyl = openmc.ZCylinder(R=0.4) + cyl = openmc.ZCylinder(r=0.4) fuel = openmc.Cell(fill=uo2, region=-cyl) moderator = openmc.Cell(fill=water, region=+cyl) diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py index 4d79fc579..feac87326 100644 --- a/tests/unit_tests/test_mesh_from_lattice.py +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -5,7 +5,7 @@ import pytest @pytest.fixture(scope='module') def pincell1(uo2, water): - cyl = openmc.ZCylinder(R=0.35) + cyl = openmc.ZCylinder(r=0.35) fuel = openmc.Cell(fill=uo2, region=-cyl) moderator = openmc.Cell(fill=water, region=+cyl) @@ -17,7 +17,7 @@ def pincell1(uo2, water): @pytest.fixture(scope='module') def pincell2(uo2, water): - cyl = openmc.ZCylinder(R=0.4) + cyl = openmc.ZCylinder(r=0.4) fuel = openmc.Cell(fill=uo2, region=-cyl) moderator = openmc.Cell(fill=water, region=+cyl) @@ -89,12 +89,12 @@ def rlat3(pincell1, pincell2, uo2, water, zr): def test_mesh2d(rlat2): shape = np.array(rlat2.shape) width = shape*rlat2.pitch - + mesh1 = openmc.Mesh.from_rect_lattice(rlat2) assert np.array_equal(mesh1.dimension, (3, 3)) assert np.array_equal(mesh1.lower_left, rlat2.lower_left) assert np.array_equal(mesh1.upper_right, rlat2.lower_left + width) - + mesh2 = openmc.Mesh.from_rect_lattice(rlat2, division=3) assert np.array_equal(mesh2.dimension, (9, 9)) assert np.array_equal(mesh2.lower_left, rlat2.lower_left) diff --git a/tests/unit_tests/test_model_triso.py b/tests/unit_tests/test_model_triso.py index 1d2e0905c..782373414 100644 --- a/tests/unit_tests/test_model_triso.py +++ b/tests/unit_tests/test_model_triso.py @@ -47,7 +47,7 @@ def centers_rectangular_prism(): @pytest.fixture(scope='module') def centers_x_cylinder(): - cylinder = openmc.XCylinder(R=1, y0=1, z0=2) + cylinder = openmc.XCylinder(r=1, y0=1, z0=2) min_x = openmc.XPlane(x0=0) max_x = openmc.XPlane(x0=1) region = +min_x & -max_x & -cylinder @@ -57,7 +57,7 @@ def centers_x_cylinder(): @pytest.fixture(scope='module') def centers_y_cylinder(): - cylinder = openmc.YCylinder(R=1, x0=1, z0=2) + cylinder = openmc.YCylinder(r=1, x0=1, z0=2) min_y = openmc.YPlane(y0=0) max_y = openmc.YPlane(y0=1) region = +min_y & -max_y & -cylinder @@ -67,7 +67,7 @@ def centers_y_cylinder(): @pytest.fixture(scope='module') def centers_z_cylinder(): - cylinder = openmc.ZCylinder(R=1, x0=1, y0=2) + cylinder = openmc.ZCylinder(r=1, x0=1, y0=2) min_z = openmc.ZPlane(z0=0) max_z = openmc.ZPlane(z0=1) region = +min_z & -max_z & -cylinder @@ -77,7 +77,7 @@ def centers_z_cylinder(): @pytest.fixture(scope='module') def centers_sphere(): - sphere = openmc.Sphere(R=1, x0=1, y0=2, z0=3) + sphere = openmc.Sphere(r=1, x0=1, y0=2, z0=3) region = -sphere return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) @@ -85,8 +85,8 @@ def centers_sphere(): @pytest.fixture(scope='module') def centers_spherical_shell(): - sphere = openmc.Sphere(R=1, x0=1, y0=2, z0=3) - inner_sphere = openmc.Sphere(R=0.5, x0=1, y0=2, z0=3) + sphere = openmc.Sphere(r=1, x0=1, y0=2, z0=3) + inner_sphere = openmc.Sphere(r=0.5, x0=1, y0=2, z0=3) region = -sphere & +inner_sphere return openmc.model.pack_spheres(radius=_RADIUS, region=region, pf=_PACKING_FRACTION, initial_pf=0.2) @@ -94,7 +94,7 @@ def centers_spherical_shell(): @pytest.fixture(scope='module') def triso_universe(): - sphere = openmc.Sphere(R=_RADIUS) + sphere = openmc.Sphere(r=_RADIUS) cell = openmc.Cell(region=-sphere) univ = openmc.Universe(cells=[cell]) return univ @@ -179,10 +179,10 @@ def test_packing_fraction(container, centers): def test_num_spheres(): """Check that the function returns the correct number of spheres""" centers = openmc.model.pack_spheres( - radius=_RADIUS, region=-openmc.Sphere(R=1), num_spheres=50 + radius=_RADIUS, region=-openmc.Sphere(r=1), num_spheres=50 ) assert len(centers) == 50 - + def test_triso_lattice(triso_universe, centers_rectangular_prism): trisos = [openmc.model.TRISO(_RADIUS, triso_universe, c) @@ -203,7 +203,7 @@ def test_container_input(triso_universe): # Invalid container shape with pytest.raises(ValueError): centers = openmc.model.pack_spheres( - radius=_RADIUS, region=+openmc.Sphere(R=1), num_spheres=100 + radius=_RADIUS, region=+openmc.Sphere(r=1), num_spheres=100 ) @@ -211,17 +211,17 @@ def test_packing_fraction_input(): # Provide neither packing fraction nor number of spheres with pytest.raises(ValueError): centers = openmc.model.pack_spheres( - radius=_RADIUS, region=-openmc.Sphere(R=1) + radius=_RADIUS, region=-openmc.Sphere(r=1) ) # Specify a packing fraction that is too high for CRP with pytest.raises(ValueError): centers = openmc.model.pack_spheres( - radius=_RADIUS, region=-openmc.Sphere(R=1), pf=1 + radius=_RADIUS, region=-openmc.Sphere(r=1), pf=1 ) # Specify a packing fraction that is too high for RSP with pytest.raises(ValueError): centers = openmc.model.pack_spheres( - radius=_RADIUS, region=-openmc.Sphere(R=1), pf=0.5, initial_pf=0.4 + radius=_RADIUS, region=-openmc.Sphere(r=1), pf=0.5, initial_pf=0.4 ) diff --git a/tests/unit_tests/test_plots.py b/tests/unit_tests/test_plots.py index d48b609c1..840e7e635 100644 --- a/tests/unit_tests/test_plots.py +++ b/tests/unit_tests/test_plots.py @@ -45,7 +45,7 @@ def test_repr(myplot): def test_from_geometry(): width = 25. - s = openmc.Sphere(R=width/2, boundary_type='vacuum') + s = openmc.Sphere(r=width/2, boundary_type='vacuum') c = openmc.Cell(region=-s) univ = openmc.Universe(cells=[c]) geom = openmc.Geometry(univ) diff --git a/tests/unit_tests/test_region.py b/tests/unit_tests/test_region.py index 377c8fbd5..d1074fd7d 100644 --- a/tests/unit_tests/test_region.py +++ b/tests/unit_tests/test_region.py @@ -34,6 +34,12 @@ def test_union(reset): assert (6, -1, 0) not in reg2 assert str(reg2) == '((1 | -2) 3)' + # translate method + regt = region.translate((2.0, 0.0, 0.0)) + assert (-4, 0, 0) in regt + assert (6, 0, 0) not in regt + assert (8, 0, 0) in regt + def test_intersection(reset): s1 = openmc.XPlane(surface_id=1, x0=5) @@ -61,9 +67,15 @@ def test_intersection(reset): assert (-6, -2, 0) not in reg2 assert str(reg2) == '((-1 2) | 3)' + # translate method + regt = region.translate((2.0, 0.0, 0.0)) + assert (-4, 0, 0) not in regt + assert (6, 0, 0) in regt + assert (8, 0, 0) not in regt + def test_complement(reset): - zcyl = openmc.ZCylinder(surface_id=1, R=1.) + zcyl = openmc.ZCylinder(surface_id=1, r=1.) z0 = openmc.ZPlane(surface_id=2, z0=-5.) z1 = openmc.ZPlane(surface_id=3, z0=5.) outside = +zcyl | -z0 | +z1 @@ -88,6 +100,12 @@ def test_complement(reset): assert (0, 0, 6) not in inside assert (0, 0, 6) in outside + # translate method + inside_t = inside.translate((1.0, 1.0, 1.0)) + ll, ur = inside_t.bounding_box + assert ll == pytest.approx((0., 0., -4.)) + assert ur == pytest.approx((2., 2., 6.)) + def test_get_surfaces(): s1 = openmc.XPlane() diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 697eb095f..e42f6240f 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -36,7 +36,6 @@ def test_export_to_xml(run_in_tmpdir): s.tabular_legendre = {'enable': True, 'num_points': 50} s.temperature = {'default': 293.6, 'method': 'interpolation', 'multipole': True, 'range': (200., 1000.)} - s.threads = 8 s.trace = (10, 1, 20) s.track = [1, 1, 1, 2, 1, 1] s.ufs_mesh = mesh diff --git a/tests/unit_tests/test_surface.py b/tests/unit_tests/test_surface.py index 3db84cc05..6ed657a4c 100644 --- a/tests/unit_tests/test_surface.py +++ b/tests/unit_tests/test_surface.py @@ -13,7 +13,7 @@ def assert_infinite_bb(s): def test_plane(): - s = openmc.Plane(A=1, B=2, C=-1, D=3, name='my plane') + s = openmc.Plane(a=1, b=2, c=-1, d=3, name='my plane') assert s.a == 1 assert s.b == 2 assert s.c == -1 @@ -29,10 +29,28 @@ def test_plane(): x, y, z = (4, 3, 6) assert s.evaluate((x, y, z)) == pytest.approx(s.a*x + s.b*y + s.c*z - s.d) + # translate method + st = s.translate((1.0, 0.0, 0.0)) + assert (st.a, st.b, st.c, st.d) == (s.a, s.b, s.c, 4) + # Make sure repr works repr(s) +def test_plane_from_points(): + # Generate the plane x - y = 1 given three points + p1 = (0, -1, 0) + p2 = (1, 0, 0) + p3 = (1, 0, 1) + s = openmc.Plane.from_points(p1, p2, p3) + + # Confirm correct coefficients + assert s.a == 1.0 + assert s.b == -1.0 + assert s.c == 0.0 + assert s.d == 1.0 + + def test_xplane(): s = openmc.XPlane(x0=3., boundary_type='reflective') assert s.x0 == 3. @@ -55,6 +73,10 @@ def test_xplane(): # evaluate method assert s.evaluate((5., 0., 0.)) == pytest.approx(2.) + # translate method + st = s.translate((1.0, 0.0, 0.0)) + assert st.x0 == s.x0 + 1 + # Make sure repr works repr(s) @@ -80,6 +102,10 @@ def test_yplane(): # evaluate method assert s.evaluate((0., 0., 0.)) == pytest.approx(-3.) + # translate method + st = s.translate((0.0, 1.0, 0.0)) + assert st.y0 == s.y0 + 1 + def test_zplane(): s = openmc.ZPlane(z0=3.) @@ -102,13 +128,17 @@ def test_zplane(): # evaluate method assert s.evaluate((0., 0., 10.)) == pytest.approx(7.) + # translate method + st = s.translate((0.0, 0.0, 1.0)) + assert st.z0 == s.z0 + 1 + # Make sure repr works repr(s) def test_xcylinder(): y, z, r = 3, 5, 2 - s = openmc.XCylinder(y0=y, z0=z, R=r) + s = openmc.XCylinder(y0=y, z0=z, r=r) assert s.y0 == y assert s.z0 == z assert s.r == r @@ -124,6 +154,12 @@ def test_xcylinder(): # evaluate method assert s.evaluate((0, y, z)) == pytest.approx(-r**2) + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.y0 == s.y0 + 1 + assert st.z0 == s.z0 + 1 + assert st.r == s.r + # Make sure repr works repr(s) @@ -139,7 +175,7 @@ def test_periodic(): def test_ycylinder(): x, z, r = 3, 5, 2 - s = openmc.YCylinder(x0=x, z0=z, R=r) + s = openmc.YCylinder(x0=x, z0=z, r=r) assert s.x0 == x assert s.z0 == z assert s.r == r @@ -155,10 +191,16 @@ def test_ycylinder(): # evaluate method assert s.evaluate((x, 0, z)) == pytest.approx(-r**2) + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.z0 == s.z0 + 1 + assert st.r == s.r + def test_zcylinder(): x, y, r = 3, 5, 2 - s = openmc.ZCylinder(x0=x, y0=y, R=r) + s = openmc.ZCylinder(x0=x, y0=y, r=r) assert s.x0 == x assert s.y0 == y assert s.r == r @@ -174,13 +216,19 @@ def test_zcylinder(): # evaluate method assert s.evaluate((x, y, 0)) == pytest.approx(-r**2) + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.y0 == s.y0 + 1 + assert st.r == s.r + # Make sure repr works repr(s) def test_sphere(): x, y, z, r = -3, 5, 6, 2 - s = openmc.Sphere(x0=x, y0=y, z0=z, R=r) + s = openmc.Sphere(x0=x, y0=y, z0=z, r=r) assert s.x0 == x assert s.y0 == y assert s.z0 == z @@ -197,13 +245,20 @@ def test_sphere(): # evaluate method assert s.evaluate((x, y, z)) == pytest.approx(-r**2) + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.y0 == s.y0 + 1 + assert st.z0 == s.z0 + 1 + assert st.r == s.r + # Make sure repr works repr(s) def cone_common(apex, r2, cls): x, y, z = apex - s = cls(x0=x, y0=y, z0=z, R2=r2) + s = cls(x0=x, y0=y, z0=z, r2=r2) assert s.x0 == x assert s.y0 == y assert s.z0 == z @@ -215,6 +270,13 @@ def cone_common(apex, r2, cls): # evaluate method -- should be zero at apex assert s.evaluate((x, y, z)) == pytest.approx(0.0) + # translate method + st = s.translate((1.0, 1.0, 1.0)) + assert st.x0 == s.x0 + 1 + assert st.y0 == s.y0 + 1 + assert st.z0 == s.z0 + 1 + assert st.r2 == s.r2 + # Make sure repr works repr(s) @@ -257,3 +319,24 @@ def test_quadric(): # evaluate method assert s.evaluate((0., 0., 0.)) == pytest.approx(coeffs['k']) assert s.evaluate((1., 1., 1.)) == pytest.approx(3 + coeffs['k']) + + # translate method + st = s.translate((1.0, 1.0, 1.0)) + for coeff in 'abcdef': + assert getattr(s, coeff) == getattr(st, coeff) + assert (st.g, st.h, st.j) == (-2, -2, -2) + assert st.k == s.k + 3 + + +def test_cylinder_from_points(): + # Generate 45-degree rotated cylinder in x-y plane with radius 1 + p1 = (0, 0, 0) + p2 = (1, 1, 0) + s = openmc.model.cylinder_from_points(p1, p2, 1) + + # Points p1 and p2 need to be inside cylinder + assert p1 in -s + assert p2 in -s + assert (-1, 1, 0) in +s + assert (1, -1, 0) in +s + assert (0, 0, 1.5) in +s diff --git a/tests/unit_tests/test_universe.py b/tests/unit_tests/test_universe.py index 89904524b..674fc5c18 100644 --- a/tests/unit_tests/test_universe.py +++ b/tests/unit_tests/test_universe.py @@ -34,8 +34,8 @@ def test_basic(): def test_bounding_box(): - cyl1 = openmc.ZCylinder(R=1.0) - cyl2 = openmc.ZCylinder(R=2.0) + cyl1 = openmc.ZCylinder(r=1.0) + cyl2 = openmc.ZCylinder(r=2.0) c1 = openmc.Cell(region=-cyl1) c2 = openmc.Cell(region=+cyl1 & -cyl2) diff --git a/tools/ci/download-xs.sh b/tools/ci/download-xs.sh index 5fbe93c71..ce4d12c1b 100755 --- a/tools/ci/download-xs.sh +++ b/tools/ci/download-xs.sh @@ -3,7 +3,7 @@ set -ex # Download HDF5 data if [[ ! -e $HOME/nndc_hdf5/cross_sections.xml ]]; then - wget -q -O - https://anl.box.com/shared/static/snrluuy79o2fffpvpng9bsuis5kl811d.xz | tar -C $HOME -xJ + wget -q -O - https://anl.box.com/shared/static/pzutl4i2717yypv12l78l7fn5nmg6grs.xz | tar -C $HOME -xJ fi # Download ENDF/B-VII.1 distribution