Merge tag 'v0.7.1'

This commit is contained in:
Paul Romano 2015-12-23 16:48:57 -06:00
commit 77237af16b
584 changed files with 38731 additions and 34225 deletions

13
.gitignore vendored
View file

@ -36,6 +36,8 @@ src/xml-fortran/xmlreader
# Test results error file
results_error.dat
inputs_error.dat
results_test.dat
# Test build files
tests/build/
@ -62,4 +64,13 @@ data/nndc
.idea/*
# IPython notebook checkpoints
.ipynb_checkpoints
.ipynb_checkpoints
# Multi-group cross section IPython Notebook
docs/source/pythonapi/examples/*.xml
docs/source/pythonapi/examples/*.png
docs/source/pythonapi/examples/*.xls
docs/source/pythonapi/examples/mgxs
docs/source/pythonapi/examples/tracks
docs/source/pythonapi/examples/fission-rates
docs/source/pythonapi/examples/plots

View file

@ -27,7 +27,7 @@ before_install:
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py pandas
- source activate test-environment
# Install GCC, MPICH, HDF5, PHDF5

View file

@ -7,6 +7,12 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/include)
# Set module path
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
# Make sure Fortran module directory is included when building
include_directories(${CMAKE_BINARY_DIR}/include)
#===============================================================================
# Architecture specific definitions
#===============================================================================
@ -23,37 +29,23 @@ option(openmp "Enable shared-memory parallelism with OpenMP" OFF)
option(profile "Compile with profiling flags" OFF)
option(debug "Compile with debug flags" OFF)
option(optimize "Turn on all compiler optimization flags" OFF)
option(verbose "Create verbose Makefiles" OFF)
option(coverage "Compile with coverage analysis flags" OFF)
option(mpif08 "Use Fortran 2008 MPI interface" OFF)
if (verbose)
set(CMAKE_VERBOSE_MAKEFILE on)
endif()
# Maximum number of nested coordinates levels
set(maxcoord 10 CACHE STRING "Maximum number of nested coordinate levels")
add_definitions(-DMAX_COORD=${maxcoord})
#===============================================================================
# MPI for distributed-memory parallelism / HDF5 for binary output
# MPI for distributed-memory parallelism
#===============================================================================
set(MPI_ENABLED FALSE)
set(HDF5_ENABLED FALSE)
if($ENV{FC} MATCHES "mpi[^/]*$")
message("-- Detected MPI wrapper: $ENV{FC}")
add_definitions(-DMPI)
set(MPI_ENABLED TRUE)
elseif($ENV{FC} MATCHES "h5fc$")
message("-- Detected HDF5 wrapper: $ENV{FC}")
add_definitions(-DHDF5)
set(HDF5_ENABLED TRUE)
elseif($ENV{FC} MATCHES "h5pfc$")
message("-- Detected parallel HDF5 wrapper: $ENV{FC}")
add_definitions(-DMPI -DHDF5)
set(MPI_ENABLED TRUE)
set(HDF5_ENABLED TRUE)
endif()
# Check for Fortran 2008 MPI interface
@ -62,11 +54,52 @@ if(MPI_ENABLED AND mpif08)
add_definitions(-DMPIF08)
endif()
#===============================================================================
# HDF5 for binary output
#===============================================================================
# Unfortunately FindHDF5.cmake will always prefer a serial HDF5 installation
# over a parallel installation if both appear on the user's PATH. To get around
# this, we check for the environment variable HDF5_ROOT and if it exists, use it
# to check whether its a parallel version.
if(DEFINED ENV{HDF5_ROOT} AND EXISTS $ENV{HDF5_ROOT}/bin/h5pcc)
set(HDF5_PREFER_PARALLEL TRUE)
else()
set(HDF5_PREFER_PARALLEL FALSE)
endif()
find_package(HDF5 COMPONENTS Fortran_HL)
if(NOT HDF5_FOUND)
message(FATAL_ERROR "Could not find HDF5")
endif()
if(HDF5_IS_PARALLEL)
if(NOT MPI_ENABLED)
message(FATAL_ERROR "Parallel HDF5 must be used with MPI.")
endif()
add_definitions(-DPHDF5)
message("-- Using parallel HDF5")
endif()
#===============================================================================
# Set compile/link flags based on which compiler is being used
#===============================================================================
if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
# Support for Fortran in FindOpenMP was added in CMake 3.1. To support lower
# versions, we manually add the flags. However, at some point in time, the
# manual logic can be removed in favor of the block below
#if(NOT (CMAKE_VERSION VERSION_LESS 3.1))
# if(openmp)
# find_package(OpenMP)
# if(OPENMP_FOUND)
# list(APPEND f90flags ${OpenMP_Fortran_FLAGS})
# list(APPEND ldflags ${OpenMP_Fortran_FLAGS})
# endif()
# endif()
#endif()
if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
# Make sure version is sufficient
execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
@ -75,88 +108,93 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
endif()
# GNU Fortran compiler options
set(f90flags "-cpp -std=f2008 -fbacktrace")
list(APPEND f90flags -cpp -std=f2008 -fbacktrace)
if(debug)
set(f90flags "-g -Wall -pedantic -fbounds-check -ffpe-trap=invalid,overflow,underflow ${f90flags}")
set(ldflags "-g")
if(NOT (GCC_VERSION VERSION_LESS 4.7))
list(APPEND f90flags -Wall)
endif()
list(APPEND f90flags -g -pedantic -fbounds-check
-ffpe-trap=invalid,overflow,underflow)
list(APPEND ldflags -g)
endif()
if(profile)
set(f90flags "-pg ${f90flags}")
set(ldflags "-pg ${ldflags}")
list(APPEND f90flags -pg)
list(APPEND ldflags -pg)
endif()
if(optimize)
set(f90flags "-O3 ${f90flags}")
list(APPEND f90flags -O3)
endif()
if(openmp)
set(f90flags "-fopenmp ${f90flags}")
set(ldflags "-fopenmp ${ldflags}")
list(APPEND f90flags -fopenmp)
list(APPEND ldflags -fopenmp)
endif()
if(coverage)
set(f90flags "-coverage ${f90flags}")
set(ldflags "-coverage ${ldflags}")
list(APPEND f90flags -coverage)
list(APPEND ldflags -coverage)
endif()
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL Intel)
# Intel Fortran compiler options
set(f90flags "-fpp -std08 -assume byterecl -traceback")
list(APPEND f90flags -fpp -std08 -assume byterecl -traceback)
if(debug)
set(f90flags "-g -warn -ftrapuv -fp-stack-check -check all -fpe0 ${f90flags}")
set(ldflags "-g")
list(APPEND f90flags -g -warn -ftrapuv -fp-stack-check
"-check all" -fpe0)
list(APPEND ldflags -g)
endif()
if(profile)
set(f90flags "-pg ${f90flags}")
set(ldflags "-pg ${ldflags}")
list(APPEND f90flags -pg)
list(APPEND ldflags -pg)
endif()
if(optimize)
set(f90flags "-O3 ${f90flags}")
list(APPEND f90flags -O3)
endif()
if(openmp)
set(f90flags "-openmp ${f90flags}")
set(ldflags "-openmp ${ldflags}")
list(APPEND f90flags -openmp)
list(APPEND ldflags -openmp)
endif()
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "PGI")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL PGI)
# PGI Fortran compiler options
set(f90flags "-Mpreprocess -Minform=inform -traceback")
list(APPEND f90flags -Mpreprocess -Minform=inform -traceback)
add_definitions(-DNO_F2008)
if(debug)
set(f90flags "-g -Mbounds -Mchkptr -Mchkstk ${f90flags}")
set(ldflags "-g")
list(APPEND f90flags -g -Mbounds -Mchkptr -Mchkstk)
list(APPEND ldflags -g)
endif()
if(profile)
set(f90flags "-pg ${f90flags}")
set(ldflags "-pg ${ldflags}")
list(APPEND f90flags -pg)
list(APPEND ldflags -pg)
endif()
if(optimize)
set(f90flags "-fast -Mipa ${f90flags}")
list(APPEND f90flags -fast -Mipa)
endif()
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "XL")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL XL)
# IBM XL compiler options
set(f90flags "-O2")
list(APPEND f90flags -O2)
add_definitions(-DNO_F2008)
if(debug)
set(f90flags "-g -C -qflag=i:i -u")
set(ldflags "-g")
list(APPEND f90flags -g -C -qflag=i:i -u)
list(APPEND ldflags -g)
endif()
if(profile)
set(f90flags "-p ${f90flags}")
set(ldflags "-p ${ldflags}")
list(APPEND f90flags -p)
list(APPEND ldflags -p)
endif()
if(optimize)
set(f90flags "-O3 ${f90flags}")
list(APPEND f90flags -O3)
endif()
if(openmp)
set(f90flags "-qsmp=omp ${f90flags}")
set(ldflags "-qsmp=omp ${ldflags}")
list(APPEND f90flags -qsmp=omp)
list(APPEND ldflags -qsmp=omp)
endif()
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Cray")
elseif(CMAKE_Fortran_COMPILER_ID STREQUAL Cray)
# Cray Fortran compiler options
set(f90flags "-e Z -m 0")
list(APPEND f90flags -e Z -m 0)
if(debug)
set(f90flags "-g -R abcnsp -O0 ${f90flags}")
set(ldflags "-g")
list(APPEND f90flags -g -R abcnsp -O0)
list(APPEND ldflags -g)
endif()
endif()
@ -197,6 +235,14 @@ if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/xml/fox/.git)
endif()
add_subdirectory(src/xml/fox)
#===============================================================================
# RPATH information
#===============================================================================
# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
#===============================================================================
# Build OpenMC executable
#===============================================================================
@ -204,10 +250,34 @@ add_subdirectory(src/xml/fox)
set(program "openmc")
file(GLOB source src/*.F90 src/xml/openmc_fox.F90)
add_executable(${program} ${source})
target_link_libraries(${program} ${libraries} fox_dom)
set_target_properties(${program} PROPERTIES
COMPILE_FLAGS "${f90flags}"
LINK_FLAGS "${ldflags}")
# target_include_directories was added in CMake 2.8.11 and is the recommended
# way to set include directories. For lesser versions, we revert to set_property
if(CMAKE_VERSION VERSION_LESS 2.8.11)
include_directories(${HDF5_INCLUDE_DIRS})
else()
target_include_directories(${program} PUBLIC ${HDF5_INCLUDE_DIRS})
endif()
# target_compile_options was added in CMake 2.8.12 and is the recommended way to
# set compile flags. Note that this sets the COMPILE_OPTIONS property (also
# available only in 2.8.12+) rather than the COMPILE_FLAGS property, which is
# deprecated. The former can handle lists whereas the latter cannot.
if(CMAKE_VERSION VERSION_LESS 4.8.12)
string(REPLACE ";" " " f90flags "${f90flags}")
set_property(TARGET ${program} PROPERTY COMPILE_FLAGS "${f90flags}")
else()
target_compile_options(${program} PUBLIC ${f90flags})
endif()
# Add HDF5 library directories to link line with -L
foreach(LIBDIR ${HDF5_LIBRARY_DIRS})
list(APPEND ldflags "-L${LIBDIR}")
endforeach()
# 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(${program} ${ldflags} ${HDF5_LIBRARIES} fox_dom)
#===============================================================================
# Install executable, scripts, manpage, license
@ -216,14 +286,21 @@ set_target_properties(${program} PROPERTIES
install(TARGETS ${program} RUNTIME DESTINATION bin)
install(DIRECTORY src/relaxng DESTINATION share/openmc)
install(FILES man/man1/openmc.1 DESTINATION share/man/man1)
install(FILES LICENSE DESTINATION "share/doc/${program}/copyright")
install(FILES LICENSE DESTINATION "share/doc/${program}" RENAME copyright)
find_package(PythonInterp)
if(PYTHONINTERP_FOUND)
install(CODE "execute_process(
COMMAND ${PYTHON_EXECUTABLE} setup.py install
--prefix=${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})")
if(debian)
install(CODE "execute_process(
COMMAND ${PYTHON_EXECUTABLE} setup.py install
--root=debian/openmc --install-layout=deb
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})")
else()
install(CODE "execute_process(
COMMAND ${PYTHON_EXECUTABLE} setup.py install
--prefix=${CMAKE_INSTALL_PREFIX}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})")
endif()
endif()
#===============================================================================
@ -306,38 +383,18 @@ foreach(test ${TESTS})
# If a restart test is encounted, need to run with -r and restart file(s)
elseif(${test} MATCHES "restart")
# Set restart file names
if (${HDF5_ENABLED})
# Handle restart tests separately
if(${test} MATCHES "test_statepoint_restart")
set(RESTART_FILE statepoint.07.h5)
elseif(${test} MATCHES "test_sourcepoint_restart")
set(RESTART_FILE statepoint.07.h5 source.07.h5)
elseif(${test} MATCHES "test_particle_restart_eigval")
set(RESTART_FILE particle_12_616.h5)
elseif(${test} MATCHES "test_particle_restart_fixed")
set(RESTART_FILE particle_7_6144.h5)
else(${test} MATCHES "test_statepoint_restart")
message(FATAL_ERROR "Restart test ${test} not recognized")
endif(${test} MATCHES "test_statepoint_restart")
else(${HDF5_ENABLED})
# Handle restart tests separately
if(${test} MATCHES "test_statepoint_restart")
set(RESTART_FILE statepoint.07.binary)
elseif(${test} MATCHES "test_sourcepoint_restart")
set(RESTART_FILE statepoint.07.binary source.07.binary)
elseif(${test} MATCHES "test_particle_restart_eigval")
set(RESTART_FILE particle_12_616.binary)
elseif(${test} MATCHES "test_particle_restart_fixed")
set(RESTART_FILE particle_7_6144.binary)
else(${test} MATCHES "test_statepoint_restart")
message(FATAL_ERROR "Restart test ${test} not recognized")
endif(${test} MATCHES "test_statepoint_restart")
endif(${HDF5_ENABLED})
# Handle restart tests separately
if(${test} MATCHES "test_statepoint_restart")
set(RESTART_FILE statepoint.07.h5)
elseif(${test} MATCHES "test_sourcepoint_restart")
set(RESTART_FILE statepoint.07.h5 source.07.h5)
elseif(${test} MATCHES "test_particle_restart_eigval")
set(RESTART_FILE particle_9_555.h5)
elseif(${test} MATCHES "test_particle_restart_fixed")
set(RESTART_FILE particle_7_928.h5)
else(${test} MATCHES "test_statepoint_restart")
message(FATAL_ERROR "Restart test ${test} not recognized")
endif(${test} MATCHES "test_statepoint_restart")
# Perform serial valgrind and coverage test
add_test(NAME ${TEST_NAME}

View file

@ -0,0 +1,399 @@
#.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 <wdicharry@stellarscience.com>.
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
)

View file

@ -27,6 +27,7 @@ sys.path.insert(0, os.path.abspath('../..'))
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.pngmath',
'sphinx.ext.autosummary',
'sphinxcontrib.tikz',
'sphinx_numfig',
'notebook_sphinxext']
@ -54,7 +55,7 @@ copyright = u'2011-2015, Massachusetts Institute of Technology'
# The short X.Y version.
version = "0.7"
# The full version, including alpha/beta/rc tags.
release = "0.7.0"
release = "0.7.1"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@ -199,7 +200,7 @@ latex_elements = {
\usepackage{enumitem}
\usepackage{amsfonts}
\usepackage{amsmath}
\setlistdepth{9}
\setlistdepth{99}
\usepackage{tikz}
\usetikzlibrary{shapes,snakes,shadows,arrows,calc,decorations.markings,patterns,fit,matrix,spy}
\usepackage{fixltx2e}

View file

@ -16,6 +16,4 @@ as debugging.
styleguide
workflow
xml-parsing
statepoint
voxel
docbuild

View file

@ -1,291 +0,0 @@
.. _devguide_statepoint:
======================================
State Point Binary File Specifications
======================================
The current revision of the statepoint binary file is 13.
**integer(4) FILETYPE_STATEPOINT**
Flags whether this file is a statepoint file or a particle restart file.
**integer(4) REVISION_STATEPOINT**
Revision of the binary state point file. Any time a change is made in the
format of the state-point file, this integer is incremented.
**integer(4) VERSION_MAJOR**
Major version number for OpenMC
**integer(4) VERSION_MINOR**
Minor version number for OpenMC
**integer(4) VERSION_RELEASE**
Release version number for OpenMC
**character(19) time_stamp**
Date and time the state point was written.
**character(255) path**
Absolute path to directory containing input files.
**integer(8) seed**
Pseudo-random number generator seed.
**integer(4) run_mode**
run mode used. The modes are described in constants.F90.
**integer(8) n_particles**
Number of particles used per generation.
**integer(4) current_batch**
The number of batches already simulated.
if (run_mode == MODE_EIGENVALUE)
**integer(4) n_inactive**
Number of inactive batches
**integer(4) gen_per_batch**
Number of generations per batch for criticality calculations
*do i = 1, current_batch \* gen_per_batch*
**real(8) k_generation(i)**
k-effective for the i-th total generation
*do i = 1, current_batch \* gen_per_batch*
**real(8) entropy(i)**
Shannon entropy for the i-th total generation
**real(8) k_col_abs**
Sum of product of collision/absorption estimates of k-effective
**real(8) k_col_tra**
Sum of product of collision/track-length estimates of k-effective
**real(8) k_abs_tra**
Sum of product of absorption/track-length estimates of k-effective
**real(8) k_combined(2)**
Mean and standard deviation of a combined estimate of k-effective
**integer(4) cmfd_on**
Flag that cmfd is on
if (cmfd_on)
**integer(4) cmfd % indices**
Indices for cmfd mesh (i,j,k,g)
**real(8) cmfd % k_cmfd(1:current_batch)**
CMFD eigenvalues
**real(8) cmfd % src(1:G,1:I,1:J,1:K)**
CMFD fission source
**real(8) cmfd % entropy(1:current_batch)**
CMFD estimate of Shannon entropy
**real(8) cmfd % balance(1:current_batch)**
RMS of the residual neutron balance equation on CMFD mesh
**real(8) cmfd % dom(1:current_batch)**
CMFD estimate of dominance ratio
**real(8) cmfd % scr_cmp(1:current_batch)**
RMS comparison of difference between OpenMC and CMFD fission source
**integer(4) n_meshes**
Number of meshes in tallies.xml file
*do i = 1, n_meshes*
**integer(4) meshes(i) % id**
Unique ID of mesh.
**integer(4) meshes(i) % type**
Type of mesh.
**integer(4) meshes(i) % n_dimension**
Number of dimensions for mesh (2 or 3).
**integer(4) meshes(i) % dimension(:)**
Number of mesh cells in each dimension.
**real(8) meshes(i) % lower_left(:)**
Coordinates of lower-left corner of mesh.
**real(8) meshes(i) % upper_right(:)**
Coordinates of upper-right corner of mesh.
**real(8) meshes(i) % width(:)**
Width of each mesh cell in each dimension.
**integer(4) n_tallies**
*do i = 1, n_tallies*
**integer(4) tallies(i) % id**
Unique ID of tally.
**integer(4) tallies(i) % n_realizations**
Number of realizations for the i-th tally.
**integer(4) size(tallies(i) % scores, 1)**
Total number of score bins for the i-th tally
**integer(4) size(tallies(i) % scores, 2)**
Total number of filter bins for the i-th tally
**integer(4) tallies(i) % n_filters**
*do j = 1, tallies(i) % n_filters*
**integer(4) tallies(i) % filter(j) % type**
Type of tally filter.
**integer(4) tallies(i) % filter(j) % n_bins**
Number of bins for filter.
**integer(4)/real(8) tallies(i) % filter(j) % bins(:)**
Value for each filter bin of this type.
**integer(4) tallies(i) % n_nuclide_bins**
Number of nuclide bins. If none are specified, this is just one.
*do j = 1, tallies(i) % n_nuclide_bins*
**integer(4) tallies(i) % nuclide_bins(j)**
Values of specified nuclide bins
**integer(4) tallies(i) % n_score_bins**
Number of scoring bins.
*do j = 1, tallies(i) % n_score_bins*
**integer(4) tallies(i) % score_bins(j)**
Values of specified scoring bins (e.g. SCORE_FLUX).
**integer(4) tallies(i) % n_score_bins**
Number of scoring bins without accounting for those added by
the scatter-pn command.
*do j = 1, tallies(i) % n_user_score_bins*
**character(8) tallies(i) % moment_order(j)**
Tallying moment order for Legendre and spherical
harmonic tally expansions (*e.g.*, 'P2', 'Y1,2', etc.).
**integer(4) source_present**
Flag indicated if source bank is present in the file
**integer(4) n_realizations**
Number of realizations for global tallies.
**integer(4) N_GLOBAL_TALLIES**
Number of global tally scores
*do i = 1, N_GLOBAL_TALLIES*
**real(8) global_tallies(i) % sum**
Accumulated sum for the i-th global tally
**real(8) global_tallies(i) % sum_sq**
Accumulated sum of squares for the i-th global tally
**integer(4) tallies_on**
Flag indicated if tallies are present in the file.
if (tallies_on > 0)
*do i = 1, n_tallies*
*do k = 1, size(tallies(i) % scores, 2)*
*do j = 1, size(tallies(i) % scores, 1)*
**real(8) tallies(i) % scores(j,k) % sum**
Accumulated sum for the j-th score and k-th filter of the
i-th tally
**real(8) tallies(i) % scores(j,k) % sum_sq**
Accumulated sum of squares for the j-th score and k-th
filter of the i-th tally
if (run_mode == MODE_EIGENVALUE and source_present)
*do i = 1, n_particles*
**real(8) source_bank(i) % wgt**
Weight of the i-th source particle
**real(8) source_bank(i) % xyz(1:3)**
Coordinates of the i-th source particle.
**real(8) source_bank(i) % uvw(1:3)**
Direction of the i-th source particle
**real(8) source_bank(i) % E**
Energy of the i-th source particle.

View file

@ -1,52 +0,0 @@
.. _devguide_voxel:
=====================================
Voxel Plot Binary File Specifications
=====================================
The current revision of the voxel plot binary file is 1.
**integer(4) n_voxels_x**
Number of voxels in the x direction
**integer(4) n_voxels_y**
Number of voxels in the y direction
**integer(4) n_voxels_z**
Number of voxels in the z direction
**real(8) width_voxel_x**
Width of voxels in the x direction
**real(8) width_voxel_y**
Width of voxels in the y direction
**real(8) width_voxel_z**
Width of voxels in the z direction
**real(8) lower_left_x**
Lower left x point of the voxel grid
**real(8) lower_left_y**
Lower left y point of the voxel grid
**real(8) lower_left_z**
Lower left z point of the voxel grid
*do x = 1, n_voxels_x*
*do y = 1, n_voxels_y*
*do z = 1, n_voxels_z*
**integer(4) id**
Cell or material id number at this voxel center. Set to -1 when
cell not_found.

View file

@ -142,7 +142,7 @@ than unity. By ensuring that the expected number of fission sites in each mesh
cell is constant, the collision density across all cells, and hence the variance
of tallies, is more uniform than it would be otherwise.
.. _Shannon entropy: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-06-3737_entropy.pdf
.. _Shannon entropy: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-06-3737.pdf
.. [Lieberoth] J. Lieberoth, "A Monte Carlo Technique to Solve the Static
Eigenvalue Problem of the Boltzmann Transport Equation," *Nukleonik*, **11**,

View file

@ -10,7 +10,7 @@ Constructive Solid Geometry
OpenMC uses a technique known as `constructive solid geometry`_ (CSG) to build
arbitrarily complex three-dimensional models in Euclidean space. In a CSG model,
every unique object is described as the union, intersection, or difference of
every unique object is described as the union and/or intersection of
*half-spaces* created by bounding `surfaces`_. Every surface divides all of
space into exactly two half-spaces. We can mathematically define a surface as a
collection of points that satisfy an equation of the form :math:`f(x,y,z) = 0`
@ -54,13 +54,12 @@ dividing space into two half-spaces.
Example of an ellipse and its associated half-spaces.
References to half-spaces created by surfaces are used to define regions of
space of uniform composition, known as *cells*. While some codes allow regions
to be defined by intersections, unions, and differences or half-spaces, OpenMC
is currently limited to cells defined only as intersections of
half-spaces. Thus, the specification of the cell must include a list of
half-space references whose intersection defines the region. The region is then
assigned a material defined elsewhere. Figure :num:`fig-union` shows an
example of a cell defined as the intersection of an ellipse and two planes.
space of uniform composition, which are then assigned to *cells*. OpenMC allows
regions to be defined using union, intersection, and complement operators. As in
MCNP_, the intersection operator is implicit as doesn't need to be written in a
region specification. A defined region is then associated with a material
composition in a cell. Figure :num:`fig-union` shows an example of a cell region
defined as the intersection of an ellipse and two planes.
.. _fig-union:
@ -117,6 +116,10 @@ to fully define the surface.
| Cone parallel to the | z-cone | :math:`(x-x_0)^2 + (y-y_0)^2 | :math:`x_0 \; y_0 \; |
| :math:`z`-axis | | = R^2(z-z_0)^2` | z_0 \; R^2` |
+----------------------+------------+------------------------------+-------------------------+
| General quadric | quadric | :math:`Ax^2 + By^2 + Cz^2 + | :math:`A \; B \; C \; D |
| surface | | Dxy + Eyz + Fxz + Gx + Hy + | \; E \; F \; G \; H \; |
| | | Jz + K` | J \; K` |
+----------------------+------------+------------------------------+-------------------------+
.. _universes:

View file

@ -187,11 +187,11 @@ secondary photons from nuclear de-excitation are tracked in OpenMC.
------------------------
These types of reactions are just treated as inelastic scattering and as such
are subject to the same procedure as described in
:ref:`inelastic-scatter`. Rather than tracking multiple secondary neutrons, the
weight of the outgoing neutron is multiplied by the number of secondary
neutrons, e.g. for :math:`(n,2n)`, only one outgoing neutron is tracked but its
weight is doubled.
are subject to the same procedure as described in :ref:`inelastic-scatter`. For
reactions with integral multiplicity, e.g., :math:`(n,2n)`, an appropriate
number of secondary neutrons are created. For reactions that have a multiplicity
given as a function of the incoming neutron energy (which occasionally occurs
for MT=5), the weight of the outgoing neutron is multiplied by the multiplcity.
.. _fission:
@ -1027,14 +1027,19 @@ probability distribution function can be found by integrating equation
Let us call the normalization factor in the denominator of equation
:eq:`target-pdf-1` :math:`C`.
It is normally assumed that :math:`\sigma (v_r)` is constant over the range of
Constant Cross Section Model
----------------------------
It is often assumed that :math:`\sigma (v_r)` is constant over the range of
relative velocities of interest. This is a good assumption for almost all cases
since the elastic scattering cross section varies slowly with velocity for light
nuclei, and for heavy nuclei where large variations can occur due to resonance
scattering, the moderating effect is rather small. Nonetheless, this assumption
may cause incorrect answers in systems with low-lying resonances that can cause
a significant amount of up-scatter that would be ignored by this assumption
(e.g. U-238 in commercial light-water reactors). Nevertheless, with this
(e.g. U-238 in commercial light-water reactors). We will revisit this assumption
later in :ref:`energy_dependent_xs_model`. For now, continuing with the
assumption, we write :math:`\sigma (v_r) = \sigma_s` which simplifies
:eq:`target-pdf-1` to
@ -1232,6 +1237,35 @@ If is not accepted, then we repeat the process and resample a target speed and
cosine until a combination is found that satisfies equation
:eq:`freegas-accept-2`.
.. _energy_dependent_xs_model:
Energy-Dependent Cross Section Model
------------------------------------
As was noted earlier, assuming that the elastic scattering cross section is
constant in :eq:`reaction-rate` is not strictly correct, especially when
low-lying resonances are present in the cross sections for heavy nuclides. To
correctly account for energy dependence of the scattering cross section entails
performing another rejection step. The most common method is to sample
:math:`\mu` and :math:`v_T` as in the constant cross section approximation and
then perform a rejection on the ratio of the 0 K elastic scattering cross
section at the relative velocity to the maximum 0 K elastic scattering cross
section over the range of velocities considered:
.. math::
:label: dbrc
p_{dbrc} = \frac{\sigma_s(v_r)}{\sigma_{s,max}}
where it should be noted that the maximum is taken over the range :math:`[v_n -
4/\beta, 4_n + 4\beta]`. This method is known as Doppler broadening rejection
correction (DBRC) and was first introduced by `Becker et al.`_. OpenMC has an
implementation of DBRC as well as an accelerated sampling method that are
described fully in `Walsh et al.`_
.. _Becker et al.: http://dx.doi.org/10.1016/j.anucene.2008.12.001
.. _Walsh et al.: http://dx.doi.org/10.1016/j.anucene.2014.01.017
.. _sab_tables:
------------

View file

@ -26,6 +26,10 @@ Overviews
Benchmarking
------------
- Khurrum S. Chaudri and Sikander M. Mirza, "Burnup dependent Monte Carlo
neutron physics calculations of IAEA MTR benchmark," *Prog. Nucl. Energy*,
**81**, 43-52 (2015). `<http://dx.doi.org/j.pnucene.2014.12.018>`_
- Daniel J. Kelly, Brian N. Aviles, Paul K. Romano, Bryan R. Herman,
Nicholas E. Horelik, and Benoit Forget, "Analysis of select BEAVRS PWR
benchmark cycle 1 results using MC21 and OpenMC," *Proc. PHYSOR*, Kyoto,
@ -57,13 +61,8 @@ Coupling and Multi-physics
- Bryan R. Herman, Benoit Forget, and Kord Smith, "Progress toward Monte
Carlo-thermal hydraulic coupling using low-order nonlinear diffusion
acceleration methods." In press, *Ann. Nucl. Energy*,
(2014). `<http://dx.doi.org/10.1016/j.anucene/2014.10.029>`_
- Adam G. Nelson and William R. Martin, "Improved Convergence of Monte Carlo
Generated Multi-Group Scattering Moments," *Proc. Int. Conf. Mathematics and
Computational Methods Applied to Nuclear Science and Engineering*, Sun Valley,
Idaho, May 5--9 (2013).
acceleration methods." *Ann. Nucl. Energy*, **84**, 63-72
(2015). `<http://dx.doi.org/10.1016/j.anucene.2014.10.029>`_
- Bryan R. Herman, Benoit Forget, and Kord Smith, "Utilizing CMFD in OpenMC to
Estimate Dominance Ratio and Adjoint," *Trans. Am. Nucl. Soc.*, **109**,
@ -81,19 +80,65 @@ Geometry
Miscellaneous
-------------
- William Boyd, Sterling Harper, and Paul K. Romano, "Equipping OpenMC for the
big data era," Accepted, *PHYSOR 2016*, Sun Valley, Idaho, May 1-5, 2016.
- Qicang Shen, William Boyd, Benoit Forget, and Kord Smith, "Tally precision
triggers for the OpenMC Monte Carlo code," *Trans. Am. Nucl. Soc.*, **112**,
637-640 (2015).
- Timothy P. Burke, Brian C. Kiedrowski, and William R. Martin, "Flux and
Reaction Rate Kernel Density Estimators in OpenMC," *Trans. Am. Nucl. Soc.*,
**109**, 683-686 (2013).
------------------------------------
Multi-group Cross Section Generation
------------------------------------
- Adam G. Nelson and William R. Martin, "Improved Monte Carlo tallying of
multi-group scattering moments using the NDPP code," *Trans. Am. Nucl. Soc.*,
**113**, 645-648 (2015)
- Adam G. Nelson and William R. Martin, "Improved Monte Carlo tallying of
multi-group scattering moment matrices," *Trans. Am. Nucl. Soc.*, **110**,
217-220 (2014).
- Adam G. Nelson and William R. Martin, "Improved Convergence of Monte Carlo
Generated Multi-Group Scattering Moments," *Proc. Int. Conf. Mathematics and
Computational Methods Applied to Nuclear Science and Engineering*, Sun Valley,
Idaho, May 5--9 (2013).
------------
Nuclear Data
------------
- Colin Josey, Pablo Ducru, Benoit Forget, and Kord Smith, "Windowed multipole
for cross section Doppler broadening," *J. Comput. Phys.*, In Press
(2016). `<http://dx.doi.org/10.1016/jcp.2015.08.013>`_
- Colin Josey, Benoit Forget, and Kord Smith, "Windowed multipole sensitivity to
target accuracy of the optimization procedure," *J. Nucl. Sci. Technol.*,
**52**, 987-992 (2015). `<http://dx.doi.org/10.1080/00223131.2015.1035353>`_
- Jonathan A. Walsh, Paul K. Romano, Benoit Forget, and Kord S. Smith,
"Optimizations of the energy grid search algorithm in continuous-energy Monte
Carlo particle transport codes", *Comput. Phys. Commun.*, **196**, 134-142
(2015). `<http://dx.doi.org/10.1016/j.cpc.2015.05.025>`_
- Jonathan A. Walsh, Benoit Forget, Kord S. Smith, Brian C. Kiedrowski, and
Forrest B. Brown, "Direct, on-the-fly calculation of unresolved resonance
region cross sections in Monte Carlo simulations," *Proc. Joint
Int. Conf. M&C+SNA+MC*, Nashville, Tennessee, Apr. 19--23 (2015).
- Amanda L. Lund, Andrew R. Siegel, Benoit Forget, Colin Josey, and
Paul K. Romano, "Using fractional cascading to accelerate cross section
lookups in Monte Carlo particle transport calculations," *Proc. Joint
Int. Conf. M&C+SNA+MC*, Nashville, Tennessee, Apr. 19--23 (2015).
- Ronald O. Rahaman, Andrew R. Siegel, and Paul K. Romano, "Monte Carlo
performance analysis for varying cross section parameter regimes,"
*Proc. Joint Int. Conf. M&C+SNA+MC*, Nashville, Tennessee, Apr. 19--23 (2015).
- Paul K. Romano and Timothy H. Trumbull, "Comparison of algorithms for Doppler
broadening pointwise tabulated cross sections," *Ann. Nucl. Energy*, **75**,
358--364 (2015). `<http://dx.doi.org/10.1016/j.anucene.2014.08.046>`_
@ -114,6 +159,10 @@ Nuclear Data
Parallelism
-----------
- Paul K. Romano, John R. Tramm, and Andrew R. Siegel, "Efficacy of hardware
threading for Monte Carlo particle transport calculations on multi- and
many-core systems," Accepted, *PHYSOR 2016*, Sun Valley, Idaho, May 1-5, 2016.
- David Ozog, Allen D. Malony, and Andrew R. Siegel, "A performance analysis of
SIMD algorithms for Monte Carlo simulations of nuclear reactor cores,"
*Proc. IEEE Int. Parallel and Distributed Processing Symposium*, Hyderabad,

View file

@ -0,0 +1,8 @@
.. _pythonapi_energy_groups:
=============
Energy Groups
=============
.. automodule:: openmc.mgxs.groups
:members:

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
.. _notebook_mgxs_part_i:
=========================
MGXS Part I: Introduction
=========================
.. only:: html
.. notebook:: mgxs-part-i.ipynb
.. only:: latex
IPython notebooks must be viewed in the online HTML documentation.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
.. _notebook_mgxs_part_ii:
===============================
MGXS Part II: Advanced Features
===============================
.. only:: html
.. notebook:: mgxs-part-ii.ipynb
.. only:: latex
IPython notebooks must be viewed in the online HTML documentation.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
.. _notebook_mgxs_part_iii:
========================
MGXS Part III: Libraries
========================
.. only:: html
.. notebook:: mgxs-part-iii.ipynb
.. only:: latex
IPython notebooks must be viewed in the online HTML documentation.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,13 @@
.. _notebook_post_processing:
===============
Post Processing
===============
.. only:: html
.. notebook:: post-processing.ipynb
.. only:: latex
IPython notebooks must be viewed in the online HTML documentation.

View file

@ -182,20 +182,19 @@
"# Create fuel Cell\n",
"fuel_cell = openmc.Cell(name='1.6% Fuel')\n",
"fuel_cell.fill = fuel\n",
"fuel_cell.add_surface(fuel_outer_radius, halfspace=-1)\n",
"fuel_cell.region = -fuel_outer_radius\n",
"pin_cell_universe.add_cell(fuel_cell)\n",
"\n",
"# Create a clad Cell\n",
"clad_cell = openmc.Cell(name='1.6% Clad')\n",
"clad_cell.fill = zircaloy\n",
"clad_cell.add_surface(fuel_outer_radius, halfspace=+1)\n",
"clad_cell.add_surface(clad_outer_radius, halfspace=-1)\n",
"clad_cell.region = +fuel_outer_radius & -clad_outer_radius\n",
"pin_cell_universe.add_cell(clad_cell)\n",
"\n",
"# Create a moderator Cell\n",
"moderator_cell = openmc.Cell(name='1.6% Moderator')\n",
"moderator_cell.fill = water\n",
"moderator_cell.add_surface(clad_outer_radius, halfspace=+1)\n",
"moderator_cell.region = +clad_outer_radius\n",
"pin_cell_universe.add_cell(moderator_cell)"
]
},
@ -219,12 +218,7 @@
"root_cell.fill = pin_cell_universe\n",
"\n",
"# Add boundary planes\n",
"root_cell.add_surface(min_x, halfspace=+1)\n",
"root_cell.add_surface(max_x, halfspace=-1)\n",
"root_cell.add_surface(min_y, halfspace=+1)\n",
"root_cell.add_surface(max_y, halfspace=-1)\n",
"root_cell.add_surface(min_z, halfspace=+1)\n",
"root_cell.add_surface(max_z, halfspace=-1)\n",
"root_cell.region = +min_x & -max_x & +min_y & -max_y & +min_z & -max_z\n",
"\n",
"# Create root Universe\n",
"root_universe = openmc.Universe(universe_id=0, name='root universe')\n",
@ -342,7 +336,18 @@
"metadata": {
"collapsed": false
},
"outputs": [],
"outputs": [
{
"data": {
"text/plain": [
"0"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Run openmc in plotting mode\n",
"executor = openmc.Executor()\n",
@ -358,7 +363,26 @@
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC\nAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAxQTFRF\n////chIS6YCRTb/E6kGE+wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAALKSURB\nVGje7dpLcqQwDAbgHHE2YeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmN\nP+HDhw8fPnz48Kf6VH9G+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4\nzPji99z0/AJ4n1lfvJ6fnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6\npA0wfln+ho/fwgYYn19C/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tN\nDbSGz7T0SBEWw4vLXzbQ6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X5\n8wZaxWd1+fMGiuFvir8bvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV\n873hB8UnM3xzANtf8nb4dwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7\nT/ppARBvp48UwJnelT5SACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4/\n/Jve+fhsH6Ctv7n8PTzjvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V\n32/o9+fl389Xnx+g5x/o+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6\n/4Le/6D3T/D9V67Y/ZsVQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/\ngPs/0P4TtP8F7r9J3AIO9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTu\nf4X7b+H+X7T/+BPuf3aM8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIw\nMTUtMDgtMDZUMTY6NDM6MTMrMDc6MDBtUQj+AAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTA4LTA2\nVDE2OjQzOjEzKzA3OjAwHAywQgAAAABJRU5ErkJggg==\n",
"image/png": [
"iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\n",
"AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\n",
"QYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB98LGQ4UM+6dthcAAALKSURBVGje7dpLcqQwDAbgHHE2\n",
"YeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n",
"+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\n",
"nl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n",
"/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n",
"6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\n",
"vjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\n",
"dwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\n",
"ACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\n",
"vY/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\n",
"QBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n",
"9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n",
"8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTUtMTEtMjVUMTQ6MjA6\n",
"NTEtMDg6MDDVsKLDAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE1LTExLTI1VDE0OjIwOjUxLTA4OjAw\n",
"pO0afwAAAABJRU5ErkJggg==\n"
],
"text/plain": [
"<IPython.core.display.Image object>"
]
@ -387,13 +411,12 @@
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": true
"collapsed": false
},
"outputs": [],
"source": [
"# Instantiate an empty TalliesFile\n",
"tallies_file = openmc.TalliesFile()\n",
"tallies_file.tallies = []"
"tallies_file = openmc.TalliesFile()"
]
},
{
@ -569,7 +592,9 @@
" Copyright: 2011-2015 Massachusetts Institute of Technology\n",
" License: http://mit-crpg.github.io/openmc/license.html\n",
" Version: 0.7.0\n",
" Date/Time: 2015-08-15 10:52:49\n",
" Git SHA1: 74ffcb447521c968fb64fdaa63e40598783f2fba\n",
" Date/Time: 2015-11-25 14:20:51\n",
" MPI Processes: 1\n",
"\n",
" ===========================================================================\n",
" ========================> INITIALIZATION <=========================\n",
@ -581,12 +606,13 @@
" Reading materials XML file...\n",
" Reading tallies XML file...\n",
" Building neighboring cells lists for each surface...\n",
" Loading ACE cross section table: 92235.71c\n",
" Loading ACE cross section table: 92238.71c\n",
" Loading ACE cross section table: 8016.71c\n",
" Loading ACE cross section table: 92235.71c\n",
" Loading ACE cross section table: 5010.71c\n",
" Loading ACE cross section table: 1001.71c\n",
" Loading ACE cross section table: 5010.71c\n",
" Loading ACE cross section table: 40090.71c\n",
" Maximum neutron transport energy: 20.0000 MeV for 92235.71c\n",
" Initializing source particles...\n",
"\n",
" ===========================================================================\n",
@ -595,26 +621,26 @@
"\n",
" Bat./Gen. k Average k \n",
" ========= ======== ==================== \n",
" 1/1 1.00465 \n",
" 2/1 1.05814 \n",
" 3/1 1.05114 \n",
" 4/1 1.09189 \n",
" 5/1 1.03731 \n",
" 6/1 1.03510 \n",
" 7/1 1.09378 1.06444 +/- 0.02934\n",
" 8/1 1.04522 1.05803 +/- 0.01811\n",
" 9/1 1.06557 1.05992 +/- 0.01294\n",
" 10/1 1.05757 1.05945 +/- 0.01004\n",
" 11/1 1.04858 1.05764 +/- 0.00839\n",
" 12/1 1.01832 1.05202 +/- 0.00905\n",
" 13/1 1.05822 1.05279 +/- 0.00787\n",
" 14/1 1.07684 1.05547 +/- 0.00744\n",
" 15/1 1.00349 1.05027 +/- 0.00844\n",
" 16/1 1.06969 1.05203 +/- 0.00784\n",
" 17/1 1.06377 1.05301 +/- 0.00722\n",
" 18/1 1.02897 1.05116 +/- 0.00690\n",
" 19/1 1.00685 1.04800 +/- 0.00713\n",
" 20/1 1.02644 1.04656 +/- 0.00679\n",
" 1/1 1.05992 \n",
" 2/1 1.05251 \n",
" 3/1 1.05204 \n",
" 4/1 1.02100 \n",
" 5/1 1.07784 \n",
" 6/1 1.04814 \n",
" 7/1 1.02335 1.03574 +/- 0.01239\n",
" 8/1 1.02415 1.03188 +/- 0.00813\n",
" 9/1 1.10331 1.04974 +/- 0.01876\n",
" 10/1 1.05452 1.05069 +/- 0.01456\n",
" 11/1 1.07867 1.05536 +/- 0.01277\n",
" 12/1 1.04203 1.05345 +/- 0.01096\n",
" 13/1 1.04482 1.05237 +/- 0.00955\n",
" 14/1 1.04116 1.05113 +/- 0.00852\n",
" 15/1 1.07569 1.05358 +/- 0.00800\n",
" 16/1 1.04188 1.05252 +/- 0.00732\n",
" 17/1 1.03775 1.05129 +/- 0.00679\n",
" 18/1 0.98462 1.04616 +/- 0.00808\n",
" 19/1 1.08613 1.04902 +/- 0.00801\n",
" 20/1 1.00571 1.04613 +/- 0.00800\n",
" Creating state point statepoint.20.h5...\n",
"\n",
" ===========================================================================\n",
@ -624,27 +650,27 @@
"\n",
" =======================> TIMING STATISTICS <=======================\n",
"\n",
" Total time for initialization = 4.4100E-01 seconds\n",
" Reading cross sections = 1.1300E-01 seconds\n",
" Total time in simulation = 1.8418E+01 seconds\n",
" Time in transport only = 1.8403E+01 seconds\n",
" Time in inactive batches = 2.1070E+00 seconds\n",
" Time in active batches = 1.6311E+01 seconds\n",
" Total time for initialization = 7.9600E-01 seconds\n",
" Reading cross sections = 2.1200E-01 seconds\n",
" Total time in simulation = 1.8740E+01 seconds\n",
" Time in transport only = 1.8727E+01 seconds\n",
" Time in inactive batches = 2.5970E+00 seconds\n",
" Time in active batches = 1.6143E+01 seconds\n",
" Time synchronizing fission bank = 2.0000E-03 seconds\n",
" Sampling source sites = 2.0000E-03 seconds\n",
" SEND/RECV source sites = 0.0000E+00 seconds\n",
" Sampling source sites = 1.0000E-03 seconds\n",
" SEND/RECV source sites = 1.0000E-03 seconds\n",
" Time accumulating tallies = 0.0000E+00 seconds\n",
" Total time for finalization = 1.0000E-03 seconds\n",
" Total time elapsed = 1.8861E+01 seconds\n",
" Calculation Rate (inactive) = 5932.61 neutrons/second\n",
" Calculation Rate (active) = 2299.06 neutrons/second\n",
" Total time for finalization = 2.0000E-03 seconds\n",
" Total time elapsed = 1.9553E+01 seconds\n",
" Calculation Rate (inactive) = 4813.25 neutrons/second\n",
" Calculation Rate (active) = 2322.99 neutrons/second\n",
"\n",
" ============================> RESULTS <============================\n",
"\n",
" k-effective (Collision) = 1.04599 +/- 0.00622\n",
" k-effective (Track-length) = 1.04656 +/- 0.00679\n",
" k-effective (Absorption) = 1.04614 +/- 0.00461\n",
" Combined k-effective = 1.04651 +/- 0.00368\n",
" k-effective (Collision) = 1.04597 +/- 0.00663\n",
" k-effective (Track-length) = 1.04613 +/- 0.00800\n",
" k-effective (Absorption) = 1.04087 +/- 0.00627\n",
" Combined k-effective = 1.04322 +/- 0.00570\n",
" Leakage Fraction = 0.00000 +/- 0.00000\n",
"\n"
]
@ -692,8 +718,7 @@
"outputs": [],
"source": [
"# Load the statepoint file\n",
"sp = StatePoint('statepoint.20.h5')\n",
"sp.read_results()"
"sp = StatePoint('statepoint.20.h5')"
]
},
{
@ -746,30 +771,22 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>total</td>\n",
" <td>(nu-fission / absorption)</td>\n",
" <td>1.042726</td>\n",
" <td>0.008661</td>\n",
" <td> total</td>\n",
" <td> (nu-fission / absorption)</td>\n",
" <td> 1.040687</td>\n",
" <td> 0.010913</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" nuclide score mean std. dev.\n",
"bin \n",
"0 total (nu-fission / absorption) 1.042726 0.008661"
" nuclide score mean std. dev.\n",
"0 total (nu-fission / absorption) 1.040687 0.010913"
]
},
"execution_count": 26,
@ -809,35 +826,29 @@
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>energy [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>total</td>\n",
" <td>absorption</td>\n",
" <td>0.958874</td>\n",
" <td>0.007146</td>\n",
" <td> (0.0e+00 - 6.2e-01)</td>\n",
" <td> total</td>\n",
" <td> absorption</td>\n",
" <td> 0.959302</td>\n",
" <td> 0.010033</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" nuclide score mean std. dev.\n",
"bin \n",
"0 total absorption 0.958874 0.007146"
" energy [MeV] nuclide score mean std. dev.\n",
"0 (0.0e+00 - 6.2e-01) total absorption 0.959302 0.010033"
]
},
"execution_count": 27,
@ -875,35 +886,29 @@
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>energy [MeV]</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>total</td>\n",
" <td>nu-fission</td>\n",
" <td>1.09186</td>\n",
" <td>0.010424</td>\n",
" <td> (0.0e+00 - 6.2e-01)</td>\n",
" <td> total</td>\n",
" <td> nu-fission</td>\n",
" <td> 1.09103</td>\n",
" <td> 0.012491</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" nuclide score mean std. dev.\n",
"bin \n",
"0 total nu-fission 1.09186 0.010424"
" energy [MeV] nuclide score mean std. dev.\n",
"0 (0.0e+00 - 6.2e-01) total nu-fission 1.09103 0.012491"
]
},
"execution_count": 28,
@ -949,25 +954,16 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.0e+00 - 6.2e-01</td>\n",
" <td>10000</td>\n",
" <td>total</td>\n",
" <td>absorption</td>\n",
" <td>0.802921</td>\n",
" <td>0.006109</td>\n",
" <td> (0.0e+00 - 6.2e-01)</td>\n",
" <td> 10000</td>\n",
" <td> total</td>\n",
" <td> absorption</td>\n",
" <td> 0.803182</td>\n",
" <td> 0.008664</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -975,8 +971,7 @@
],
"text/plain": [
" energy [MeV] cell nuclide score mean std. dev.\n",
"bin \n",
"0 0.0e+00 - 6.2e-01 10000 total absorption 0.802921 0.006109"
"0 (0.0e+00 - 6.2e-01) 10000 total absorption 0.803182 0.008664"
]
},
"execution_count": 29,
@ -1020,25 +1015,16 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.0e+00 - 6.2e-01</td>\n",
" <td>10000</td>\n",
" <td>total</td>\n",
" <td>(nu-fission / absorption)</td>\n",
" <td>1.240421</td>\n",
" <td>0.010978</td>\n",
" <td> (0.0e+00 - 6.2e-01)</td>\n",
" <td> 10000</td>\n",
" <td> total</td>\n",
" <td> (nu-fission / absorption)</td>\n",
" <td> 1.237982</td>\n",
" <td> 0.014179</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
@ -1046,12 +1032,10 @@
],
"text/plain": [
" energy [MeV] cell nuclide score mean \\\n",
"bin \n",
"0 0.0e+00 - 6.2e-01 10000 total (nu-fission / absorption) 1.240421 \n",
"0 (0.0e+00 - 6.2e-01) 10000 total (nu-fission / absorption) 1.237982 \n",
"\n",
" std. dev. \n",
"bin \n",
"0 0.010978 "
" std. dev. \n",
"0 0.014179 "
]
},
"execution_count": 30,
@ -1087,39 +1071,34 @@
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>energy [MeV]</th>\n",
" <th>cell</th>\n",
" <th>nuclide</th>\n",
" <th>score</th>\n",
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>total</td>\n",
" <td>(((absorption * nu-fission) * absorption) * (n...</td>\n",
" <td>1.042726</td>\n",
" <td>0.017538</td>\n",
" <td> (0.0e+00 - 6.2e-01)</td>\n",
" <td> 10000</td>\n",
" <td> total</td>\n",
" <td> (((absorption * nu-fission) * absorption) * (n...</td>\n",
" <td> 1.040687</td>\n",
" <td> 0.022989</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" nuclide score mean \\\n",
"bin \n",
"0 total (((absorption * nu-fission) * absorption) * (n... 1.042726 \n",
" energy [MeV] cell nuclide \\\n",
"0 (0.0e+00 - 6.2e-01) 10000 total \n",
"\n",
" std. dev. \n",
"bin \n",
"0 0.017538 "
" score mean std. dev. \n",
"0 (((absorption * nu-fission) * absorption) * (n... 1.040687 0.022989 "
]
},
"execution_count": 31,
@ -1179,115 +1158,104 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>(U-238 / total)</td>\n",
" <td>(nu-fission / flux)</td>\n",
" <td>0.000001</td>\n",
" <td>6.985151e-09</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> (U-238 / total)</td>\n",
" <td> (nu-fission / flux)</td>\n",
" <td> 0.000001</td>\n",
" <td> 8.078651e-09</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>(U-238 / total)</td>\n",
" <td>(scatter / flux)</td>\n",
" <td>0.209988</td>\n",
" <td>2.206753e-03</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> (U-238 / total)</td>\n",
" <td> (scatter / flux)</td>\n",
" <td> 0.209990</td>\n",
" <td> 2.449396e-03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>(U-235 / total)</td>\n",
" <td>(nu-fission / flux)</td>\n",
" <td>0.355276</td>\n",
" <td>3.741612e-03</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> (U-235 / total)</td>\n",
" <td> (nu-fission / flux)</td>\n",
" <td> 0.356117</td>\n",
" <td> 4.364366e-03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>(U-235 / total)</td>\n",
" <td>(scatter / flux)</td>\n",
" <td>0.005555</td>\n",
" <td>5.842517e-05</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> (U-235 / total)</td>\n",
" <td> (scatter / flux)</td>\n",
" <td> 0.005555</td>\n",
" <td> 6.495710e-05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>(U-238 / total)</td>\n",
" <td>(nu-fission / flux)</td>\n",
" <td>0.007229</td>\n",
" <td>5.951357e-05</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> (U-238 / total)</td>\n",
" <td> (nu-fission / flux)</td>\n",
" <td> 0.007190</td>\n",
" <td> 7.596666e-05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>(U-238 / total)</td>\n",
" <td>(scatter / flux)</td>\n",
" <td>0.227642</td>\n",
" <td>9.496469e-04</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> (U-238 / total)</td>\n",
" <td> (scatter / flux)</td>\n",
" <td> 0.227843</td>\n",
" <td> 1.024510e-03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>(U-235 / total)</td>\n",
" <td>(nu-fission / flux)</td>\n",
" <td>0.008076</td>\n",
" <td>5.699123e-05</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> (U-235 / total)</td>\n",
" <td> (nu-fission / flux)</td>\n",
" <td> 0.008086</td>\n",
" <td> 6.251590e-05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>(U-235 / total)</td>\n",
" <td>(scatter / flux)</td>\n",
" <td>0.003369</td>\n",
" <td>1.369755e-05</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> (U-235 / total)</td>\n",
" <td> (scatter / flux)</td>\n",
" <td> 0.003365</td>\n",
" <td> 1.646663e-05</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide score mean \\\n",
"bin \n",
"0 10000 0.0e+00 - 6.3e-07 (U-238 / total) (nu-fission / flux) 0.000001 \n",
"1 10000 0.0e+00 - 6.3e-07 (U-238 / total) (scatter / flux) 0.209988 \n",
"2 10000 0.0e+00 - 6.3e-07 (U-235 / total) (nu-fission / flux) 0.355276 \n",
"3 10000 0.0e+00 - 6.3e-07 (U-235 / total) (scatter / flux) 0.005555 \n",
"4 10000 6.3e-07 - 2.0e+01 (U-238 / total) (nu-fission / flux) 0.007229 \n",
"5 10000 6.3e-07 - 2.0e+01 (U-238 / total) (scatter / flux) 0.227642 \n",
"6 10000 6.3e-07 - 2.0e+01 (U-235 / total) (nu-fission / flux) 0.008076 \n",
"7 10000 6.3e-07 - 2.0e+01 (U-235 / total) (scatter / flux) 0.003369 \n",
" cell energy [MeV] nuclide score mean \\\n",
"0 10000 (0.0e+00 - 6.3e-07) (U-238 / total) (nu-fission / flux) 0.000001 \n",
"1 10000 (0.0e+00 - 6.3e-07) (U-238 / total) (scatter / flux) 0.209990 \n",
"2 10000 (0.0e+00 - 6.3e-07) (U-235 / total) (nu-fission / flux) 0.356117 \n",
"3 10000 (0.0e+00 - 6.3e-07) (U-235 / total) (scatter / flux) 0.005555 \n",
"4 10000 (6.3e-07 - 2.0e+01) (U-238 / total) (nu-fission / flux) 0.007190 \n",
"5 10000 (6.3e-07 - 2.0e+01) (U-238 / total) (scatter / flux) 0.227843 \n",
"6 10000 (6.3e-07 - 2.0e+01) (U-235 / total) (nu-fission / flux) 0.008086 \n",
"7 10000 (6.3e-07 - 2.0e+01) (U-235 / total) (scatter / flux) 0.003365 \n",
"\n",
" std. dev. \n",
"bin \n",
"0 6.985151e-09 \n",
"1 2.206753e-03 \n",
"2 3.741612e-03 \n",
"3 5.842517e-05 \n",
"4 5.951357e-05 \n",
"5 9.496469e-04 \n",
"6 5.699123e-05 \n",
"7 1.369755e-05 "
" std. dev. \n",
"0 8.078651e-09 \n",
"1 2.449396e-03 \n",
"2 4.364366e-03 \n",
"3 6.495710e-05 \n",
"4 7.596666e-05 \n",
"5 1.024510e-03 \n",
"6 6.251590e-05 \n",
"7 1.646663e-05 "
]
},
"execution_count": 33,
@ -1318,11 +1286,11 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[[ 6.63809296e-07]\n",
" [ 3.55275544e-01]]\n",
"[[[ 6.65302296e-07]\n",
" [ 3.56116716e-01]]\n",
"\n",
" [[ 7.22895528e-03]\n",
" [ 8.07565148e-03]]]\n"
" [[ 7.19004460e-03]\n",
" [ 8.08598751e-03]]]\n"
]
}
],
@ -1350,9 +1318,9 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[[ 0.00555505]]\n",
"[[[ 0.00555516]]\n",
"\n",
" [[ 0.0033688 ]]]\n"
" [[ 0.00336498]]]\n"
]
}
],
@ -1374,8 +1342,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"[[[ 0.2276418]\n",
" [ 0.0033688]]]\n"
"[[[ 0.22784316]\n",
" [ 0.00336498]]]\n"
]
}
],
@ -1416,64 +1384,54 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>U-238</td>\n",
" <td>nu-fission</td>\n",
" <td>0.000002</td>\n",
" <td>1.211808e-08</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> U-238</td>\n",
" <td> nu-fission</td>\n",
" <td> 0.000002</td>\n",
" <td> 1.450189e-08</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10000</td>\n",
" <td>0.0e+00 - 6.3e-07</td>\n",
" <td>U-235</td>\n",
" <td>nu-fission</td>\n",
" <td>0.870360</td>\n",
" <td>6.496431e-03</td>\n",
" <td> 10000</td>\n",
" <td> (0.0e+00 - 6.3e-07)</td>\n",
" <td> U-235</td>\n",
" <td> nu-fission</td>\n",
" <td> 0.870882</td>\n",
" <td> 7.895515e-03</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>U-238</td>\n",
" <td>nu-fission</td>\n",
" <td>0.083226</td>\n",
" <td>6.367951e-04</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> U-238</td>\n",
" <td> nu-fission</td>\n",
" <td> 0.082484</td>\n",
" <td> 8.253437e-04</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10000</td>\n",
" <td>6.3e-07 - 2.0e+01</td>\n",
" <td>U-235</td>\n",
" <td>nu-fission</td>\n",
" <td>0.092974</td>\n",
" <td>5.921990e-04</td>\n",
" <td> 10000</td>\n",
" <td> (6.3e-07 - 2.0e+01)</td>\n",
" <td> U-235</td>\n",
" <td> nu-fission</td>\n",
" <td> 0.092762</td>\n",
" <td> 6.444580e-04</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide score mean std. dev.\n",
"bin \n",
"0 10000 0.0e+00 - 6.3e-07 U-238 nu-fission 0.000002 1.211808e-08\n",
"1 10000 0.0e+00 - 6.3e-07 U-235 nu-fission 0.870360 6.496431e-03\n",
"2 10000 6.3e-07 - 2.0e+01 U-238 nu-fission 0.083226 6.367951e-04\n",
"3 10000 6.3e-07 - 2.0e+01 U-235 nu-fission 0.092974 5.921990e-04"
" cell energy [MeV] nuclide score mean std. dev.\n",
"0 10000 (0.0e+00 - 6.3e-07) U-238 nu-fission 0.000002 1.450189e-08\n",
"1 10000 (0.0e+00 - 6.3e-07) U-235 nu-fission 0.870882 7.895515e-03\n",
"2 10000 (6.3e-07 - 2.0e+01) U-238 nu-fission 0.082484 8.253437e-04\n",
"3 10000 (6.3e-07 - 2.0e+01) U-235 nu-fission 0.092762 6.444580e-04"
]
},
"execution_count": 37,
@ -1509,114 +1467,104 @@
" <th>mean</th>\n",
" <th>std. dev.</th>\n",
" </tr>\n",
" <tr>\n",
" <th>bin</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10002</td>\n",
" <td>1.0e-08 - 1.1e-07</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>4.638428</td>\n",
" <td>0.034134</td>\n",
" <td> 10002</td>\n",
" <td> (1.0e-08 - 1.1e-07)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 4.630154</td>\n",
" <td> 0.044512</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10002</td>\n",
" <td>1.1e-07 - 1.2e-06</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>2.050818</td>\n",
" <td>0.010745</td>\n",
" <td> 10002</td>\n",
" <td> (1.1e-07 - 1.2e-06)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 2.042984</td>\n",
" <td> 0.011429</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10002</td>\n",
" <td>1.2e-06 - 1.3e-05</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>1.656905</td>\n",
" <td>0.009480</td>\n",
" <td> 10002</td>\n",
" <td> (1.2e-06 - 1.3e-05)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 1.657517</td>\n",
" <td> 0.008617</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10002</td>\n",
" <td>1.3e-05 - 1.4e-04</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>1.870808</td>\n",
" <td>0.011883</td>\n",
" <td> 10002</td>\n",
" <td> (1.3e-05 - 1.4e-04)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 1.863326</td>\n",
" <td> 0.008848</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10002</td>\n",
" <td>1.4e-04 - 1.5e-03</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>2.045621</td>\n",
" <td>0.011414</td>\n",
" <td> 10002</td>\n",
" <td> (1.4e-04 - 1.5e-03)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 2.043916</td>\n",
" <td> 0.014195</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>10002</td>\n",
" <td>1.5e-03 - 1.6e-02</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>2.163297</td>\n",
" <td>0.008725</td>\n",
" <td> 10002</td>\n",
" <td> (1.5e-03 - 1.6e-02)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 2.134458</td>\n",
" <td> 0.007561</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>10002</td>\n",
" <td>1.6e-02 - 1.7e-01</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>2.202045</td>\n",
" <td>0.013500</td>\n",
" <td> 10002</td>\n",
" <td> (1.6e-02 - 1.7e-01)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 2.209947</td>\n",
" <td> 0.013848</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>10002</td>\n",
" <td>1.7e-01 - 1.9e+00</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>1.996977</td>\n",
" <td>0.010791</td>\n",
" <td> 10002</td>\n",
" <td> (1.7e-01 - 1.9e+00)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 2.006967</td>\n",
" <td> 0.009368</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>10002</td>\n",
" <td>1.9e+00 - 2.0e+01</td>\n",
" <td>H-1</td>\n",
" <td>scatter</td>\n",
" <td>0.370890</td>\n",
" <td>0.003597</td>\n",
" <td> 10002</td>\n",
" <td> (1.9e+00 - 2.0e+01)</td>\n",
" <td> H-1</td>\n",
" <td> scatter</td>\n",
" <td> 0.373895</td>\n",
" <td> 0.002964</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" cell energy [MeV] nuclide score mean std. dev.\n",
"bin \n",
"0 10002 1.0e-08 - 1.1e-07 H-1 scatter 4.638428 0.034134\n",
"1 10002 1.1e-07 - 1.2e-06 H-1 scatter 2.050818 0.010745\n",
"2 10002 1.2e-06 - 1.3e-05 H-1 scatter 1.656905 0.009480\n",
"3 10002 1.3e-05 - 1.4e-04 H-1 scatter 1.870808 0.011883\n",
"4 10002 1.4e-04 - 1.5e-03 H-1 scatter 2.045621 0.011414\n",
"5 10002 1.5e-03 - 1.6e-02 H-1 scatter 2.163297 0.008725\n",
"6 10002 1.6e-02 - 1.7e-01 H-1 scatter 2.202045 0.013500\n",
"7 10002 1.7e-01 - 1.9e+00 H-1 scatter 1.996977 0.010791\n",
"8 10002 1.9e+00 - 2.0e+01 H-1 scatter 0.370890 0.003597"
" cell energy [MeV] nuclide score mean std. dev.\n",
"0 10002 (1.0e-08 - 1.1e-07) H-1 scatter 4.630154 0.044512\n",
"1 10002 (1.1e-07 - 1.2e-06) H-1 scatter 2.042984 0.011429\n",
"2 10002 (1.2e-06 - 1.3e-05) H-1 scatter 1.657517 0.008617\n",
"3 10002 (1.3e-05 - 1.4e-04) H-1 scatter 1.863326 0.008848\n",
"4 10002 (1.4e-04 - 1.5e-03) H-1 scatter 2.043916 0.014195\n",
"5 10002 (1.5e-03 - 1.6e-02) H-1 scatter 2.134458 0.007561\n",
"6 10002 (1.6e-02 - 1.7e-01) H-1 scatter 2.209947 0.013848\n",
"7 10002 (1.7e-01 - 1.9e+00) H-1 scatter 2.006967 0.009368\n",
"8 10002 (1.9e+00 - 2.0e+01) H-1 scatter 0.373895 0.002964"
]
},
"execution_count": 38,
@ -1649,7 +1597,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.8"
"version": "2.7.10"
}
},
"nbformat": 4,

View file

@ -57,13 +57,26 @@ on a given module or class.
summary
tallies
**Multi-Group Cross Section Generation**
.. toctree::
:maxdepth: 1
mgxs
energy_groups
mgxs_library
**Example Jupyter Notebooks:**
.. toctree::
:maxdepth: 1
examples/post-processing
examples/pandas-dataframes
examples/tally-arithmetic
examples/mgxs-part-i
examples/mgxs-part-ii
examples/mgxs-part-iii
.. _Jupyter: https://jupyter.org/
.. _NumPy: http://www.numpy.org/

View file

@ -0,0 +1,66 @@
.. _pythonapi_mgxs:
==========================
Multi-Group Cross Sections
==========================
.. currentmodule:: openmc.mgxs.mgxs
----------------------------
Summary of Available Classes
----------------------------
.. autosummary::
MGXS
AbsorptionXS
CaptureXS
Chi
FissionXS
NuFissionXS
NuScatterXS
NuScatterMatrixXS
ScatterXS
ScatterMatrixXS
TotalXS
TransportXS
-------------------
Class Documentation
-------------------
.. autoclass:: MGXS
:members:
.. autoclass:: AbsorptionXS
:members:
.. autoclass:: CaptureXS
:members:
.. autoclass:: Chi
:members:
.. autoclass:: FissionXS
:members:
.. autoclass:: NuFissionXS
:members:
.. autoclass:: NuScatterXS
:members:
.. autoclass:: NuScatterMatrixXS
:members:
.. autoclass:: ScatterXS
:members:
.. autoclass:: ScatterMatrixXS
:members:
.. autoclass:: TotalXS
:members:
.. autoclass:: TransportXS
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_mgxs_library:
============
MGXS Library
============
.. automodule:: openmc.mgxs.library
:members:

View file

@ -35,8 +35,8 @@ Installing from Source on Linux or Mac OS X
-------------------------------------------
All OpenMC source code is hosted on GitHub_. If you have git_, the gfortran_
compiler, and CMake_ installed, you can download and install OpenMC be entering
the following commands in a terminal:
compiler, CMake_, and HDF5_ installed, you can download and install OpenMC be
entering the following commands in a terminal:
.. code-block:: sh

View file

@ -1,9 +1,30 @@
.. _releasenotes:
==============================
Release Notes for OpenMC 0.7.0
Release Notes for OpenMC 0.7.1
==============================
This release of OpenMC provides some substantial improvements over version
0.7.0. Non-simple cell regions can now be defined through the ``|`` (union) and
``~`` (complement) operators. Similar changes in the Python API also allow
complex cell regions to be defined. A true secondary particle bank now exists;
this is crucial for photon transport (to be added in the next minor release). A
rich API for multi-group cross section generation has been added via the
``openmc.mgxs`` Python module.
Various improvements to tallies have also been made. It is now possible to
explicitly specify that a collision estimator be used in a tally. A new
``delayedgroup`` filter and ``delayed-nu-fission`` score allow a user to obtain
delayed fission neutron production rates filtered by delayed group. Finally, the
new ``inverse-velocity`` score may be useful for calculating kinetics
parameters.
.. caution:: In previous versions, depending on how OpenMC was compiled binary
output was either given in HDF5 or a flat binary format. With this
version, all binary output is now HDF5 which means you **must**
have HDF5 in order to install OpenMC. Please consult the user's
guide for instructions on how to compile with HDF5.
-------------------
System Requirements
-------------------
@ -17,36 +38,41 @@ the problem at hand (mostly on the number of nuclides in the problem).
New Features
------------
- Complete Python API
- Python 3 compatability for all scripts
- All scripts consistently named openmc-* and installed together
- New 'distribcell' tally filter for repeated cells
- Ability to specify outer lattice universe
- XML input validation utility (openmc-validate-xml)
- Support for hexagonal lattices
- Material union energy grid method
- Tally triggers
- Remove dependence on PETSc
- Significant OpenMP performance improvements
- Support for Fortran 2008 MPI interface
- Use of Travis CI for continuous integration
- Simplifications and improvements to test suite
- Support for complex cell regions (union and complement operators)
- Generic quadric surface type
- Improved handling of secondary particles
- Binary output is now solely HDF5
- ``openmc.mgxs`` Python module enabling multi-group cross section generation
- Collision estimator for tallies
- Delayed fission neutron production tallies with ability to filter by delayed
group
- Inverse velocity tally score
- Performance improvements for binary search
- Performance improvements for reaction rate tallies
---------
Bug Fixes
---------
- b5f712_: Fix bug in spherical harmonics tallies
- e6675b_: Ensure all constants are double precision
- 04e2c1_: Fix potential bug in sample_nuclide routine
- 6121d9_: Fix bugs related to particle track files
- 2f0e89_: Fixes for nuclide specification in tallies
- 299322_: Bug with material filter when void material present
- d74840_: Fix triggers on tallies with multiple filters
- c29a81_: Correctly handle maximum transport energy
- 3edc23_: Fixes in the nu-scatter score
- 629e3b_: Assume unspecified surface coefficients are zero in Python API
- 5dbe8b_: Fix energy filters for openmc-plot-mesh-tally
- ff66f4_: Fixes in the openmc-plot-mesh-tally script
- 441fd4_: Fix bug in kappa-fission score
- 7e5974_: Allow fixed source simulations from Python API
.. _b5f712: https://github.com/mit-crpg/openmc/commit/b5f712
.. _e6675b: https://github.com/mit-crpg/openmc/commit/e6675b
.. _04e2c1: https://github.com/mit-crpg/openmc/commit/04e2c1
.. _6121d9: https://github.com/mit-crpg/openmc/commit/6121d9
.. _2f0e89: https://github.com/mit-crpg/openmc/commit/2f0e89
.. _299322: https://github.com/mit-crpg/openmc/commit/299322
.. _d74840: https://github.com/mit-crpg/openmc/commit/d74840
.. _c29a81: https://github.com/mit-crpg/openmc/commit/c29a81
.. _3edc23: https://github.com/mit-crpg/openmc/commit/3edc23
.. _629e3b: https://github.com/mit-crpg/openmc/commit/629e3b
.. _5dbe8b: https://github.com/mit-crpg/openmc/commit/5dbe8b
.. _ff66f4: https://github.com/mit-crpg/openmc/commit/ff66f4
.. _441fd4: https://github.com/mit-crpg/openmc/commit/441fd4
.. _7e5974: https://github.com/mit-crpg/openmc/commit/7e5974
------------
Contributors
@ -55,13 +81,11 @@ Contributors
This release contains new contributions from the following people:
- `Will Boyd <wbinventor@gmail.com>`_
- `Matt Ellis <mellis13@mit.edu>`_
- `Sterling Harper <sterlingmharper@mit.edu>`_
- `Bryan Herman <bherman@mit.edu>`_
- `Nicholas Horelik <nicholas.horelik@gmail.com>`_
- `Bryan Herman <hermab53@gmail.com>`_
- `Colin Josey <cjosey@mit.edu>`_
- `William Lyu <PaleNeutron@users.noreply.github.com>`_
- `Adam Nelson <nelsonag@umich.edu>`_
- `Paul Romano <paul.k.romano@gmail.com>`_
- `Anthony Scopatz <scopatz@gmail.com>`_
- `Kelly Rowland <kellylynnerowland@gmail.com>`_
- `Sam Shaner <samuelshaner@gmail.com>`_
- `Jon Walsh <walshjon@mit.edu>`_

View file

@ -5,7 +5,7 @@ User's Guide
============
Welcome to the OpenMC User's Guide! This tutorial will guide you through the
essential aspects of using OpenMC to perform neutronic simulations.
essential aspects of using OpenMC to perform simulations.
.. toctree::
:numbered:
@ -14,5 +14,6 @@ essential aspects of using OpenMC to perform neutronic simulations.
beginners
install
input
output/index
processing
troubleshoot

View file

@ -79,14 +79,13 @@ Message Description
[VALID] XML file matches RelaxNG.
======================== ===================================
As an example, if OpenMC is installed in the directory
``/opt/openmc/0.6.2`` and the current working directory is where
OpenMC XML input files are located, they can be validated using
the following command:
As an example, if OpenMC is installed in the directory ``/opt/openmc/`` and the
current working directory is where OpenMC XML input files are located, they can
be validated using the following command:
.. code-block:: bash
/opt/openmc/0.6.2/bin/xml_validate
/opt/openmc/bin/openmc-validate-xml
--------------------------------------
Settings Specification -- settings.xml
@ -721,9 +720,8 @@ Geometry Specification -- geometry.xml
The geometry in OpenMC is described using `constructive solid geometry`_ (CSG),
also sometimes referred to as combinatorial geometry. CSG allows a user to
create complex objects using Boolean operators on a set of simpler surfaces. In
the geometry model, each unique closed volume in defined by its bounding
surfaces. In OpenMC, most `quadratic surfaces`_ can be modeled and used as
bounding surfaces.
the geometry model, each unique volume is defined by its bounding surfaces. In
OpenMC, most `quadratic surfaces`_ can be modeled and used as bounding surfaces.
Every geometry.xml must have an XML declaration at the beginning of the file and
a root element named geometry. Within the root element the user can define any
@ -746,7 +744,7 @@ number of cells, surfaces, and lattices. Let us look at the following example:
<id>1</id>
<universe>0</universe>
<material>1</material>
<surfaces>-1</surfaces>
<region>-1</region>
</cell>
</geometry>
@ -764,7 +762,7 @@ could be written as:
<!-- This is a comment -->
<surface id="1" type="sphere" coeffs="0.0 0.0 0.0 5.0" boundary="vacuum" />
<cell id="1" universe="0" material="1" surfaces="-1" />
<cell id="1" universe="0" material="1" region="-1" />
</geometry>
@ -788,7 +786,8 @@ Each ``<surface>`` element can have the following attributes or sub-elements:
:type:
The type of the surfaces. This can be "x-plane", "y-plane", "z-plane",
"plane", "x-cylinder", "y-cylinder", "z-cylinder", or "sphere".
"plane", "x-cylinder", "y-cylinder", "z-cylinder", "sphere", "x-cone",
"y-cone", "z-cone", or "quadric".
*Default*: None
@ -856,6 +855,12 @@ The following quadratic surfaces can be modeled:
R^2 (z - z_0)^2`. The coefficients specified are ":math:`x_0 \: y_0 \: z_0
\: R^2`".
:quadric:
A general quadric surface of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy +
Eyz + Fxz + Gx + Hy + Jz + K = 0` The coefficients specified are ":math:`A
\: B \: C \: D \: E \: F \: G \: H \: J \: K`".
``<cell>`` Element
------------------
@ -892,15 +897,29 @@ Each ``<cell>`` element can have the following attributes or sub-elements:
*Default*: None
:surfaces:
A list of the ``ids`` for surfaces that bound this cell, e.g. if the cell
is on the negative side of surface 3 and the positive side of surface 5, the
bounding surfaces would be given as "-3 5".
:region:
A Boolean expression of half-spaces that defines the spatial region which
the cell occupies. Each half-space is identified by the unique ID of the
surface prefixed by `-` or `+` to indicate that it is the negative or
positive half-space, respectively. The `+` sign for a positive half-space
can be omitted. Valid Boolean operators are parentheses, union `|`,
complement `~`, and intersection. Intersection is implicit and indicated by
the presence of whitespace. The order of operator precedence is parentheses,
complement, intersection, and then union.
.. note:: The surface attribute/element can be omitted to make a cell fill
its entire universe.
As an example, the following code gives a cell that is the union of the
negative half-space of surface 3 and the complement of the intersection of
the positive half-space of surface 5 and the negative half-space of surface
2:
*Default*: No surfaces
.. code-block:: xml
<cell id="1" material="1" region="-3 | ~(5 -2)" />
.. note:: The ``region`` attribute/element can be omitted to make a cell
fill its entire universe.
*Default*: A region filling all space.
:rotation:
If the cell is filled with a universe, this element specifies the angles in
@ -1095,8 +1114,10 @@ Each ``material`` element can have the following attributes or sub-elements:
An element with attributes/sub-elements called ``value`` and ``units``. The
``value`` attribute is the numeric value of the density while the ``units``
can be "g/cm3", "kg/m3", "atom/b-cm", "atom/cm3", or "sum". The "sum" unit
indicates that the density should be calculated as the sum of the atom
fractions for each nuclide in the material. This should not be used in
indicates that values appearing in ``ao`` attributes for ``<nuclide>`` and
``<element>`` sub-elements are to be interpreted as nuclide/element
densities in atom/b-cm, and the total density of the material is taken as
the sum of all nuclides/elements. The "sum" option cannot be used in
conjunction with weight percents.
*Default*: None
@ -1117,6 +1138,15 @@ Each ``material`` element can have the following attributes or sub-elements:
.. note:: If one nuclide is specified in atom percent, all others must also
be given in atom percent. The same applies for weight percentages.
An optional attribute/sub-element for each nuclide is ``scattering``. This
attribute may be set to "data" to use the scattering laws specified by the
cross section library (default). Alternatively, when set to "iso-in-lab",
the scattering laws are used to sample the outgoing energy but an
isotropic-in-lab distribution is used to sample the outgoing angle at each
scattering interaction. The ``scattering`` attribute may be most useful
when using OpenMC to compute multi-group cross-sections for deterministic
transport codes and to quantify the effects of anisotropic scattering.
*Default*: None
:element:
@ -1143,6 +1173,16 @@ Each ``material`` element can have the following attributes or sub-elements:
*Default*: None
An optional attribute/sub-element for each element is ``scattering``. This
attribute may be set to "data" to use the scattering laws specified by the
cross section library (default). Alternatively, when set to "iso-in-lab",
the scattering laws are used to sample the outgoing energy but an
isotropic-in-lab distribution is used to sample the outgoing angle at each
scattering interaction. The ``scattering`` attribute may be most useful
when using OpenMC to compute multi-group cross-sections for deterministic
transport codes and to quantify the effects of anisotropic scattering.
*Default*: None
:sab:
Associates an S(a,b) table with the material. This element has
@ -1214,8 +1254,8 @@ The ``<tally>`` element accepts the following sub-elements:
:type:
The type of the filter. Accepted options are "cell", "cellborn",
"material", "universe", "energy", "energyout", "mesh", and
"distribcell".
"material", "universe", "energy", "energyout", "mesh", "distribcell",
and "delayedgroup".
:bins:
For each filter type, the corresponding ``bins`` entry is given as
@ -1240,17 +1280,87 @@ The ``<tally>`` element accepts the following sub-elements:
:energy:
A monotonically increasing list of bounding **pre-collision** energies
for a number of groups. For example, if this filter is specified as
``<filter type="energy" bins="0.0 1.0 20.0" />``, then two energy bins
will be created, one with energies between 0 and 1 MeV and the other
with energies between 1 and 20 MeV.
.. code-block:: xml
<filter type="energy" bins="0.0 1.0 20.0" />
then two energy bins will be created, one with energies between 0 and
1 MeV and the other with energies between 1 and 20 MeV.
:energyout:
A monotonically increasing list of bounding **post-collision**
energies for a number of groups. For example, if this filter is
specified as ``<filter type="energyout" bins="0.0 1.0 20.0" />``, then
two post-collision energy bins will be created, one with energies
specified as
.. code-block:: xml
<filter type="energyout" bins="0.0 1.0 20.0" />
then two post-collision energy bins will be created, one with energies
between 0 and 1 MeV and the other with energies between 1 and 20 MeV.
:mu:
A monotonically increasing list of bounding **post-collision** cosines
of the change in a particle's angle (i.e., :math:`\mu = \hat{\Omega}
\cdot \hat{\Omega}'`), which represents a portion of the possible
values of :math:`[-1,1]`. For example, spanning all of :math:`[-1,1]`
with five equi-width bins can be specified as:
.. code-block:: xml
<filter type="mu" bins="-1.0 -0.6 -0.2 0.2 0.6 1.0" />
Alternatively, if only one value is provided as a bin, OpenMC will
interpret this to mean the complete range of :math:`[-1,1]` should
be automatically subdivided in to the provided value for the bin.
That is, the above example of five equi-width bins spanning
:math:`[-1,1]` can be instead written as:
.. code-block:: xml
<filter type="mu" bins="5" />
:polar:
A monotonically increasing list of bounding particle polar angles
which represents a portion of the possible values of :math:`[0,\pi]`.
For example, spanning all of :math:`[0,\pi]` with five equi-width
bins can be specified as:
.. code-block:: xml
<filter type="polar" bins="0.0 0.6283 1.2566 1.8850 2.5132 3.1416"/>
Alternatively, if only one value is provided as a bin, OpenMC will
interpret this to mean the complete range of :math:`[0,\pi]` should
be automatically subdivided in to the provided value for the bin.
That is, the above example of five equi-width bins spanning
:math:`[0,\pi]` can be instead written as:
.. code-block:: xml
<filter type="polar" bins="5" />
:azimuthal:
A monotonically increasing list of bounding particle azimuthal angles
which represents a portion of the possible values of :math:`[-\pi,\pi)`.
For example, spanning all of :math:`[-\pi,\pi)` with two equi-width
bins can be specified as:
.. code-block:: xml
<filter type="azimuthal" bins="0.0 3.1416 6.2832" />
Alternatively, if only one value is provided as a bin, OpenMC will
interpret this to mean the complete range of :math:`[-\pi,\pi)` should
be automatically subdivided in to the provided value for the bin.
That is, the above example of five equi-width bins spanning
:math:`[-\pi,\pi)` can be instead written as:
.. code-block:: xml
<filter type="azimuthal" bins="2" />
:mesh:
The ``id`` of a structured mesh to be tallied over.
@ -1263,6 +1373,15 @@ The ``<tally>`` element accepts the following sub-elements:
not accept more than one cell ID. It is not recommended to combine
this filter with a cell or mesh filter.
:delayedgroup:
A list of delayed neutron precursor groups for which the tally should
be accumulated. For instance, to tally to all 6 delayed groups in the
ENDF/B-VII.1 library the filter is specified as:
.. code-block:: xml
<filter type="delayedgroup" bins="1 2 3 4 5 6" />
:nuclides:
If specified, the scores listed will be for particular nuclides, not the
summation of reactions from all nuclides. The format for nuclides should be
@ -1278,26 +1397,32 @@ The ``<tally>`` element accepts the following sub-elements:
*Default*: total
:estimator:
The estimator element is used to force the use of either ``analog`` or
``tracklength`` tally estimation. ''analog'' is generally less efficient
though it can be used with every score type. ''tracklength'' is generally
the most efficient, though its usage is restricted to tallies that do not
score particle information which requires a collision to have occured, such
as a scattering tally which utilizes outgoing energy filters.
The estimator element is used to force the use of either ``analog``,
``collision``, or ``tracklength`` tally estimation. ``analog`` is generally
the least efficient though it can be used with every score type.
``tracklength`` is generally the most efficient, but neither ``tracklength``
nor ``collision`` can be used to score a tally that requires post-collision
information. For example, a scattering tally with outgoing energy filters
cannot be used with ``tracklength`` or ``collision`` because the code will
not know the outgoing energy distribution.
*Default*: ``tracklength`` but will revert to analog if necessary.
*Default*: ``tracklength`` but will revert to ``analog`` if necessary.
:scores:
A space-separated list of the desired responses to be accumulated. Accepted
options are "flux", "total", "scatter", "absorption", "fission",
"nu-fission", "kappa-fission", "nu-scatter", "scatter-N", "scatter-PN",
"scatter-YN", "nu-scatter-N", "nu-scatter-PN", "nu-scatter-YN", "flux-YN",
"total-YN", "current", and "events". These corresponding to the following
physical quantities:
"nu-fission", "delayed-nu-fission", "kappa-fission", "nu-scatter",
"scatter-N", "scatter-PN", "scatter-YN", "nu-scatter-N", "nu-scatter-PN",
"nu-scatter-YN", "flux-YN", "total-YN", "current", "inverse-velocity" and
"events". These correspond to the following physical quantities:
:flux:
Total flux in particle-cm per source particle.
.. note::
The ``analog`` estimator is actually identical to the ``collision``
estimator for the flux score.
:total:
Total reaction rate in reactions per source particle.
@ -1316,6 +1441,10 @@ The ``<tally>`` element accepts the following sub-elements:
Total production of neutrons due to fission. Units are neutrons produced
per source neutron.
:delayed-nu-fission:
Total production of delayed neutrons due to fission. Units are neutrons produced
per source neutron.
:kappa-fission:
The recoverable energy production rate due to fission. The recoverable
energy is defined as the fission product kinetic energy, prompt and
@ -1378,6 +1507,14 @@ The ``<tally>`` element accepts the following sub-elements:
specified. Furthermore, it may not be used in conjunction with any
other score.
:inverse-velocity:
The flux-weighted inverse velocity where the velocity is in units of
centimeters per second.
.. note::
The ``analog`` estimator is actually identical to the ``collision``
estimator for the inverse-velocity score.
:events:
Number of scoring events. Units are events per source particle.
@ -1423,8 +1560,7 @@ a separate element with the tag name ``<mesh>``. This element has the following
attributes/sub-elements:
:type:
The type of structured mesh. Valid options include "rectangular" and
"hexagonal".
The type of structured mesh. The only valid option is "regular".
:dimension:
The number of mesh cells in each direction.
@ -1526,16 +1662,16 @@ sub-elements:
*Default*: None - Required entry
:type:
Keyword for type of plot to be produced. Currently only "slice" and
"voxel" plots are implemented. The "slice" plot type creates 2D pixel
maps saved in the PPM file format. PPM files can be displayed in most
viewers (e.g. the default Gnome viewer, IrfanView, etc.). The "voxel"
plot type produces a binary datafile containing voxel grid positioning and
the cell or material (specified by the ``color`` tag) at the center of each
voxel. These datafiles can be processed into 3D SILO files using the
``voxel.py`` utility provided with the OpenMC source, and subsequently
viewed with a 3D viewer such as VISIT or Paraview. See the
:ref:`devguide_voxel` for information about the datafile structure.
Keyword for type of plot to be produced. Currently only "slice" and "voxel"
plots are implemented. The "slice" plot type creates 2D pixel maps saved in
the PPM file format. PPM files can be displayed in most viewers (e.g. the
default Gnome viewer, IrfanView, etc.). The "voxel" plot type produces a
binary datafile containing voxel grid positioning and the cell or material
(specified by the ``color`` tag) at the center of each voxel. These
datafiles can be processed into 3D SILO files using the
``openmc-voxel-to-silovtk`` utility provided with the OpenMC source, and
subsequently viewed with a 3D viewer such as VISIT or Paraview. See the
:ref:`usersguide_voxel` for information about the datafile structure.
.. note:: Since the PPM format is saved without any kind of compression,
the resulting file sizes can be quite large. Saving the image in

View file

@ -8,7 +8,7 @@ Installation and Configuration
Installing on Ubuntu with PPA
-----------------------------
For users with Ubuntu 11.10 or later, a binary package for OpenMC is available
For users with Ubuntu 15.04 or later, a binary package for OpenMC is available
through a Personal Package Archive (PPA) and can be installed through the APT
package manager. First, add the following PPA to the repository sources:
@ -28,6 +28,9 @@ Now OpenMC should be recognized within the repository and can be installed:
sudo apt-get install openmc
Binary packages from this PPA may exist for earlier versions of Ubuntu, but they
are no longer supported.
--------------------
Building from Source
--------------------
@ -59,6 +62,37 @@ Prerequisites
sudo apt-get install cmake
* HDF5_ Library for portable binary output format
OpenMC uses HDF5 for binary output files. As such, you will need to have
HDF5 installed on your computer. The installed version will need to have
been compiled with the same compiler you intend to compile OpenMC with. If
you are using HDF5 in conjunction with MPI, we recommend that your HDF5
installation be built with parallel I/O features. An example of
configuring HDF5_ is listed below::
FC=/opt/mpich/3.1/bin/mpif90 CC=/opt/mpich/3.1/bin/mpicc \
./configure --prefix=/opt/hdf5/1.8.12 --enable-fortran \
--enable-fortran2003 --enable-parallel
You may omit ``--enable-parallel`` if you want to compile HDF5_ in serial.
.. important::
OpenMC uses various parts of the HDF5 Fortran 2003 API; as such you
must include ``--enable-fortran2003`` or else OpenMC will not be able
to compile.
On Debian derivatives, HDF5 and/or parallel HDF5 can be installed through
the APT package manager:
.. code-block:: sh
sudo apt-get install libhdf5-8 libhdf5-dev hdf5-helpers
Note that the exact package names may vary depending on your particular
distribution and version.
.. admonition:: Optional
* An MPI implementation for distributed-memory parallel runs
@ -72,20 +106,6 @@ Prerequisites
sudo apt-get install mpich libmpich-dev
sudo apt-get install openmpi-bin libopenmpi1.6 libopenmpi-dev
* HDF5_ Library for portable binary output format
To compile with support for HDF5_ output (highly recommended), you will
need to have HDF5 installed on your computer. The installed version will
need to have been compiled with the same compiler you intend to compile
OpenMC with. HDF5_ must be built with parallel I/O features if you intend
to use HDF5_ with MPI. An example of configuring HDF5_ is listed below::
FC=/opt/mpich/3.1/bin/mpif90 CC=/opt/mpich/3.1/bin/mpicc \
./configure --prefix=/opt/hdf5/1.8.12 --enable-fortran \
--enable-fortran2003 --enable-parallel
You may omit ``--enable-parallel`` if you want to compile HDF5_ in serial.
* git_ version control software for obtaining source code
.. _gfortran: http://gcc.gnu.org/wiki/GFortran
@ -194,27 +214,26 @@ command, i.e.
FC=mpif90 cmake /path/to/openmc
Compiling with HDF5
+++++++++++++++++++
To compile with MPI, set the :envvar:`FC` environment variable to the path to
the HDF5 Fortran wrapper. For example, in a bash shell:
Selecting HDF5 Installation
+++++++++++++++++++++++++++
CMakeLists.txt searches for the ``h5fc`` or ``h5pfc`` HDF5 Fortran wrapper on
your PATH environment variable and subsequently uses it to determine library
locations and compile flags. If you have multiple installations of HDF5 or one
that does not appear on your PATH, you can set the HDF5_ROOT environment
variable to the root directory of the HDF5 installation, e.g.
.. code-block:: sh
export FC=h5fc
export HDF5_ROOT=/opt/hdf5/1.8.15
cmake /path/to/openmc
As noted above, an environment variable can typically be set for a single
command, i.e.
This will cause CMake to search first in /opt/hdf5/1.8.15/bin for ``h5fc`` /
``h5pfc`` before it searches elsewhere. As noted above, an environment variable
can typically be set for a single command, i.e.
.. code-block:: sh
FC=h5fc cmake /path/to/openmc
To compile with support for both MPI and HDF5, use the parallel HDF5 wrapper
``h5pfc`` instead. Note that this requires that your HDF5 installation be
compiled with ``--enable-parallel``.
HDF5_ROOT=/opt/hdf5/1.8.15 cmake /path/to/openmc
Compiling on Linux and Mac OS X
-------------------------------
@ -308,6 +327,25 @@ This will build an executable named ``openmc``.
.. _MinGW: http://www.mingw.org
.. _SourceForge: http://sourceforge.net/projects/mingw
Compiling for the Intel Xeon Phi
--------------------------------
In order to build OpenMC for the Intel Xeon Phi using the Intel Fortran
compiler, it is necessary to specify that all objects be compiled with the
``-mmic`` flag as follows:
.. code-block:: sh
mkdir build && cd build
FC=ifort FFLAGS=-mmic cmake -Dopenmp=on ..
make
Note that unless an HDF5 build for the Intel Xeon Phi is already on your target
machine, you will need to cross-compile HDF5 for the Xeon Phi. An `example
script`_ to build zlib and HDF5 provides several necessary workarounds.
.. _example script: https://github.com/paulromano/install-scripts/blob/master/install-hdf5-mic
Testing Build
-------------

View file

@ -0,0 +1,16 @@
.. _usersguide_output:
===================
Output File Formats
===================
.. toctree::
:numbered:
:maxdepth: 3
statepoint
source
summary
particle_restart
track
voxel

View file

@ -0,0 +1,57 @@
.. _usersguide_particle_restart:
============================
Particle Restart File Format
============================
The current revision of the particle restart file format is 1.
**/filetype** (*char[]*)
String indicating the type of file.
**/revision** (*int*)
Revision of the particle restart file format. Any time a change is made in
the format, this integer is incremented.
**/current_batch** (*int*)
The number of batches already simulated.
**/gen_per_batch** (*int*)
Number of generations per batch.
**/current_gen** (*int*)
The number of generations already simulated.
**/n_particles** (*int8_t*)
Number of particles used per generation.
**/run_mode** (*int*)
Run mode used. A value of 1 indicates a fixed-source run and a value of 2
indicates an eigenvalue run.
**/id** (*int8_t*)
Unique identifier of the particle.
**/weight** (*double*)
Weight of the particle.
**/energy** (*double*)
Energy of the particle in MeV.
**/xyz** (*double[3]*)
Position of the particle.
**/uvw** (*double[3]*)
Direction of the particle.

View file

@ -0,0 +1,19 @@
.. _usersguide_source:
==================
Source File Format
==================
Normally, source data is stored in a state point file. However, it is possible
to request that the source be written separately, in which case the format used
is that documented here.
**/filetype** (*char[]*)
String indicating the type of file.
**/source_bank** (Compound type)
Source bank information for each particle. The compound type has fields
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight, position,
direction, and energy of the source particle, respectively.

View file

@ -0,0 +1,259 @@
.. _usersguide_statepoint:
=======================
State Point File Format
=======================
The current revision of the statepoint file format is 14.
**/filetype** (*char[]*)
String indicating the type of file.
**/revision** (*int*)
Revision of the state point file format. Any time a change is made in the
format, this integer is incremented.
**/version_major** (*int*)
Major version number for OpenMC
**/version_minor** (*int*)
Minor version number for OpenMC
**/version_release** (*int*)
Release version number for OpenMC
**/date_and_time** (*char[]*)
Date and time the state point was written.
**/path** (*char[]*)
Absolute path to directory containing input files.
**/seed** (*int8_t*)
Pseudo-random number generator seed.
**/run_mode** (*char[]*)
Run mode used. A value of 1 indicates a fixed-source run and a value of 2
indicates an eigenvalue run.
**/n_particles** (*int8_t*)
Number of particles used per generation.
**/n_batches** (*int*)
Number of batches to simulate.
**/current_batch** (*int*)
The number of batches already simulated.
if run_mode == 'k-eigenvalue':
**/n_inactive** (*int*)
Number of inactive batches.
**/gen_per_batch** (*int*)
Number of generations per batch.
**/k_generation** (*double[]*)
k-effective for each generation simulated.
**/entropy** (*double[]*)
Shannon entropy for each generation simulated
**/k_col_abs** (*double*)
Sum of product of collision/absorption estimates of k-effective
**/k_col_tra** (*double*)
Sum of product of collision/track-length estimates of k-effective
**/k_abs_tra** (*double*)
Sum of product of absorption/track-length estimates of k-effective
**/k_combined** (*double[2]*)
Mean and standard deviation of a combined estimate of k-effective
**/cmfd_on** (*int*)
Flag indicating whether CMFD is on (1) or off (0).
if (cmfd_on)
**/cmfd/indices** (*int[4]*)
Indices for cmfd mesh (i,j,k,g)
**/cmfd/k_cmfd** (*double[]*)
CMFD eigenvalues
**/cmfd/cmfd_src** (*double[][][][]*)
CMFD fission source
**/cmfd/cmfd_entropy** (*double[]*)
CMFD estimate of Shannon entropy
**/cmfd/cmfd_balance** (*double[]*)
RMS of the residual neutron balance equation on CMFD mesh
**/cmfd/cmfd_dominance** (*double[]*)
CMFD estimate of dominance ratio
**/cmfd/cmfd_srccmp** (*double[]*)
RMS comparison of difference between OpenMC and CMFD fission source
**/tallies/n_meshes** (*int*)
Number of meshes in tallies.xml file
**/tally/meshes/ids** (*int[]*)
Internal unique ID of each mesh.
**/tally/meshes/keys** (*int[]*)
User-identified unique ID of each mesh.
**/tallies/meshes/mesh <uid>/type** (*char[]*)
Type of mesh.
**/tallies/meshes/mesh <uid>/dimension** (*int*)
Number of mesh cells in each dimension.
**/tallies/meshes/mesh <uid>/lower_left** (*double[]*)
Coordinates of lower-left corner of mesh.
**/tallies/meshes/mesh <uid>/upper_right** (*double[]*)
Coordinates of upper-right corner of mesh.
**/tallies/meshes/mesh <uid>/width** (*double[]*)
Width of each mesh cell in each dimension.
**/tallies/n_tallies** (*int*)
Number of user-defined tallies.
**/tallies/ids** (*int[]*)
Internal unique ID of each tally.
**/tallies/keys** (*int[]*)
User-identified unique ID of each tally.
**/tallies/tally <uid>/estimator** (*char[]*)
Type of tally estimator, either 'analog', 'tracklength', or 'collision'.
**/tallies/tally <uid>/n_realizations** (*int*)
Number of realizations.
**/tallies/tally <uid>/n_filters** (*int*)
Number of filters used.
**/tallies/tally <uid>/filter <j>/type** (*char[]*)
Type of the j-th filter. Can be 'universe', 'material', 'cell', 'cellborn',
'surface', 'mesh', 'energy', 'energyout', or 'distribcell'.
**/tallies/tally <uid>/filter <j>/offset** (*int*)
Filter offset (used for distribcell filter).
**/tallies/tally <uid>/filter <j>/n_bins** (*int*)
Number of bins for the j-th filter.
**/tallies/tally <uid>/filter <j>/bins** (*int[]* or *double[]*)
Value for each filter bin of this type.
**/tallies/tally <uid>/nuclides** (*char[][]*)
Array of nuclides to tally. Note that if no nuclide is specified in the user
input, a single 'total' nuclide appears here.
**/tallies/tally <uid>/n_score_bins** (*int*)
Number of scoring bins for a single nuclide. In general, this can be greater
than the number of user-specified scores since each score might have
multiple scoring bins, e.g., scatter-PN.
**/tallies/tally <uid>/score_bins** (*char[][]*)
Values of specified scores.
**/tallies/tally <uid>/n_user_scores** (*int*)
Number of scores without accounting for those added by expansions,
e.g. scatter-PN.
**/tallies/tally <uid>/moment_orders** (*char[][]*)
Tallying moment orders for Legendre and spherical harmonic tally expansions
(*e.g.*, 'P2', 'Y1,2', etc.).
**/tallies/tally <uid>/results** (Compound type)
Accumulated sum and sum-of-squares for each bin of the i-th tally. This is a
two-dimensional array, the first dimension of which represents combinations
of filter bins and the second dimensions of which represents scoring
bins. Each element of the array has fields 'sum' and 'sum_sq'.
**/source_present** (*int*)
Flag indicated if source bank is present in the file
**/n_realizations** (*int*)
Number of realizations for global tallies.
**/n_global_tallies** (*int*)
Number of global tally scores.
**/global_tallies** (Compound type)
Accumulated sum and sum-of-squares for each global tally. The compound type
has fields named ``sum`` and ``sum_sq``.
**tallies_present** (*int*)
Flag indicated if tallies are present in the file.
if (run_mode == 'k-eigenvalue' and source_present > 0)
**/source_bank** (Compound type)
Source bank information for each particle. The compound type has fields
``wgt``, ``xyz``, ``uvw``, and ``E`` which represent the weight,
position, direction, and energy of the source particle, respectively.

View file

@ -0,0 +1,311 @@
.. _usersguide_summary:
===================
Summary File Format
===================
The current revision of the summary file format is 1.
**/filetype** (*char[]*)
String indicating the type of file.
**/revision** (*int*)
Revision of the summary file format. Any time a change is made in the
format, this integer is incremented.
**/version_major** (*int*)
Major version number for OpenMC
**/version_minor** (*int*)
Minor version number for OpenMC
**/version_release** (*int*)
Release version number for OpenMC
**/date_and_time** (*char[]*)
Date and time the summary was written.
**/n_procs** (*int*)
Number of MPI processes used.
**/n_particles** (*int8_t*)
Number of particles used per generation.
**/n_batches** (*int*)
Number of batches to simulate.
**/n_inactive** (*int*)
Number of inactive batches. Only present if /run_mode is set to
'k-eigenvalue'.
**/n_active** (*int*)
Number of active batches. Only present if /run_mode is set to
'k-eigenvalue'.
**/gen_per_batch** (*int*)
Number of generations per batch. Only present if /run_mode is set to
'k-eigenvalue'.
**/geometry/n_cells** (*int*)
Number of cells in the problem.
**/geometry/n_surfaces** (*int*)
Number of surfaces in the problem.
**/geometry/n_universes** (*int*)
Number of unique universes in the problem.
**/geometry/n_lattices** (*int*)
Number of lattices in the problem.
**/geometry/cells/cell <uid>/index** (*int*)
Index in cells array used internally in OpenMC.
**/geometry/cells/cell <uid>/name** (*char[]*)
Name of the cell.
**/geometry/cells/cell <uid>/universe** (*int*)
Universe assigned to the cell. If none is specified, the default
universe (0) is assigned.
**/geometry/cells/cell <uid>/fill_type** (*char[]*)
Type of fill for the cell. Can be 'normal', 'universe', or 'lattice'.
**/geometry/cells/cell <uid>/material** (*int*)
Unique ID of the material assigned to the cell. This dataset is present only
if fill_type is set to 'normal'.
**/geometry/cells/cell <uid>/offset** (*int[]*)
Offsets used for distribcell tally filter. This dataset is present only if
fill_type is set to 'universe'.
**/geometry/cells/cell <uid>/translation** (*double[3]*)
Translation applied to the fill universe. This dataset is present only if
fill_type is set to 'universe'.
**/geometry/cells/cell <uid>/rotation** (*double[3]*)
Angles in degrees about the x-, y-, and z-axes for which the fill universe
should be rotated. This dataset is present only if fill_type is set to
'universe'.
**/geometry/cells/cell <uid>/lattice** (*int*)
Unique ID of the lattice which fills the cell. Only present if fill_type is
set to 'lattice'.
**/geometry/cells/cell <uid>/region** (*char[]*)
Region specification for the cell.
**/geometry/surfaces/surface <uid>/index** (*int*)
Index in surfaces array used internally in OpenMC.
**/geometry/surfaces/surface <uid>/name** (*char[]*)
Name of the surface.
**/geometry/surfaces/surface <uid>/type** (*char[]*)
Type of the surface. Can be 'x-plane', 'y-plane', 'z-plane', 'plane',
'x-cylinder', 'y-cylinder', 'sphere', 'x-cone', 'y-cone', 'z-cone', or
'quadric'.
**/geometry/surfaces/surface <uid>/coefficients** (*double[]*)
Array of coefficients that define the surface. See :ref:`surface_element`
for what coefficients are defined for each surface type.
**/geometry/surfaces/surface <uid>/boundary_condition** (*char[]*)
Boundary condition applied to the surface. Can be 'transmission', 'vacuum',
'reflective', or 'periodic'.
**/geometry/universes/universe <uid>/index** (*int*)
Index in the universes array used internally in OpenMC.
**/geometry/universes/universe <uid>/cells** (*int[]*)
Array of unique IDs of cells that appear in the universe.
**/geometry/lattices/lattice <uid>/index** (*int*)
Index in the lattices array used internally in OpenMC.
**/geometry/lattices/lattice <uid>/name** (*char[]*)
Name of the lattice.
**/geometry/lattices/lattice <uid>/type** (*char[]*)
Type of the lattice, either 'rectangular' or 'hexagonal'.
**/geometry/lattices/lattice <uid>/pitch** (*double[]*)
Pitch of the lattice.
**/geometry/lattices/lattice <uid>/outer** (*int*)
Outer universe assigned to lattice cells outside the defined range.
**/geometry/lattices/lattice <uid>/offsets** (*int[]*)
Offsets used for distribcell tally filter.
**/geometry/lattices/lattice <uid>/universes** (*int[]*)
Three-dimensional array of universes assigned to each cell of the lattice.
**/geometry/lattices/lattice <uid>/dimension** (*int[]*)
The number of lattice cells in each direction. This dataset is present only
when the 'type' dataset is set to 'rectangular'.
**/geometry/lattices/lattice <uid>/lower_left** (*double[]*)
The coordinates of the lower-left corner of the lattice. This dataset is
present only when the 'type' dataset is set to 'rectangular'.
**/geometry/lattices/lattice <uid>/n_rings** (*int*)
Number of radial ring positions in the xy-plane. This dataset is present
only when the 'type' dataset is set to 'hexagonal'.
**/geometry/lattices/lattice <uid>/n_axial** (*int*)
Number of lattice positions along the z-axis. This dataset is present only
when the 'type' dataset is set to 'hexagonal'.
**/geometry/lattices/lattice <uid>/center** (*double[]*)
Coordinates of the center of the lattice. This dataset is present only when
the 'type' dataset is set to 'hexagonal'.
**/n_materials** (*int*)
Number of materials in the problem.
**/materials/material <uid>/index** (*int*)
Index in materials array used internally in OpenMC.
**/materials/material <uid>/name** (*char[]*)
Name of the material.
**/materials/material <uid>/atom_density** (*double[]*)
Total atom density of the material in atom/b-cm.
**/materials/material <uid>/nuclides** (*char[][]*)
Array of nuclides present in the material, e.g., 'U-235.71c'.
**/materials/material <uid>/nuclide_densities** (*double[]*)
Atom density of each nuclide.
**/materials/material <uid>/sab_names** (*char[][]*)
Names of S(:math:`\alpha`,:math:`\beta`) tables assigned to the material.
**/tallies/n_tallies** (*int*)
Number of tallies in the problem.
**/tallies/n_meshes** (*int*)
Number of meshes in the problem.
**/tallies/mesh <uid>/index** (*int*)
Index in the meshes array used internally in OpenMC.
**/tallies/mesh <uid>/type** (*char[]*)
Type of the mesh. The only valid option is currently 'regular'.
**/tallies/mesh <uid>/dimension** (*int[]*)
Number of mesh cells in each direction.
**/tallies/mesh <uid>/lower_left** (*double[]*)
Coordinates of the lower-left corner of the mesh.
**/tallies/mesh <uid>/upper_right** (*double[]*)
Coordinates of the upper-right corner of the mesh.
**/tallies/mesh <uid>/width** (*double[]*)
Width of a single mesh cell in each direction.
**/tallies/tally <uid>/index** (*int*)
Index in tallies array used internally in OpenMC.
**/tallies/tally <uid>/name** (*char[]*)
Name of the tally.
**/tallies/tally <uid>/n_filters** (*int*)
Number of filters applied to the tally.
**/tallies/tally <uid>/filter <j>/type** (*char[]*)
Type of the j-th filter. Can be 'universe', 'material', 'cell', 'cellborn',
'surface', 'mesh', 'energy', 'energyout', or 'distribcell'.
**/tallies/tally <uid>/filter <j>/offset** (*int*)
Filter offset (used for distribcell filter).
**/tallies/tally <uid>/filter <j>/n_bins** (*int*)
Number of bins for the j-th filter.
**/tallies/tally <uid>/filter <j>/bins** (*int[]* or *double[]*)
Value for each filter bin of this type.
**/tallies/tally <uid>/nuclides** (*char[][]*)
Array of nuclides to tally. Note that if no nuclide is specified in the user
input, a single 'total' nuclide appears here.
**/tallies/tally <uid>/n_score_bins** (*int*)
Number of scoring bins for a single nuclide. In general, this can be greater
than the number of user-specified scores since each score might have
multiple scoring bins, e.g., scatter-PN.
**/tallies/tally <uid>/score_bins** (*char[][]*)
Scoring bins for the tally.

View file

@ -0,0 +1,30 @@
.. _usersguide_track:
=================
Track File Format
=================
The current revision of the particle track file format is 1.
**/filetype** (*char[]*)
String indicating the type of file.
**/revision** (*int*)
Revision of the track file format. Any time a change is made in the format,
this integer is incremented.
**/n_particles** (*int*)
Number of particles for which tracks are recorded.
**/n_coords** (*int[]*)
Number of coordinates for each particle.
*do i = 1, n_particles*
**/coordinates_i** (*double[][3]*)
(x,y,z) coordinates for the *i*-th particle.

View file

@ -0,0 +1,25 @@
.. _usersguide_voxel:
======================
Voxel Plot File Format
======================
**/filetype** (*char[]*)
String indicating the type of file.
**/num_voxels** (*int[3]*)
Number of voxels in the x-, y-, and z- directions.
**/voxel_width** (*double[3]*)
Width of a voxel in centimeters.
**/lower_left** (*double[3]*)
Cartesian coordinates of the lower-left corner of the plot.
**/data** (*int[][][]*)
Data for each voxel that represents a material or cell ID.

View file

@ -6,31 +6,34 @@ Data Processing and Visualization
This section is intended to explain in detail the recommended procedures for
carrying out common post-processing tasks with OpenMC. While several utilities
of varying complexity are provided to help automate the process, in many cases
it will be extremely beneficial to do some coding in Python to quickly obtain
results. In these cases, and for many of the provided utilities, it is necessary
for your Python installation to contain:
of varying complexity are provided to help automate the process, the most
powerful capabilities for post-processing derive from use of the :ref:`Python
API <pythonapi>`. Both the provided scripts and the Python API rely on a number
third-party Python packages, including:
* [1]_ `Numpy <http://www.numpy.org/>`_
* [1]_ `Scipy <http://www.scipy.org/>`_
* [2]_ `h5py <http://code.google.com/p/h5py/>`_
* [3]_ `Matplotlib <http://matplotlib.org/>`_
* [3]_ `Silomesh <https://github.com/nhorelik/silomesh>`_
* [3]_ `VTK <http://www.vtk.org/>`_
* [1]_ `NumPy <http://www.numpy.org/>`_
* [2]_ `h5py <http://www.h5py.org>`_
* [3]_ `pandas <http://pandas.pydata.org>`_
* [4]_ `matplotlib <http://matplotlib.org/>`_
* [4]_ `Silomesh <https://github.com/nhorelik/silomesh>`_
* [4]_ `VTK <http://www.vtk.org/>`_
* [4]_ `lxml <http://lxml.de>`_
Most of these are easily obtainable in Ubuntu through the package manager, or
are easily installed with distutils.
Most of these are can easily be installed with `pip <https://pip.pypa.io>`_
or alternatively obtaining through a package manager.
.. [1] Required for tally data extraction from statepoints with statepoint.py
.. [2] Required only if reading HDF5 statepoint files.
.. [3] Optional for plotting utilities
.. [1] Required for most post-processing tasks
.. [2] Required for reading HDF5 output files
.. [3] Optional dependency for advanced features in Python API
.. [4] Not used directly by the Python API, but are optional dependencies for a
number of scripts.
----------------------
Geometry Visualization
----------------------
Geometry plotting is carried out by creating a plots.xml, specifying plots, and
running OpenMC with the -plot or -p command-line option (See
running OpenMC with the --plot or -p command-line option (See
:ref:`usersguide_plotting`).
Plotting in 2D
@ -128,27 +131,26 @@ capabilities of 3D voxel plots.
Voxel plots are built the same way 2D slice plots are, by determining the cell
or material id of a particle at the center of each voxel. In this example, the
space covered is the cube between the points (-5,-5,-5) and (5,5,5), with voxel
centers 10/500 = 0.02 cm apart. The binary VOXEL files that are produced do not
centers 10/500 = 0.02 cm apart. The HDF5 voxel files that are produced do not
specify any color - instead containing only material or cell ids (material id
in this example) - and thus the ``background``, ``col_spec``, and ``mask``
elements are not used. If no cell is found at a voxel center, an id of -1 is
stored.
The binary VOXEL files output by OpenMC can not be viewed directly by any
existing viewers. In order to view them, they must be converted into a standard
mesh format that can be viewed in ParaView, Visit, etc. This typically will
compress the size of the file significantly. The provided utility voxel.py
accomplishes this for SILO:
The voxel plot data is written to an HDF5 file. The voxel file can subsequently
be converted into a standard mesh format that can be viewed in ParaView, Visit,
etc. This typically will compress the size of the file significantly. The
provided utility openmc-voxel-to-silovtk accomplishes this for SILO:
.. code-block:: sh
<openmc_root>/src/utils/voxel.py myplot.voxel -o output.silo
openmc-voxel-to-silovtk myplot.voxel -o output.silo
and VTK file formats:
.. code-block:: sh
<openmc_root>/src/utils/voxel.py myplot.voxel --vtk -o output.vti
openmc-voxel-to-silovtk myplot.voxel --vtk -o output.vti
To use this utility you need either
@ -156,11 +158,10 @@ To use this utility you need either
or
* `VTK <http://www.vtk.org/>`_ with python bindings - On Ubuntu, these are
easily obtained with ``sudo apt-get install python-vtk``
* `VTK <http://www.vtk.org/>`_ with python bindings. On debian derivatives,
these are easily obtained with ``sudo apt-get install python-vtk``
Users can process the binary into any other format if desired by following the
example of voxel.py. For the binary file structure, see :ref:`devguide_voxel`.
For the HDF5 file structure, see :ref:`usersguide_voxel`.
Once processed into a standard 3D file format, colors and masks can be defined
using the stored id numbers to better explore the geometry. The process for
@ -183,150 +184,38 @@ doing this will depend on the 3D viewer, but should be straightforward.
Tally Visualization
-------------------
Tally results are saved in both a text file (tallies.out) as well as a binary
Tally results are saved in both a text file (tallies.out) as well as an HDF5
statepoint file. While the tallies.out file may be fine for simple tallies, in
many cases the user requires more information about the tally or the run, or
has to deal with a large number of result values (e.g. for mesh tallies). In
these cases, extracting data from the statepoint file via Python scripting is
the preferred method of data analysis and visualization.
many cases the user requires more information about the tally or the run, or has
to deal with a large number of result values (e.g. for mesh tallies). In these
cases, extracting data from the statepoint file via the :ref:`pythonapi` is the
preferred method of data analysis and visualization.
Data Extraction
---------------
A great deal of information is available in statepoint files (See
:ref:`devguide_statepoint`), most of which is easily extracted by the provided
utility statepoint.py. This utility provides a Python class to load statepoints
and extract data - it is used in many of the provided plotting utilities, and
can be used in user-created scripts to carry out manipulations of the data. To
read tallies using this utility, make sure statepoint.py is in your PYTHONPATH,
and then import the class, instantiate it, and call read_results:
:ref:`usersguide_statepoint`), all of which is accessible through the Python
API. The ``openmc.statepoint`` module (see :ref:`pythonapi_statepoint`) provides
a class to load statepoints and access data as requested; it is used in many of
the provided plotting utilities, OpenMC's regression test suite, and can be used
in user-created scripts to carry out manipulations of the data.
.. code-block:: python
from statepoint import StatePoint
sp = StatePoint('statepoint.100.binary')
sp.read_results()
At this point the user can extract entire scores from tallies into a data
dictionary containing numpy arrays:
.. code-block:: python
tallyid = 1
score = 'flux'
data = sp.extract_results(tallyid, score)
means = data['means']
print data.keys()
The results from this function contain all filter bins (all mesh points, all
energy groups, etc.), which can be reshaped with the bin ordering also contained
in the output dictionary. This is the best choice of output for easily
integrating ranges of data.
Alternatively the user can extract specific values for a single score/filter
combination:
.. code-block:: python
tallyid = 1
score = 'flux'
filters = [('mesh', (1, 1, 5)), ('energyin', 0)]
value, error = sp.get_value(tallyid, filters, score)
In the future more documentation may become available here for statepoint.py and
the data extraction functions of StatePoint objects. However, for now it is up
to the user to explore the classes in statepoint.py to discover what data is
available in StatePoint objects (we highly recommend interactively exploring
with `IPython <http://ipython.org/>`_). Many examples can be found by looking
through the other utilities that use statepoint.py, and a few common
visualization tasks will be described here in the following sections.
An :ref:`example IPython notebook <notebook_post_processing>` demonstrates how
to extract data from a statepoint using the Python API.
Plotting in 2D
--------------
The :ref:`IPython notebook example <notebook_post_processing>` also demonstrates
how to plot a mesh tally in two dimensions using the Python API. Note, however,
that there is also a script distributed with OpenMC, ``openmc-plot-mesh-tally``,
that provides an interactive GUI to explore and plot mesh tallies for any scores
and filter bins.
.. image:: ../_images/plotmeshtally.png
:height: 200px
For simple viewing of 2D slices of a mesh plot, the utility plot_mesh_tally.py
is provided. This utility provides an interactive GUI to explore and plot
mesh tallies for any scores and filter bins. It requires statepoint.py.
.. image:: ../_images/fluxplot.png
:height: 200px
Alternatively, the user can write their own Python script to manipulate the data
appropriately. Consider a run where the first tally contains a 105x105x20 mesh
over a small core, with a flux score and two energyin filter bins. To explicitly
extract the data and create a plot with gnuplot, the following script can be
used. The script operates in several steps for clarity, and is not necessarily
the most efficient way to extract data from large mesh tallies. This creates the
two heatmaps in the previous figure.
.. code-block:: python
#!/usr/bin/env python
import os
import statepoint
# load and parse the statepoint file
sp = statepoint.StatePoint('statepoint.300.binary')
sp.read_results()
tallyid = 0 # This is tally 1
score = 0 # This corresponds to flux (see tally.scores)
# get mesh dimensions
meshid = sp.tallies[tallyid].filters['mesh'].bins[0]
for i,m in enumerate(sp.meshes):
if m.id == meshid:
mesh = m
break
nx,ny,nz = mesh.dimension
# loop through mesh and extract values to python dictionaries
thermal = {}
fast = {}
for x in range(1,nx+1):
for y in range(1,ny+1):
for z in range(1,nz+1):
val,err = sp.get_value(tallyid,
[('mesh',(x,y,z)),('energyin',0)],
score)
thermal[(x,y,z)] = val
val,err = sp.get_value(tallyid,
[('mesh',(x,y,z)),('energyin',1)],
score)
fast[(x,y,z)] = val
# sum up the axial values and write datafile for gnuplot
with open('meshdata.dat','w') as fh:
for x in range(1,nx+1):
for y in range(1,ny+1):
thermalval = 0.
fastval = 0.
for z in range(1,nz+1):
thermalval += thermal[(x,y,z)]
fastval += fast[(x,y,z)]
fh.write("{} {} {} {}\n".format(x,y,thermalval,fastval))
# write gnuplot file
with open('tmp.gnuplot','w') as fh:
fh.write(r"""set terminal png size 1000 400
set output 'fluxplot.png'
set nokey
set autoscale fix
set multiplot layout 1,2 title "Pin Mesh Flux Tally"
set title "Thermal"
plot 'meshdata.dat' using 1:2:3 with image
set title "Fast"
plot 'meshdata.dat' using 1:2:4 with image
""")
# make plot
os.system("gnuplot < tmp.gnuplot")
Plotting in 3D
--------------
@ -334,22 +223,23 @@ Plotting in 3D
:height: 200px
As with 3D plots of the geometry, meshtally data needs to be put into a standard
format for viewing. The utility statepoint_3d.py is provided to accomplish this
for both VTK and SILO. By default statepoint_3d.py processes a statepoint into a
3D file with all mesh tallies and filter/score combinations,
format for viewing. The utility ``openmc-statepoint-3d`` is provided to
accomplish this for both VTK and SILO. By default ``openmc-statepoint-3d``
processes a statepoint into a 3D file with all mesh tallies and filter/score
combinations,
.. code-block:: sh
<openmc_root>/src/utils/statepoint_3d.py <statepoint_file> -o output.silo
<openmc_root>/src/utils/statepoint_3d.py <statepoint_file> --vtk -o output.vtm
openmc-statepoint-3d <statepoint_file> -o output.silo
openmc-statepoint-3d <statepoint_file> --vtk -o output.vtm
but it also provides several command-line options to selectively process only
certain data arrays in order to keep file sizes down.
.. code-block:: sh
statepoint_3d.py <statepoint_file> --tallies 2,4 --scores 4.1,4.3 -o output.silo
statepoint_3d.py <statepoint_file> --filters 2.energyin.1 --vtk -o output.vtm
openmc-statepoint-3d <statepoint_file> --tallies 2,4 --scores 4.1,4.3 -o output.silo
openmc-statepoint-3d <statepoint_file> --filters 2.energyin.1 --vtk -o output.vtm
All available options for specifying a subset of tallies, scores, and filters
can be listed with the ``--list`` or ``-l`` command line options.
@ -426,13 +316,11 @@ Getting Data into MATLAB
------------------------
There is currently no front-end utility to dump tally data to MATLAB files, but
the process is straightforward. First extract the data using a custom Python
script with statepoint.py, put the data into appropriately-shaped numpy arrays,
and then use the `Scipy MATLAB IO routines
the process is straightforward. First extract the data using the Python API via
``openmc.statepoint`` and then use the `Scipy MATLAB IO routines
<http://docs.scipy.org/doc/scipy/reference/tutorial/io.html>`_ to save to a MAT
file. Note that the data contained in the output from
``StatePoint.extract_result`` is already in a Numpy array that can be reshaped
and dumped to MATLAB in one step.
file. Note that all arrays that are accessible in a statepoint are already in
NumPy arrays that can be reshaped and dumped to MATLAB in one step.
----------------------------
Particle Track Visualization
@ -463,15 +351,15 @@ particle numbers, respectively. For example, to output the tracks for particles
</track>
After running OpenMC, the directory should contain a file of the form
"track_(batch #)_(generation #)_(particle #).(binary or h5)" for each particle
tracked. These track files can be converted into VTK poly data files with the
"track.py" utility. The usage of track.py is of the form "track.py [-o OUT] IN"
where OUT is the optional output filename and IN is one or more filenames
describing track files. The default output name is "track.pvtp". A common
usage of track.py is "track.py track*.binary" which will use the data from all
binary track files in the directory to write a "track.pvtp" VTK output file.
The .pvtp file can then be read and plotted by 3d visualization programs such as
ParaView.
"track_(batch #)_(generation #)_(particle #).h5" for each particle tracked.
These track files can be converted into VTK poly data files with the
``openmc-track-to-vtk`` utility. The usage of ``openmc-track-to-vtk`` is of the
form "openmc-track-to-vtk [-o OUT] IN" where OUT is the optional output filename
and IN is one or more filenames describing track files. The default output name
is "track.pvtp". A common usage of track.py is "openmc-track-to-vtk track*.h5"
which will use the data from all binary track files in the directory to write a
"track.pvtp" VTK output file. The .pvtp file can then be read and plotted by 3d
visualization programs such as ParaView.
----------------------
Source Site Processing
@ -480,43 +368,6 @@ Source Site Processing
For eigenvalue problems, OpenMC will store information on the fission source
sites in the statepoint file by default. For each source site, the weight,
position, sampled direction, and sampled energy are stored. To extract this data
from a statepoint file, the statepoint.py Python module can be used. Below is an
example of an interactive ipython session using the statepoint.py Python module:
.. code-block:: python
In [1]: import statepoint
In [2]: sp = statepoint.StatePoint('statepoint.100.h5')
In [3]: sp.read_source()
In [4]: len(sp.source)
Out[4]: 1000
In [5]: sp.source[0:10]
Out[5]:
[<SourceSite: xyz=[ 2.21980946 -8.92686048 87.93720485] at E=0.932923263566>,
<SourceSite: xyz=[ 2.21980946 -8.92686048 87.93720485] at E=0.349240220512>,
<SourceSite: xyz=[-31.21542213 -30.26762771 72.10845757] at E=3.75843584486>,
<SourceSite: xyz=[-31.21542213 -30.26762771 72.10845757] at E=0.80550137267>,
<SourceSite: xyz=[ 0.18805099 -69.13376508 103.67726838] at E=1.67922461097>,
<SourceSite: xyz=[ 0.18805099 -69.13376508 103.67726838] at E=1.16304110199>,
<SourceSite: xyz=[ -50.42189115 -9.96571672 123.34077905] at E=0.710937974074>,
<SourceSite: xyz=[ -32.80427668 -15.49316628 125.26301151] at E=1.61907104162>,
<SourceSite: xyz=[ 53.20376026 -15.38643708 120.58071044] at E=3.33962024907>,
<SourceSite: xyz=[ 53.20376026 -15.38643708 120.58071044] at E=1.90185680329>]
In [6]: site = sp.source[0]
In [7]: site.weight
Out[7]: 1.0
In [8]: site.xyz
Out[8]: array([ 2.21980946, -8.92686048, 87.93720485])
In [9]: site.uvw
Out[9]: array([ 0.06740523, 0.50612814, 0.85982024])
In [10]: site.E
Out[10]: 0.93292326356564159
from a statepoint file, the ``openmc.statepoint`` module can be used. An
:ref:`example IPython notebook <notebook_post_processing>` demontrates how to
analyze and plot source information.

View file

@ -31,21 +31,6 @@ f951: error: unrecognized command line option "-fbacktrace"
You are probably using a version of the gfortran compiler that is too
old. Download and install the latest version of gfortran_.
make[1]: ifort: Command not found
*********************************
You tried compiling with the Intel Fortran compiler and it was not found on your
:envvar:`PATH`. If you have the Intel compiler installed, make sure the shell
can locate it (this can be tested with :program:`which ifort`).
make[1]: pgf90: Command not found
*********************************
You tried compiling with the PGI Fortran compiler and it was not found on your
:envvar:`PATH`. If you have the PGI compiler installed, make sure the shell can
locate it (this can be tested with :program:`which pgf90`).
-------------------------
Problems with Simulations
-------------------------
@ -56,13 +41,13 @@ Segmentation Fault
A segmentation fault occurs when the program tries to access a variable in
memory that was outside the memory allocated for the program. The best way to
debug a segmentation fault is to re-compile OpenMC with debug options turned
on. First go to your ``openmc/src`` directory where OpenMC was compiled and type
the following commands:
on. Create a new build directory and type the following commands:
.. code-block:: sh
make distclean
make DEBUG=yes
mkdir build-debug && cd build-debug
cmake -Ddebug=on /path/to/openmc
make
Now when you re-run your problem, it should report exactly where the program
failed. If after reading the debug output, you are still unsure why the program

View file

@ -1,6 +1,5 @@
import openmc
###############################################################################
# Simulation Input File Parameters
###############################################################################
@ -54,12 +53,11 @@ cell2 = openmc.Cell(cell_id=100, name='cell 2')
cell3 = openmc.Cell(cell_id=101, name='cell 3')
cell4 = openmc.Cell(cell_id=2, name='cell 4')
# Register Surfaces with Cells
cell1.add_surface(surface=surf2, halfspace=-1)
cell2.add_surface(surface=surf1, halfspace=-1)
cell3.add_surface(surface=surf1, halfspace=+1)
cell4.add_surface(surface=surf2, halfspace=+1)
cell4.add_surface(surface=surf3, halfspace=-1)
# Use surface half-spaces to define regions
cell1.region = -surf2
cell2.region = -surf1
cell3.region = +surf1
cell4.region = +surf2 & -surf3
# Register Materials with Cells
cell2.fill = fuel

View file

@ -0,0 +1,136 @@
import numpy as np
import openmc
###############################################################################
# Simulation Input File Parameters
###############################################################################
# OpenMC simulation parameters
batches = 15
inactive = 5
particles = 10000
###############################################################################
# Exporting to OpenMC materials.xml File
###############################################################################
# Instantiate some Nuclides
h1 = openmc.Nuclide('H-1')
o16 = openmc.Nuclide('O-16')
u235 = openmc.Nuclide('U-235')
u238 = openmc.Nuclide('U-238')
# Instantiate some Materials and register the appropriate Nuclides
fuel1 = openmc.Material(material_id=1, name='fuel')
fuel1.set_density('g/cc', 4.5)
fuel1.add_nuclide(u235, 1.)
fuel2 = openmc.Material(material_id=2, name='depleted fuel')
fuel2.set_density('g/cc', 4.5)
fuel2.add_nuclide(u238, 1.)
moderator = openmc.Material(material_id=3, name='moderator')
moderator.set_density('g/cc', 1.0)
moderator.add_nuclide(h1, 2.)
moderator.add_nuclide(o16, 1.)
moderator.add_s_alpha_beta('HH2O', '71t')
# Instantiate a MaterialsFile, register all Materials, and export to XML
materials_file = openmc.MaterialsFile()
materials_file.default_xs = '71c'
materials_file.add_materials([fuel1, fuel2, moderator])
materials_file.export_to_xml()
###############################################################################
# Exporting to OpenMC geometry.xml File
###############################################################################
# Instantiate planar surfaces
x1 = openmc.XPlane(surface_id=1, x0=-10)
x2 = openmc.XPlane(surface_id=2, x0=-7)
x3 = openmc.XPlane(surface_id=3, x0=-4)
x4 = openmc.XPlane(surface_id=4, x0=4)
x5 = openmc.XPlane(surface_id=5, x0=7)
x6 = openmc.XPlane(surface_id=6, x0=10)
y1 = openmc.YPlane(surface_id=11, y0=-10)
y2 = openmc.YPlane(surface_id=12, y0=-7)
y3 = openmc.YPlane(surface_id=13, y0=-4)
y4 = openmc.YPlane(surface_id=14, y0=4)
y5 = openmc.YPlane(surface_id=15, y0=7)
y6 = openmc.YPlane(surface_id=16, y0=10)
z1 = openmc.ZPlane(surface_id=21, z0=-10)
z2 = openmc.ZPlane(surface_id=22, z0=-7)
z3 = openmc.ZPlane(surface_id=23, z0=-4)
z4 = openmc.ZPlane(surface_id=24, z0=4)
z5 = openmc.ZPlane(surface_id=25, z0=7)
z6 = openmc.ZPlane(surface_id=26, z0=10)
# Set vacuum boundary conditions on outside
for surface in [x1, x6, y1, y6, z1, z6]:
surface.boundary_type = 'vacuum'
# Instantiate Cells
inner_box = openmc.Cell(cell_id=1, name='inner box')
middle_box = openmc.Cell(cell_id=2, name='middle box')
outer_box = openmc.Cell(cell_id=3, name='outer box')
# Use each set of six planes to create solid cube regions. We can then use these
# to create cubic shells.
inner_cube = +x3 & -x4 & +y3 & -y4 & +z3 & -z4
middle_cube = +x2 & -x5 & +y2 & -y5 & +z2 & -z5
outer_cube = +x1 & -x6 & +y1 & -y6 & +z1 & -z6
outside_inner_cube = -x3 | +x4 | -y3 | +y4 | -z3 | +z4
# Use surface half-spaces to define regions
inner_box.region = inner_cube
middle_box.region = middle_cube & outside_inner_cube
outer_box.region = outer_cube & ~middle_cube
# Register Materials with Cells
inner_box.fill = fuel1
middle_box.fill = fuel2
outer_box.fill = moderator
# Instantiate root universe
root = openmc.Universe(universe_id=0, name='root universe')
root.add_cells([inner_box, middle_box, outer_box])
# Instantiate a Geometry and register the root Universe
geometry = openmc.Geometry()
geometry.root_universe = root
# Instantiate a GeometryFile, register Geometry, and export to XML
geometry_file = openmc.GeometryFile()
geometry_file.geometry = geometry
geometry_file.export_to_xml()
###############################################################################
# Exporting to OpenMC settings.xml File
###############################################################################
# Instantiate a SettingsFile, set all runtime parameters, and export to XML
settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.set_source_space('box', np.concatenate(outer_cube.bounding_box))
settings_file.export_to_xml()
###############################################################################
# Exporting to OpenMC plots.xml File
###############################################################################
plot = openmc.Plot(plot_id=1)
plot.origin = [0, 0, 0]
plot.width = [20, 20]
plot.pixels = [200, 200]
plot.color = 'cell'
# Instantiate a PlotsFile, add Plot, and export to XML
plot_file = openmc.PlotsFile()
plot_file.add_plot(plot)
plot_file.export_to_xml()

View file

@ -67,15 +67,12 @@ cell4 = openmc.Cell(cell_id=500, name='cell 4')
cell5 = openmc.Cell(cell_id=600, name='cell 5')
cell6 = openmc.Cell(cell_id=601, name='cell 6')
# Register Surfaces with Cells
cell1.add_surface(left, halfspace=+1)
cell1.add_surface(right, halfspace=-1)
cell1.add_surface(bottom, halfspace=+1)
cell1.add_surface(top, halfspace=-1)
cell2.add_surface(fuel_surf, halfspace=-1)
cell3.add_surface(fuel_surf, halfspace=+1)
cell5.add_surface(fuel_surf, halfspace=-1)
cell6.add_surface(fuel_surf, halfspace=+1)
# Use surface half-spaces to define regions
cell1.region = +left & -right & +bottom & -top
cell2.region = -fuel_surf
cell3.region = +fuel_surf
cell5.region = -fuel_surf
cell6.region = +fuel_surf
# Register Materials with Cells
cell2.fill = fuel

View file

@ -66,21 +66,15 @@ cell6 = openmc.Cell(cell_id=202, name='cell 6')
cell7 = openmc.Cell(cell_id=301, name='cell 7')
cell8 = openmc.Cell(cell_id=302, name='cell 8')
# Register Surfaces with Cells
cell1.add_surface(left, halfspace=+1)
cell1.add_surface(right, halfspace=-1)
cell1.add_surface(bottom, halfspace=+1)
cell1.add_surface(top, halfspace=-1)
cell2.add_surface(left, halfspace=+1)
cell2.add_surface(right, halfspace=-1)
cell2.add_surface(bottom, halfspace=+1)
cell2.add_surface(top, halfspace=-1)
cell3.add_surface(fuel1, halfspace=-1)
cell4.add_surface(fuel1, halfspace=+1)
cell5.add_surface(fuel2, halfspace=-1)
cell6.add_surface(fuel2, halfspace=+1)
cell7.add_surface(fuel3, halfspace=-1)
cell8.add_surface(fuel3, halfspace=+1)
# Use surface half-space to define regions
cell1.region = +left & -right & +bottom & -top
cell2.region = +left & -right & +bottom & -top
cell3.region = -fuel1
cell4.region = +fuel1
cell5.region = -fuel2
cell6.region = +fuel2
cell7.region = -fuel3
cell8.region = +fuel3
# Register Materials with Cells
cell3.fill = fuel
@ -168,7 +162,7 @@ plot_file.export_to_xml()
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh.type = 'rectangular'
mesh.type = 'regular'
mesh.dimension = [4, 4]
mesh.lower_left = [-2, -2]
mesh.width = [1, 1]

View file

@ -1,6 +1,5 @@
import openmc
###############################################################################
# Simulation Input File Parameters
###############################################################################
@ -65,17 +64,14 @@ cell5 = openmc.Cell(cell_id=202, name='cell 5')
cell6 = openmc.Cell(cell_id=301, name='cell 6')
cell7 = openmc.Cell(cell_id=302, name='cell 7')
# Register Surfaces with Cells
cell1.add_surface(left, halfspace=+1)
cell1.add_surface(right, halfspace=-1)
cell1.add_surface(bottom, halfspace=+1)
cell1.add_surface(top, halfspace=-1)
cell2.add_surface(fuel1, halfspace=-1)
cell3.add_surface(fuel1, halfspace=+1)
cell4.add_surface(fuel2, halfspace=-1)
cell5.add_surface(fuel2, halfspace=+1)
cell6.add_surface(fuel3, halfspace=-1)
cell7.add_surface(fuel3, halfspace=+1)
# Use surface half-spaces to define regions
cell1.region = +left & -right & +bottom & -top
cell2.region = -fuel1
cell3.region = +fuel1
cell4.region = -fuel2
cell5.region = +fuel2
cell6.region = -fuel3
cell7.region = +fuel3
# Register Materials with Cells
cell2.fill = fuel
@ -157,7 +153,7 @@ plot_file.export_to_xml()
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh.type = 'rectangular'
mesh.type = 'regular'
mesh.dimension = [4, 4]
mesh.lower_left = [-2, -2]
mesh.width = [1, 1]

View file

@ -1,6 +1,5 @@
import openmc
###############################################################################
# Simulation Input File Parameters
###############################################################################
@ -132,17 +131,11 @@ gap = openmc.Cell(cell_id=2, name='cell 2')
clad = openmc.Cell(cell_id=3, name='cell 3')
water = openmc.Cell(cell_id=4, name='cell 4')
# Register Surfaces with Cells
fuel.add_surface(fuel_or, halfspace=-1)
gap.add_surface(fuel_or, halfspace=+1)
gap.add_surface(clad_ir, halfspace=-1)
clad.add_surface(clad_ir, halfspace=+1)
clad.add_surface(clad_or, halfspace=-1)
water.add_surface(clad_or, halfspace=+1)
water.add_surface(left, halfspace=+1)
water.add_surface(right, halfspace=-1)
water.add_surface(bottom, halfspace=+1)
water.add_surface(top, halfspace=-1)
# Use surface half-spaces to define regions
fuel.region = -fuel_or
gap.region = +fuel_or & -clad_ir
clad.region = +clad_ir & -clad_or
water.region = +clad_or & +left & -right & +bottom & -top
# Register Materials with Cells
fuel.fill = uo2
@ -189,7 +182,7 @@ settings_file.export_to_xml()
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh.type = 'rectangular'
mesh.type = 'regular'
mesh.dimension = [100, 100, 1]
mesh.lower_left = [-0.62992, -0.62992, -1.e50]
mesh.upper_right = [0.62992, 0.62992, 1.e50]

View file

@ -1,5 +1,6 @@
import openmc
import numpy as np
import openmc
###############################################################################
# Simulation Input File Parameters
@ -52,13 +53,8 @@ surf6.boundary_type = 'reflective'
# Instantiate Cell
cell = openmc.Cell(cell_id=1, name='cell 1')
# Register Surfaces with Cell
cell.add_surface(surface=surf1, halfspace=+1)
cell.add_surface(surface=surf2, halfspace=-1)
cell.add_surface(surface=surf3, halfspace=+1)
cell.add_surface(surface=surf4, halfspace=-1)
cell.add_surface(surface=surf5, halfspace=+1)
cell.add_surface(surface=surf6, halfspace=-1)
# Use surface half-spaces to define region
cell.region = +surf1 & -surf2 & +surf3 & -surf4 & +surf5 & -surf6
# Register Material with Cell
cell.fill = fuel
@ -88,5 +84,5 @@ settings_file = openmc.SettingsFile()
settings_file.batches = batches
settings_file.inactive = inactive
settings_file.particles = particles
settings_file.set_source_space('box', [-1, -1, -1, 1, 1, 1])
settings_file.set_source_space('box', np.concatenate(cell.region.bounding_box))
settings_file.export_to_xml()

View file

@ -2,14 +2,14 @@
<geometry>
<!-- Definition of Cells -->
<cell id="1" universe="0" fill="37" surfaces="-2" />
<cell id="100" universe="37" material="40" surfaces="-1" />
<cell id="101" universe="37" material="41" surfaces="1" />
<cell id="2" universe="0" material="41" surfaces = "2 -3" />
<cell id="1" universe="0" fill="37" region="-2" />
<cell id="100" universe="37" material="40" region="-1" />
<cell id="101" universe="37" material="41" region="1" />
<cell id="2" universe="0" material="41" region="2 -3" />
<!-- Defition of Surfaces -->
<surface id="1" type="z-cylinder" coeffs="0 0 7" />
<surface id="2" type="z-cylinder" coeffs="0 0 9" />
<surface id="3" type="z-cylinder" coeffs="0 0 11" boundary="vacuum" />
</geometry>

View file

@ -0,0 +1,39 @@
<?xml version="1.0"?>
<geometry>
<!--
This example consists of three nested boxes, and is meant to show how to
use Boolean operators to construct complex cell regions.
-->
<surface id="1" type="x-plane" coeffs="-10" boundary="vacuum" />
<surface id="2" type="x-plane" coeffs="-7" />
<surface id="3" type="x-plane" coeffs="-4" />
<surface id="4" type="x-plane" coeffs="4" />
<surface id="5" type="x-plane" coeffs="7" />
<surface id="6" type="x-plane" coeffs="10" boundary="vacuum" />
<surface id="11" type="y-plane" coeffs="-10" boundary="vacuum" />
<surface id="12" type="y-plane" coeffs="-7" />
<surface id="13" type="y-plane" coeffs="-4" />
<surface id="14" type="y-plane" coeffs="4" />
<surface id="15" type="y-plane" coeffs="7" />
<surface id="16" type="y-plane" coeffs="10" boundary="vacuum" />
<surface id="21" type="z-plane" coeffs="-10" boundary="vacuum" />
<surface id="22" type="z-plane" coeffs="-7" />
<surface id="23" type="z-plane" coeffs="-4" />
<surface id="24" type="z-plane" coeffs="4" />
<surface id="25" type="z-plane" coeffs="7" />
<surface id="26" type="z-plane" coeffs="10" boundary="vacuum" />
<!-- Innermost cube -->
<cell id="1" material="1" region="3 -4 13 -14 23 -24" />
<!-- Middle cubic shell -->
<cell id="2" material="2" region="2 -5 12 -15 22 -25 (-3 | 4 | -13 | 14 | -23 | 24)" />
<!-- Outermost cubic shell -->
<cell id="3" material="3" region="1 -6 11 -16 21 -26 ~(2 -5 12 -15 22 -25)" />
</geometry>

View file

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<materials>
<default_xs>71c</default_xs>
<material id="1">
<density value="4.5" units="g/cc" />
<nuclide name="U-235" ao="1.0" />
</material>
<material id="2">
<density value="4.5" units="g/cc" />
<nuclide name="U-238" ao="1.0" />
</material>
<material id="3">
<density value="1.0" units="g/cc" />
<nuclide name="O-16" ao="1.0" />
<nuclide name="H-1" ao="2.0" />
<sab name="HH2O" xs="71t" />
</material>
</materials>

View file

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<plots>
<plot id="1" type="slice">
<color>cell</color>
<origin>0. 0. 0.</origin>
<width>20. 20.</width>
<pixels>200 200</pixels>
</plot>
</plots>

View file

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<settings>
<!-- Parameters for k-eigenvalue calculation -->
<eigenvalue>
<batches>15</batches>
<inactive>5</inactive>
<particles>10000</particles>
</eigenvalue>
<!-- Starting source -->
<source>
<space type="box" parameters="-10. -10. -10. 10. 10. 10." />
</source>
</settings>

View file

@ -1,14 +1,14 @@
<?xml version="1.0"?>
<geometry>
<cell id="1" fill="6" surfaces="1 -2 3 -4" />
<cell id="2" universe="5" fill="4" surfaces="1 -2 3 -4" />
<cell id="101" universe="1" material="1" surfaces="-5" />
<cell id="102" universe="1" material="2" surfaces="5" />
<cell id="201" universe="2" material="1" surfaces="-6" />
<cell id="202" universe="2" material="2" surfaces="6" />
<cell id="301" universe="3" material="1" surfaces="-7" />
<cell id="302" universe="3" material="2" surfaces="7" />
<cell id="1" fill="6" region="1 -2 3 -4" />
<cell id="2" universe="5" fill="4" region="1 -2 3 -4" />
<cell id="101" universe="1" material="1" region="-5" />
<cell id="102" universe="1" material="2" region="5" />
<cell id="201" universe="2" material="1" region="-6" />
<cell id="202" universe="2" material="2" region="6" />
<cell id="301" universe="3" material="1" region="-7" />
<cell id="302" universe="3" material="2" region="7" />
<!-- 4 x 4 assembly -->
<lattice id="4">

View file

@ -2,7 +2,7 @@
<tallies>
<mesh id="1">
<type>rectangular</type>
<type>regular</type>
<dimension>4 4</dimension>
<lower_left>-2.0 -2.0</lower_left>
<width>1.0 1.0</width>

View file

@ -1,13 +1,13 @@
<?xml version="1.0"?>
<geometry>
<cell id="1" fill="5" surfaces="1 -2 3 -4" />
<cell id="101" universe="1" material="1" surfaces="-5" />
<cell id="102" universe="1" material="2" surfaces="5" />
<cell id="201" universe="2" material="1" surfaces="-6" />
<cell id="202" universe="2" material="2" surfaces="6" />
<cell id="301" universe="3" material="1" surfaces="-7" />
<cell id="302" universe="3" material="2" surfaces="7" />
<cell id="1" fill="5" region="1 -2 3 -4" />
<cell id="101" universe="1" material="1" region="-5" />
<cell id="102" universe="1" material="2" region="5" />
<cell id="201" universe="2" material="1" region="-6" />
<cell id="202" universe="2" material="2" region="6" />
<cell id="301" universe="3" material="1" region="-7" />
<cell id="302" universe="3" material="2" region="7" />
<lattice id="5">
<dimension>4 4</dimension>

View file

@ -2,7 +2,7 @@
<tallies>
<mesh id="1">
<type>rectangular</type>
<type>regular</type>
<dimension>4 4</dimension>
<lower_left>-2.0 -2.0</lower_left>
<width>1.0 1.0</width>

View file

@ -19,9 +19,9 @@
<surface id="6" type="y-plane" coeffs="-0.62992" boundary="reflective" />
<surface id="7" type="y-plane" coeffs=" 0.62992" boundary="reflective" />
<cell id="1" material="1" surfaces=" -1" /> <!-- UO2 Fuel -->
<cell id="2" material="2" surfaces="1 -2" /> <!-- Helium gap -->
<cell id="3" material="3" surfaces="2 -3" /> <!-- Zircaloy cladding -->
<cell id="4" material="4" surfaces="3 4 -5 6 -7" /> <!-- Borated water -->
<cell id="1" material="1" region=" -1" /> <!-- UO2 Fuel -->
<cell id="2" material="2" region="1 -2" /> <!-- Helium gap -->
<cell id="3" material="3" region="2 -3" /> <!-- Zircaloy cladding -->
<cell id="4" material="4" region="3 4 -5 6 -7" /> <!-- Borated water -->
</geometry>

View file

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<tallies>
<mesh id="1" type="rectangular">
<mesh id="1" type="regular">
<dimension>100 100 1</dimension>
<lower_left>-0.62992 -0.62992 -1.e50</lower_left>
<upper_right>0.62992 0.62992 1.e50</upper_right>
@ -13,4 +13,4 @@
<scores>flux fission nu-fission</scores>
</tally>
</tallies>
</tallies>

View file

@ -5,15 +5,15 @@
<cell id="1">
<universe>0</universe>
<material>1</material>
<surfaces>1 -2 3 -4 5 -6</surfaces>
<region>1 -2 3 -4 5 -6</region>
</cell>
<!-- Defition of Surfaces -->
<surface id="1" type="x-plane" coeffs="-1" boundary="vacuum" />
<surface id="2" type="x-plane" coeffs="1" boundary="vacuum" />
<surface id="3" type="y-plane" coeffs="-1" boundary="reflective" />
<surface id="3" type="y-plane" coeffs="-1" boundary="reflective" />
<surface id="4" type="y-plane" coeffs="1" boundary="reflective" />
<surface id="5" type="z-plane" coeffs="-1" boundary="reflective" />
<surface id="5" type="z-plane" coeffs="-1" boundary="reflective" />
<surface id="6" type="z-plane" coeffs="1" boundary="reflective" />
</geometry>

View file

@ -12,6 +12,8 @@ from openmc.trigger import *
from openmc.tallies import *
from openmc.cmfd import *
from openmc.executor import *
from openmc.statepoint import *
from openmc.summary import *
try:
from openmc.opencg_compatible import *

View file

@ -1,7 +1,7 @@
def sort_xml_elements(tree):
# Retrieve all children of the root XML node in the tree
elements = tree.getchildren()
elements = list(tree)
# Initialize empty lists for the sorted and comment elements
sorted_elements = []
@ -29,7 +29,7 @@ def sort_xml_elements(tree):
comment_elements.append((element, next_element))
# Now iterate over all tags and order the elements within each tag
for tag in tags:
for tag in sorted(list(tags)):
# Retrieve all of the elements for this tag
try:

View file

@ -1,123 +0,0 @@
"""Dictionaries of integer-to-string mappings from openmc/src/constants.F90"""
SURFACE_TYPES = {1: 'x-plane',
2: 'y-plane',
3: 'z-plane',
4: 'plane',
5: 'x-cylinder',
6: 'y-cylinder',
7: 'z-cylinder',
8: 'sphere',
9: 'x-cone',
10: 'y-cone',
11: 'z-cone'}
BC_TYPES = {0: 'transmission',
1: 'vacuum',
2: 'reflective',
3: 'periodic'}
FILL_TYPES = {1: 'normal',
2: 'fill',
3: 'lattice'}
LATTICE_TYPES = {1: 'rectangular',
2: 'hexagonal'}
ESTIMATOR_TYPES = {1: 'analog',
2: 'tracklength'}
FILTER_TYPES = {1: 'universe',
2: 'material',
3: 'cell',
4: 'cellborn',
5: 'surface',
6: 'mesh',
7: 'energy',
8: 'energyout',
9: 'distribcell'}
SCORE_TYPES = {-1: 'flux',
-2: 'total',
-3: 'scatter',
-4: 'nu-scatter',
-5: 'scatter-n',
-6: 'scatter-pn',
-7: 'nu-scatter-n',
-8: 'nu-scatter-pn',
-9: 'transport',
-10: 'n1n',
-11: 'absorption',
-12: 'fission',
-13: 'nu-fission',
-14: 'kappa-fission',
-15: 'current',
-16: 'flux-yn',
-17: 'total-yn',
-18: 'scatter-yn',
-19: 'nu-scatter-yn',
-20: 'events',
1: '(n,total)',
2: '(n,elastic)',
4: '(n,level)',
11: '(n,2nd)',
16: '(n,2n)',
17: '(n,3n)',
18: '(n,fission)',
19: '(n,f)',
20: '(n,nf)',
21: '(n,2nf)',
22: '(n,na)',
23: '(n,n3a)',
24: '(n,2na)',
25: '(n,3na)',
28: '(n,np)',
29: '(n,n2a)',
30: '(n,2n2a)',
32: '(n,nd)',
33: '(n,nt)',
34: '(n,nHe-3)',
35: '(n,nd2a)',
36: '(n,nt2a)',
37: '(n,4n)',
38: '(n,3nf)',
41: '(n,2np)',
42: '(n,3np)',
44: '(n,n2p)',
45: '(n,npa)',
91: '(n,nc)',
101: '(n,disappear)',
102: '(n,gamma)',
103: '(n,p)',
104: '(n,d)',
105: '(n,t)',
106: '(n,3He)',
107: '(n,a)',
108: '(n,2a)',
109: '(n,3a)',
111: '(n,2p)',
112: '(n,pa)',
113: '(n,t2a)',
114: '(n,d2a)',
115: '(n,pd)',
116: '(n,pt)',
117: '(n,da)',
201: '(n,Xn)',
202: '(n,Xgamma)',
203: '(n,Xp)',
204: '(n,Xd)',
205: '(n,Xt)',
206: '(n,X3He)',
207: '(n,Xa)',
444: '(damage)',
649: '(n,pc)',
699: '(n,dc)',
749: '(n,tc)',
799: '(n,3Hec)',
849: '(n,tc)'}
SCORE_TYPES.update({MT: '(n,n' + str(MT-50) + ')' for MT in range(51,91)})
SCORE_TYPES.update({MT: '(n,p' + str(MT-600) + ')' for MT in range(600,649)})
SCORE_TYPES.update({MT: '(n,d' + str(MT-650) + ')' for MT in range(650,699)})
SCORE_TYPES.update({MT: '(n,t' + str(MT-700) + ')' for MT in range(700,749)})
SCORE_TYPES.update({MT: '(n,3He' + str(MT-750) + ')' for MT in range(750,649)})
SCORE_TYPES.update({MT: '(n,a' + str(MT-800) + ')' for MT in range(800,849)})

View file

@ -1,9 +1,20 @@
import sys
from openmc import Filter, Nuclide
from openmc.filter import _FILTER_TYPES
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
# Acceptable tally arithmetic binary operations
_TALLY_ARITHMETIC_OPS = ['+', '-', '*', '/', '^']
class CrossScore(object):
"""A special-purpose tally score used to encapsulate all combinations of two
tally's scores as a outer product for tally arithmetic.
tally's scores as an outer product for tally arithmetic.
Parameters
----------
@ -40,6 +51,38 @@ class CrossScore(object):
if binary_op is not None:
self.binary_op = binary_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._left_score = self.left_score
clone._right_score = self.right_score
clone._binary_op = self.binary_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
string = '({0} {1} {2})'.format(self.left_score,
self.binary_op, self.right_score)
return string
@property
def left_score(self):
return self._left_score
@ -54,28 +97,24 @@ class CrossScore(object):
@left_score.setter
def left_score(self, left_score):
cv.check_type('left_score', left_score, (basestring, CrossScore))
self._left_score = left_score
@right_score.setter
def right_score(self, right_score):
cv.check_type('right_score', right_score, (basestring, CrossScore))
self._right_score = right_score
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, (basestring, CrossScore))
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
def __eq__(self, other):
return str(other) == str(self)
def __repr__(self):
string = '({0} {1} {2})'.format(self.left_score,
self.binary_op, self.right_score)
return string
class CrossNuclide(object):
"""A special-purpose nuclide used to encapsulate all combinations of two
tally's nuclides as a outer product for tally arithmetic.
tally's nuclides as an outer product for tally arithmetic.
Parameters
----------
@ -112,33 +151,33 @@ class CrossNuclide(object):
if binary_op is not None:
self.binary_op = binary_op
@property
def left_nuclide(self):
return self._left_nuclide
@property
def right_nuclide(self):
return self._right_nuclide
@property
def binary_op(self):
return self._binary_op
@left_nuclide.setter
def left_nuclide(self, left_nuclide):
self._left_nuclide = left_nuclide
@right_nuclide.setter
def right_nuclide(self, right_nuclide):
self._right_nuclide = right_nuclide
@binary_op.setter
def binary_op(self, binary_op):
self._binary_op = binary_op
def __hash__(self):
return hash(repr(self))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._left_nuclide = self.left_nuclide
clone._right_nuclide = self.right_nuclide
clone._binary_op = self.binary_op
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __repr__(self):
string = ''
@ -161,10 +200,38 @@ class CrossNuclide(object):
return string
@property
def left_nuclide(self):
return self._left_nuclide
@property
def right_nuclide(self):
return self._right_nuclide
@property
def binary_op(self):
return self._binary_op
@left_nuclide.setter
def left_nuclide(self, left_nuclide):
cv.check_type('left_nuclide', left_nuclide, (Nuclide, CrossNuclide))
self._left_nuclide = left_nuclide
@right_nuclide.setter
def right_nuclide(self, right_nuclide):
cv.check_type('right_nuclide', right_nuclide, (Nuclide, CrossNuclide))
self._right_nuclide = right_nuclide
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, basestring)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
class CrossFilter(object):
"""A special-purpose filter used to encapsulate all combinations of two
tally's filter bins as a outer product for tally arithmetic.
tally's filter bins as an outer product for tally arithmetic.
Parameters
----------
@ -192,12 +259,10 @@ class CrossFilter(object):
left_type = left_filter.type
right_type = right_filter.type
self.type = '({0} {1} {2})'.format(left_type, binary_op, right_type)
self._type = '({0} {1} {2})'.format(left_type, binary_op, right_type)
self._bins = {}
self._bins['left'] = left_filter.bins
self._bins['right'] = right_filter.bins
self._num_bins = left_filter.num_bins * right_filter.num_bins
self._stride = None
self._left_filter = None
self._right_filter = None
@ -205,13 +270,34 @@ class CrossFilter(object):
if left_filter is not None:
self.left_filter = left_filter
self._bins['left'] = left_filter.bins
if right_filter is not None:
self.right_filter = right_filter
self._bins['right'] = right_filter.bins
if binary_op is not None:
self.binary_op = binary_op
def __hash__(self):
return hash((self.type, self.bins))
return hash((self.left_filter, self.right_filter))
def __eq__(self, other):
return str(other) == str(self)
def __ne__(self, other):
return not self == other
def __repr__(self):
string = 'CrossFilter\n'
filter_type = '({0} {1} {2})'.format(self.left_filter.type,
self.binary_op,
self.right_filter.type)
filter_bins = '({0} {1} {2})'.format(self.left_filter.bins,
self.binary_op,
self.right_filter.bins)
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', filter_type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', filter_bins)
return string
def __deepcopy__(self, memo):
existing = memo.get(id(self))
@ -221,9 +307,12 @@ class CrossFilter(object):
clone = type(self).__new__(type(self))
clone._left_filter = self.left_filter
clone._right_filter = self.right_filter
clone._binary_op = self.binary_op
clone._type = self.type
clone._bins = self.bins
clone._bins = self._bins
clone._num_bins = self.num_bins
clone._stride = self.stride
memo[id(self)] = clone
return clone
@ -250,54 +339,49 @@ class CrossFilter(object):
@property
def bins(self):
return (self._bins['left'], self._bins['right'])
return self._bins['left'], self._bins['right']
@property
def num_bins(self):
return self._num_bins
if self.left_filter is not None and self.right_filter is not None:
return self.left_filter.num_bins * self.right_filter.num_bins
else:
return 0
@property
def stride(self):
return self.left_filter.stride * self.right_filter.stride
return self._stride
@type.setter
def type(self, filter_type):
if filter_type not in _FILTER_TYPES.values():
msg = 'Unable to set Filter type to "{0}" since it is not one ' \
'of the supported types'.format(filter_type)
raise ValueError(msg)
self._type = filter_type
@left_filter.setter
def left_filter(self, left_filter):
cv.check_type('left_filter', left_filter, (Filter, CrossFilter))
self._left_filter = left_filter
self._bins['left'] = left_filter.bins
@right_filter.setter
def right_filter(self, right_filter):
cv.check_type('right_filter', right_filter, (Filter, CrossFilter))
self._right_filter = right_filter
self._bins['right'] = right_filter.bins
@binary_op.setter
def binary_op(self, binary_op):
cv.check_type('binary_op', binary_op, basestring)
cv.check_value('binary_op', binary_op, _TALLY_ARITHMETIC_OPS)
self._binary_op = binary_op
def __eq__(self, other):
return str(other) == str(self)
def split_filters(self):
split_filters = []
# If left Filter is not a CrossFilter, simply append to list
if isinstance(self.left_filter, Filter):
split_filters.append(self.left_filter)
# Recursively descend CrossFilter tree to collect all Filters
else:
split_filters.extend(self.left_filter.split_filters())
# If right Filter is not a CrossFilter, simply append to list
if isinstance(self.right_filter, Filter):
split_filters.append(self.right_filter)
# Recursively descend CrossFilter tree to collect all Filters
else:
split_filters.extend(self.right_filter.split_filters())
return split_filters
@stride.setter
def stride(self, stride):
self._stride = stride
def get_bin_index(self, filter_bin):
"""Returns the index in the CrossFilter for some bin.
@ -316,7 +400,7 @@ class CrossFilter(object):
Returns
-------
filter_index : int
filter_index : Integral
The index in the Tally data array for this filter bin.
"""
@ -326,15 +410,56 @@ class CrossFilter(object):
filter_index = left_index * self.right_filter.num_bins + right_index
return filter_index
def __repr__(self):
def get_pandas_dataframe(self, datasize, summary=None):
"""Builds a Pandas DataFrame for the CrossFilter's bins.
string = 'CrossFilter\n'
filter_type = '({0} {1} {2})'.format(self.left_filter.type,
self.binary_op,
self.right_filter.type)
filter_bins = '({0} {1} {2})'.format(self.left_filter.bins,
self.binary_op,
self.right_filter.bins)
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', filter_type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', filter_bins)
return string
This method constructs a Pandas DataFrame object for the CrossFilter
with columns annotated by filter bin information. This is a helper
method for the Tally.get_pandas_dataframe(...) method. This method
recursively builds and concatenates Pandas DataFrames for the left
and right filters and crossfilters.
This capability has been tested for Pandas >=0.13.1. However, it is
recommended to use v0.16 or newer versions of Pandas since this method
uses Pandas' Multi-index functionality.
Parameters
----------
datasize : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a Multi-index
column with a geometric "path" to each distribcell instance.
NOTE: This option requires the OpenCG Python package.
Returns
-------
pandas.DataFrame
A Pandas DataFrame with columns of strings that characterize the
crossfilter's bins. Each entry in the DataFrame will include one
or more binary operations used to construct the crossfilter's bins.
The number of rows in the DataFrame is the same as the total number
of bins in the corresponding tally, with the filter bins
appropriately tiled to map to the corresponding tally bins.
See also
--------
Tally.get_pandas_dataframe(), Filter.get_pandas_dataframe()
"""
# If left and right filters are identical, do not combine bins
if self.left_filter == self.right_filter:
df = self.left_filter.get_pandas_dataframe(datasize, summary)
# If left and right filters are different, combine their bins
else:
left_df = self.left_filter.get_pandas_dataframe(datasize, summary)
right_df = self.right_filter.get_pandas_dataframe(datasize, summary)
left_df = left_df.astype(str)
right_df = right_df.astype(str)
df = '(' + left_df + ' ' + self.binary_op + ' ' + right_df + ')'
return df

View file

@ -24,6 +24,8 @@ class Element(object):
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
scattering : 'data' or 'iso-in-lab' or None
The type of angular scattering distribution to use
"""
@ -31,6 +33,7 @@ class Element(object):
# Initialize class attributes
self._name = ''
self._xs = None
self._scattering = None
# Set class attributes
self.name = name
@ -38,21 +41,33 @@ class Element(object):
if xs is not None:
self.xs = xs
def __eq__(self, element2):
# Check type
if not isinstance(element2, Element):
def __eq__(self, other):
if isinstance(other, Element):
if self._name != other._name:
return False
elif self._xs != other._xs:
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
return True
else:
return False
# Check name and xs
if self._name != element2._name:
return False
elif self._xs != element2._xs:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self._name, self._xs))
return hash(repr(self))
def __repr__(self):
string = 'Element - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
if self.scattering is not None:
string += '{0: <16}{1}{2}\n'.format('\tscattering', '=\t',
self.scattering)
return string
@property
def xs(self):
@ -62,6 +77,10 @@ class Element(object):
def name(self):
return self._name
@property
def scattering(self):
return self._scattering
@xs.setter
def xs(self, xs):
check_type('cross section identifier', xs, basestring)
@ -72,7 +91,12 @@ class Element(object):
check_type('name', name, basestring)
self._name = name
def __repr__(self):
string = 'Element - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
return string
@scattering.setter
def scattering(self, scattering):
if not scattering in ['data', 'iso-in-lab']:
msg = 'Unable to set scattering for Element to {0} ' \
'which is not "data" or "iso-in-lab"'.format(scattering)
raise ValueError(msg)
self._scattering = scattering

View file

@ -30,14 +30,16 @@ class Executor(object):
stdout=subprocess.PIPE)
# Capture and re-print OpenMC output in real-time
while (True and output):
line = p.stdout.readline()
print(line, end='')
while True:
# If OpenMC is finished, break loop
line = p.stdout.readline()
if not line and p.poll() != None:
break
# If user requested output, print to screen
if output:
print(line, end='')
# Return the returncode (integer, zero if no problems encountered)
return p.returncode

View file

@ -1,17 +1,26 @@
from collections import Iterable
import copy
from numbers import Real, Integral
import sys
import numpy as np
from openmc import Mesh
from openmc.constants import *
from openmc.checkvalue import check_type, check_iterable_type, \
check_greater_than
from openmc.summary import Summary
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
_FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface',
'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal',
'distribcell', 'delayedgroup']
class Filter(object):
"""A filter used to constrain a tally to a specific criterion, e.g. only tally
events when the particle is in a certain cell and energy range.
"""A filter used to constrain a tally to a specific criterion, e.g. only
tally events when the particle is in a certain cell and energy range.
Parameters
----------
@ -19,46 +28,60 @@ class Filter(object):
The type of the tally filter. Acceptable values are "universe",
"material", "cell", "cellborn", "surface", "mesh", "energy",
"energyout", and "distribcell".
bins : int or Iterable of int or Iterable of float
bins : Integral or Iterable of Integral or Iterable of Real
The bins for the filter. This takes on different meaning for different
filters.
filters. See the OpenMC online documentation for more details.
Attributes
----------
type : str
The type of the tally filter.
bins : int or Iterable of int or Iterable of float
bins : Integral or Iterable of Integral or Iterable of Real
The bins for the filter
num_bins : Integral
The number of filter bins
mesh : Mesh or None
A Mesh object for 'mesh' type filters.
offset : Integral
A value used to index tally bins for 'distribcell' tallies.
stride : Integral
The number of filter, nuclide and score bins within each of this
filter's bins.
"""
# Initialize Filter class attributes
def __init__(self, type=None, bins=None):
self.type = type
self._type = None
self._num_bins = 0
self.bins = bins
self._bins = None
self._mesh = None
self._offset = -1
self._stride = None
def __eq__(self, filter2):
# Check type
if self.type != filter2.type:
return False
if type is not None:
self.type = type
if bins is not None:
self.bins = bins
# Check number of bins
elif len(self.bins) != len(filter2.bins):
def __eq__(self, other):
if not isinstance(other, Filter):
return False
# Check bin edges
elif not np.allclose(self.bins, filter2.bins):
elif self.type != other.type:
return False
elif len(self.bins) != len(other.bins):
return False
elif not np.allclose(self.bins, other.bins):
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self._type, self._bins))
return hash(repr(self))
def __deepcopy__(self, memo):
existing = memo.get(id(self))
@ -81,6 +104,13 @@ class Filter(object):
else:
return existing
def __repr__(self):
string = 'Filter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins)
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self.offset)
return string
@property
def type(self):
return self._type
@ -91,7 +121,14 @@ class Filter(object):
@property
def num_bins(self):
return self._num_bins
if self.bins is None:
return 0
elif self.type in ['energy', 'energyout']:
return len(self.bins) - 1
elif self.type in ['cell', 'cellborn', 'surface', 'universe', 'material']:
return len(self.bins)
else:
return self._num_bins
@property
def mesh(self):
@ -109,7 +146,7 @@ class Filter(object):
def type(self, type):
if type is None:
self._type = type
elif type not in FILTER_TYPES.values():
elif type not in _FILTER_TYPES:
msg = 'Unable to set Filter type to "{0}" since it is not one ' \
'of the supported types'.format(type)
raise ValueError(msg)
@ -118,9 +155,7 @@ class Filter(object):
@bins.setter
def bins(self, bins):
if bins is None:
self.num_bins = 0
elif self._type is None:
if self.type is None:
msg = 'Unable to set bins for Filter to "{0}" since ' \
'the Filter type has not yet been set'.format(bins)
raise ValueError(msg)
@ -134,14 +169,14 @@ class Filter(object):
bins = list(bins)
if self.type in ['cell', 'cellborn', 'surface', 'material',
'universe', 'distribcell']:
check_iterable_type('filter bins', bins, Integral)
'universe', 'distribcell', 'delayedgroup']:
cv.check_iterable_type('filter bins', bins, Integral)
for edge in bins:
check_greater_than('filter bin', edge, 0, equality=True)
cv.check_greater_than('filter bin', edge, 0, equality=True)
elif self._type in ['energy', 'energyout']:
elif self.type in ['energy', 'energyout']:
for edge in bins:
if not isinstance(edge, Real):
if not cv._isinstance(edge, Real):
msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \
'since it is a non-integer or floating point ' \
'value'.format(edge, self.type)
@ -160,12 +195,12 @@ class Filter(object):
raise ValueError(msg)
# mesh filters
elif self._type == 'mesh':
elif self.type == 'mesh':
if not len(bins) == 1:
msg = 'Unable to add bins "{0}" to a mesh Filter since ' \
'only a single mesh can be used per tally'.format(bins)
raise ValueError(msg)
elif not isinstance(bins[0], Integral):
elif not cv._isinstance(bins[0], Integral):
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a non-integer'.format(bins[0])
raise ValueError(msg)
@ -177,16 +212,15 @@ class Filter(object):
# If all error checks passed, add bin edges
self._bins = np.array(bins)
# FIXME
@num_bins.setter
def num_bins(self, num_bins):
check_type('filter num_bins', num_bins, Integral)
check_greater_than('filter num_bins', num_bins, 0, equality=True)
cv.check_type('filter num_bins', num_bins, Integral)
cv.check_greater_than('filter num_bins', num_bins, 0, equality=True)
self._num_bins = num_bins
@mesh.setter
def mesh(self, mesh):
check_type('filter mesh', mesh, Mesh)
cv.check_type('filter mesh', mesh, Mesh)
self._mesh = mesh
self.type = 'mesh'
@ -194,12 +228,12 @@ class Filter(object):
@offset.setter
def offset(self, offset):
check_type('filter offset', offset, Integral)
cv.check_type('filter offset', offset, Integral)
self._offset = offset
@stride.setter
def stride(self, stride):
check_type('filter stride', stride, Integral)
cv.check_type('filter stride', stride, Integral)
if stride < 0:
msg = 'Unable to set stride "{0}" for a "{1}" Filter since it ' \
'is a negative value'.format(stride, self.type)
@ -268,31 +302,69 @@ class Filter(object):
merged_filter = copy.deepcopy(self)
# Merge unique filter bins
merged_bins = list(set(self.bins + filter.bins))
merged_bins = list(set(np.concatenate((self.bins, filter.bins))))
merged_filter.bins = merged_bins
merged_filter.num_bins = len(merged_bins)
return merged_filter
def is_subset(self, other):
"""Determine if another filter is a subset of this filter.
If all of the bins in the other filter are included as bins in this
filter, then it is a subset of this filter.
Parameters
----------
other : Filter
The filter to query as a subset of this filter
Returns
-------
bool
Whether or not the other filter is a subset of this filter
"""
if not isinstance(other, Filter):
return False
elif self.type != other.type:
return False
elif self.type in ['energy', 'energyout']:
if len(self.bins) != len(other.bins):
return False
else:
return np.allclose(self.bins, other.bins)
for bin in other.bins:
if bin not in self.bins:
return False
return True
def get_bin_index(self, filter_bin):
"""Returns the index in the Filter for some bin.
Parameters
----------
filter_bin : int or tuple
filter_bin : Integral or tuple
The bin is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. The bin is an integer for the
cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of
floats for 'energy' and 'energyout' filters corresponding to the
energy boundaries of the bin of interest. The bin is a (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell of
energy boundaries of the bin of interest. The bin is an (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell
interest.
Returns
-------
filter_index : int
filter_index : Integral
The index in the Tally data array for this filter bin.
See also
--------
Filter.get_bin()
"""
try:
@ -314,10 +386,14 @@ class Filter(object):
# Use lower energy bound to find index for energy Filters
elif self.type in ['energy', 'energyout']:
val = np.where(self.bins == filter_bin[0])[0][0]
filter_index = val
deltas = np.abs(self.bins - filter_bin[1]) / filter_bin[1]
min_delta = np.min(deltas)
if min_delta < 1E-3:
filter_index = deltas.argmin() - 1
else:
raise ValueError
# Filter bins for distribcell are the "IDs" of each unique placement
# Filter bins for distribcells are "IDs" of each unique placement
# of the Cell in the Geometry (integers starting at 0)
elif self.type == 'distribcell':
filter_index = filter_bin
@ -329,14 +405,354 @@ class Filter(object):
except ValueError:
msg = 'Unable to get the bin index for Filter since "{0}" ' \
'is not one of the bins'.format(filter_bin)
'is not one of the bins'.format(filter_bin)
raise ValueError(msg)
return filter_index
def __repr__(self):
string = 'Filter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self.type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self.bins)
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self.offset)
return string
def get_bin(self, bin_index):
"""Returns the filter bin for some filter bin index.
Parameters
----------
bin_index : Integral
The zero-based index into the filter's array of bins. The bin
index for 'material', 'surface', 'cell', 'cellborn', and 'universe'
filters corresponds to the ID in the filter's list of bins. For
'distribcell' tallies the bin index necessarily can only be zero
since only one cell can be tracked per tally. The bin index for
'energy' and 'energyout' filters corresponds to the energy range of
interest in the filter bins of energies. The bin index for 'mesh'
filters is the index into the flattened array of (x,y) or (x,y,z)
mesh cell bins.
Returns
-------
bin : 1-, 2-, or 3-tuple of Real
The bin in the Tally data array. The bin for 'material', surface',
'cell', 'cellborn', 'universe' and 'distribcell' filters is a
1-tuple of the ID corresponding to the appropriate filter bin.
The bin for 'energy' and 'energyout' filters is a 2-tuple of the
lower and upper energies bounding the energy interval for the filter
bin. The bin for 'mesh' tallies is a 2-tuple or 3-tuple of the x,y
or x,y,z mesh cell indices corresponding to the bin in a 2D/3D mesh.
See also
--------
Filter.get_bin_index()
"""
cv.check_type('bin_index', bin_index, Integral)
cv.check_greater_than('bin_index', bin_index, 0, equality=True)
cv.check_less_than('bin_index', bin_index, self.num_bins)
if self.type == 'mesh':
# Construct 3-tuple of x,y,z cell indices for a 3D mesh
if len(self.mesh.dimension) == 3:
nx, ny, nz = self.mesh.dimension
x = bin_index / (ny * nz)
y = (bin_index - (x * ny * nz)) / nz
z = bin_index - (x * ny * nz) - (y * nz)
filter_bin = (x, y, z)
# Construct 2-tuple of x,y cell indices for a 2D mesh
else:
nx, ny = self.mesh.dimension
x = bin_index / ny
y = bin_index - (x * ny)
filter_bin = (x, y)
# Construct 2-tuple of lower, upper energies for energy(out) filters
elif self.type in ['energy', 'energyout']:
filter_bin = (self.bins[bin_index], self.bins[bin_index+1])
# Construct 1-tuple of with the cell ID for distribcell filters
elif self.type == 'distribcell':
filter_bin = (self.bins[0],)
# Construct 1-tuple with domain ID (e.g., material) for other filters
else:
filter_bin = (self.bins[bin_index],)
return filter_bin
def get_pandas_dataframe(self, data_size, summary=None):
"""Builds a Pandas DataFrame for the Filter's bins.
This method constructs a Pandas DataFrame object for the filter with
columns annotated by filter bin information. This is a helper method
for the Tally.get_pandas_dataframe(...) method.
This capability has been tested for Pandas >=0.13.1. However, it is
recommended to use v0.16 or newer versions of Pandas since this method
uses Pandas' Multi-index functionality.
Parameters
----------
data_size : Integral
The total number of bins in the tally corresponding to this filter
summary : None or Summary
An optional Summary object to be used to construct columns for
distribcell tally filters (default is None). The geometric
information in the Summary object is embedded into a Multi-index
column with a geometric "path" to each distribcell instance.
NOTE: This option requires the OpenCG Python package.
Returns
-------
pandas.DataFrame
A Pandas DataFrame with columns of strings that characterize the
filter's bins. The number of rows in the DataFrame is the same as
the total number of bins in the corresponding tally, with the filter
bin appropriately tiled to map to the corresponding tally bins.
For 'cell', 'cellborn', 'surface', 'material', and 'universe'
filters, the DataFrame includes a single column with the cell,
surface, material or universe ID corresponding to each filter bin.
For 'distribcell' filters, the DataFrame either includes:
1. a single column with the cell instance IDs (without summary info)
2. separate columns for the cell IDs, universe IDs, and lattice IDs
and x,y,z cell indices corresponding to each (with summary info).
For 'energy' and 'energyout' filters, the DataFrame include a single
column with each element comprising a string with the lower, upper
energy bounds for each filter bin.
For 'mesh' filters, the DataFrame includes three columns for the
x,y,z mesh cell indices corresponding to each filter bin.
Raises
------
ImportError
When Pandas is not installed, or summary info is requested but
OpenCG is not installed.
See also
--------
Tally.get_pandas_dataframe(), CrossFilter.get_pandas_dataframe()
"""
# Attempt to import Pandas
try:
import pandas as pd
except ImportError:
msg = 'The Pandas Python package must be installed on your system'
raise ImportError(msg)
# Initialize Pandas DataFrame
df = pd.DataFrame()
# mesh filters
if self.type == 'mesh':
# Initialize dictionary to build Pandas Multi-index column
filter_dict = {}
# Append Mesh ID as outermost index of mult-index
mesh_key = 'mesh {0}'.format(self.mesh.id)
# Find mesh dimensions - use 3D indices for simplicity
if (len(self.mesh.dimension) == 3):
nx, ny, nz = self.mesh.dimension
else:
nx, ny = self.mesh.dimension
nz = 1
# Generate multi-index sub-column for x-axis
filter_bins = np.arange(1, nx+1)
repeat_factor = ny * nz * self.stride
filter_bins = np.repeat(filter_bins, repeat_factor)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_dict[(mesh_key, 'x')] = filter_bins
# Generate multi-index sub-column for y-axis
filter_bins = np.arange(1, ny+1)
repeat_factor = nz * self.stride
filter_bins = np.repeat(filter_bins, repeat_factor)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_dict[(mesh_key, 'y')] = filter_bins
# Generate multi-index sub-column for z-axis
filter_bins = np.arange(1, nz+1)
repeat_factor = self.stride
filter_bins = np.repeat(filter_bins, repeat_factor)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_dict[(mesh_key, 'z')] = filter_bins
# Initialize a Pandas DataFrame from the mesh dictionary
df = pd.concat([df, pd.DataFrame(filter_dict)])
# distribcell filters
elif self.type == 'distribcell':
level_df = None
if isinstance(summary, Summary):
# Attempt to import the OpenCG package
try:
import opencg
except ImportError:
msg = 'The OpenCG package must be installed ' \
'to use a Summary for distribcell dataframes'
raise ImportError(msg)
# Extract the OpenCG geometry from the Summary
opencg_geometry = summary.opencg_geometry
openmc_geometry = summary.openmc_geometry
# Use OpenCG to compute the number of regions
opencg_geometry.initialize_cell_offsets()
num_regions = opencg_geometry.num_regions
# Initialize a dictionary mapping OpenMC distribcell
# offsets to OpenCG LocalCoords linked lists
offsets_to_coords = {}
# Use OpenCG to compute LocalCoords linked list for
# each region and store in dictionary
for region in range(num_regions):
coords = opencg_geometry.find_region(region)
path = opencg.get_path(coords)
cell_id = path[-1]
# If this region is in Cell corresponding to the
# distribcell filter bin, store it in dictionary
if cell_id == self.bins[0]:
offset = openmc_geometry.get_offset(path, self.offset)
offsets_to_coords[offset] = coords
# Each distribcell offset is a DataFrame bin
# Unravel the paths into DataFrame columns
num_offsets = len(offsets_to_coords)
# Initialize termination condition for while loop
levels_remain = True
counter = 0
# Iterate over each level in the CSG tree hierarchy
while levels_remain:
levels_remain = False
# Initialize dictionary to build Pandas Multi-index
# column for this level in the CSG tree hierarchy
level_dict = {}
# Initialize prefix Multi-index keys
counter += 1
level_key = 'level {0}'.format(counter)
univ_key = (level_key, 'univ', 'id')
cell_key = (level_key, 'cell', 'id')
lat_id_key = (level_key, 'lat', 'id')
lat_x_key = (level_key, 'lat', 'x')
lat_y_key = (level_key, 'lat', 'y')
lat_z_key = (level_key, 'lat', 'z')
# Allocate NumPy arrays for each CSG level and
# each Multi-index column in the DataFrame
level_dict[univ_key] = np.empty(num_offsets)
level_dict[cell_key] = np.empty(num_offsets)
level_dict[lat_id_key] = np.empty(num_offsets)
level_dict[lat_x_key] = np.empty(num_offsets)
level_dict[lat_y_key] = np.empty(num_offsets)
level_dict[lat_z_key] = np.empty(num_offsets)
# Initialize Multi-index columns to NaN - this is
# necessary since some distribcell instances may
# have very different LocalCoords linked lists
level_dict[univ_key][:] = np.NAN
level_dict[cell_key][:] = np.NAN
level_dict[lat_id_key][:] = np.NAN
level_dict[lat_x_key][:] = np.NAN
level_dict[lat_y_key][:] = np.NAN
level_dict[lat_z_key][:] = np.NAN
# Iterate over all regions (distribcell instances)
for offset in range(num_offsets):
coords = offsets_to_coords[offset]
# If entire LocalCoords has been unraveled into
# Multi-index columns already, continue
if coords is None:
continue
# Assign entry to Universe Multi-index column
if coords._type == 'universe':
level_dict[univ_key][offset] = coords._universe._id
level_dict[cell_key][offset] = coords._cell._id
# Assign entry to Lattice Multi-index column
else:
level_dict[lat_id_key][offset] = coords._lattice._id
level_dict[lat_x_key][offset] = coords._lat_x
level_dict[lat_y_key][offset] = coords._lat_y
level_dict[lat_z_key][offset] = coords._lat_z
# Move to next node in LocalCoords linked list
if coords._next is None:
offsets_to_coords[offset] = None
else:
offsets_to_coords[offset] = coords._next
levels_remain = True
# Tile the Multi-index columns
for level_key, level_bins in level_dict.items():
level_bins = np.repeat(level_bins, self.stride)
tile_factor = data_size / len(level_bins)
level_bins = np.tile(level_bins, tile_factor)
level_dict[level_key] = level_bins
# Initialize a Pandas DataFrame from the level dictionary
if level_df is None:
level_df = pd.DataFrame(level_dict)
else:
level_df = pd.concat([level_df, pd.DataFrame(level_dict)], axis=1)
# Create DataFrame column for distribcell instances IDs
# NOTE: This is performed regardless of whether the user
# requests Summary geometric information
filter_bins = np.arange(self.num_bins)
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.DataFrame({self.type : filter_bins})
# If OpenCG level info DataFrame was created, concatenate
# with DataFrame of distribcell instance IDs
if level_df is not None:
level_df = level_df.dropna(axis=1, how='all')
level_df = level_df.astype(np.int)
df = pd.concat([level_df, df], axis=1)
# energy, energyout filters
elif 'energy' in self.type:
bins = self.bins
num_bins = self.num_bins
# Create strings for
template = '({0:.1e} - {1:.1e})'
filter_bins = []
for i in range(num_bins):
filter_bins.append(template.format(bins[i], bins[i+1]))
# Tile the energy bins into a DataFrame column
filter_bins = np.repeat(filter_bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.concat([df, pd.DataFrame({self.type + ' [MeV]' : filter_bins})])
# universe, material, surface, cell, and cellborn filters
else:
filter_bins = np.repeat(self.bins, self.stride)
tile_factor = data_size / len(filter_bins)
filter_bins = np.tile(filter_bins, tile_factor)
filter_bins = filter_bins
df = pd.concat([df, pd.DataFrame({self.type : filter_bins})])
return df

View file

@ -1,3 +1,4 @@
from collections import OrderedDict
from xml.etree import ElementTree as ET
import openmc
@ -110,7 +111,7 @@ class Geometry(object):
"""
nuclides = {}
nuclides = OrderedDict()
materials = self.get_all_materials()
for material in materials:
@ -134,7 +135,9 @@ class Geometry(object):
for cell in material_cells:
materials.add(cell._fill)
return list(materials)
materials = list(materials)
materials.sort(key=lambda x: x.id)
return materials
def get_all_material_cells(self):
all_cells = self.get_all_cells()
@ -144,7 +147,9 @@ class Geometry(object):
if cell._type == 'normal':
material_cells.add(cell)
return list(material_cells)
material_cells = list(material_cells)
material_cells.sort(key=lambda x: x.id)
return material_cells
def get_all_material_universes(self):
"""Return all universes composed of at least one non-fill cell
@ -165,7 +170,9 @@ class Geometry(object):
if cell._type == 'normal':
material_universes.add(universe)
return list(material_universes)
material_universes = list(material_universes)
material_universes.sort(key=lambda x: x.id)
return material_universes
class GeometryFile(object):
@ -198,7 +205,13 @@ class GeometryFile(object):
"""
root_universe = self._geometry._root_universe
# Clear OpenMC written IDs used to optimize XML generation
openmc.universe.WRITTEN_IDS = {}
# Reset xml element tree
self._geometry_file.clear()
root_universe = self.geometry.root_universe
root_universe.create_xml_subelement(self._geometry_file)
# Clean the indentation in the file to be user-readable

View file

@ -1,4 +1,4 @@
from collections import Iterable
from collections import Iterable, OrderedDict
from copy import deepcopy
from numbers import Real, Integral
import warnings
@ -33,8 +33,8 @@ NO_DENSITY = 99999.
class Material(object):
"""A material composed of a collection of nuclides/elements that can be assigned
to a region of space.
"""A material composed of a collection of nuclides/elements that can be
assigned to a region of space.
Parameters
----------
@ -64,15 +64,15 @@ class Material(object):
self._density = None
self._density_units = ''
# A dictionary of Nuclides
# An ordered dictionary of Nuclides (order affects OpenMC results)
# Keys - Nuclide names
# Values - tuple (nuclide, percent, percent type)
self._nuclides = {}
self._nuclides = OrderedDict()
# A dictionary of Elements
# An ordered dictionary of Elements (order affects OpenMC results)
# Keys - Element names
# Values - tuple (element, percent, percent type)
self._elements = {}
self._elements = OrderedDict()
# If specified, a list of tuples of (table name, xs identifier)
self._sab = []
@ -83,6 +83,89 @@ class Material(object):
# If specified, this file will be used instead of composition values
self._distrib_otf_file = None
def __eq__(self, other):
if not isinstance(other, Material):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
# FIXME: We cannot compare densities since OpenMC outputs densities
# in atom/b-cm in summary.h5 irregardless of input units, and we
# cannot compute the sum percent in Python since we lack AWR
#elif self.density != other.density:
# return False
#elif self._nuclides != other._nuclides:
# return False
#elif self._elements != other._elements:
# return False
elif self._sab != other._sab:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'Material\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}'.format('\tDensity', '=\t', self._density)
string += ' [{0}]\n'.format(self._density_units)
string += '{0: <16}\n'.format('\tS(a,b) Tables')
for sab in self._sab:
string += '{0: <16}{1}[{2}{3}]\n'.format('\tS(a,b)', '=\t',
sab[0], sab[1])
string += '{0: <16}\n'.format('\tNuclides')
for nuclide in self._nuclides:
percent = self._nuclides[nuclide][1]
percent_type = self._nuclides[nuclide][2]
string += '{0: <16}'.format('\t{0}'.format(nuclide))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
string += '{0: <16}\n'.format('\tElements')
for element in self._elements:
percent = self._nuclides[element][1]
percent_type = self._nuclides[element][2]
string += '{0: >16}'.format('\t{0}'.format(element))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
return string
def __deepcopy__(self, memo):
existing = memo.get(id(self))
if existing is None:
# If this is the first time we have tried to copy this object, create a copy
clone = type(self).__new__(type(self))
clone._id = self._id
clone._name = self._name
clone._density = self._density
clone._density_units = self._density_units
clone._nuclides = deepcopy(self._nuclides, memo)
clone._elements = deepcopy(self._elements, memo)
clone._sab = deepcopy(self._sab, memo)
clone._convert_to_distrib_comps = self._convert_to_distrib_comps
clone._distrib_otf_file = self._distrib_otf_file
memo[id(self)] = clone
return clone
else:
# If this object has been copied before, return the first copy made
return existing
@property
def id(self):
return self._id
@ -125,16 +208,19 @@ class Material(object):
msg = 'Unable to set Material ID to "{0}" since a Material with ' \
'this ID was already initialized'.format(material_id)
raise ValueError(msg)
check_greater_than('material ID', material_id, 0)
check_greater_than('material ID', material_id, 0, equality=True)
self._id = material_id
MATERIAL_IDS.append(material_id)
@name.setter
def name(self, name):
check_type('name for Material ID="{0}"'.format(self._id),
name, basestring)
self._name = name
if name is not None:
check_type('name for Material ID="{0}"'.format(self._id),
name, basestring)
self._name = name
else:
self._name = ''
def set_density(self, units, density=NO_DENSITY):
"""Set the density of the material
@ -312,6 +398,12 @@ class Material(object):
self._sab.append((name, xs))
def make_isotropic_in_lab(self):
for nuclide_name in self._nuclides:
self._nuclides[nuclide_name][0].scattering = 'iso-in-lab'
for element_name in self._elements:
self._element[element_name][0].scattering = 'iso-in-lab'
def get_all_nuclides(self):
"""Returns all nuclides in the material
@ -323,7 +415,7 @@ class Material(object):
"""
nuclides = {}
nuclides = OrderedDict()
for nuclide_name, nuclide_tuple in self._nuclides.items():
nuclide = nuclide_tuple[0]
@ -332,38 +424,6 @@ class Material(object):
return nuclides
def __repr__(self):
string = 'Material\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}'.format('\tDensity', '=\t', self._density)
string += ' [{0}]\n'.format(self._density_units)
string += '{0: <16}\n'.format('\tS(a,b) Tables')
for sab in self._sab:
string += '{0: <16}{1}[{2}{3}]\n'.format('\tS(a,b)', '=\t',
sab[0], sab[1])
string += '{0: <16}\n'.format('\tNuclides')
for nuclide in self._nuclides:
percent = self._nuclides[nuclide][1]
percent_type = self._nuclides[nuclide][2]
string += '{0: <16}'.format('\t{0}'.format(nuclide))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
string += '{0: <16}\n'.format('\tElements')
for element in self._elements:
percent = self._nuclides[element][1]
percent_type = self._nuclides[element][2]
string += '{0: >16}'.format('\t{0}'.format(element))
string += '=\t{0: <12} [{1}]\n'.format(percent, percent_type)
return string
def _get_nuclide_xml(self, nuclide, distrib=False):
xml_element = ET.Element("nuclide")
xml_element.set("name", nuclide[0]._name)
@ -374,8 +434,11 @@ class Material(object):
else:
xml_element.set("wo", str(nuclide[1]))
if nuclide[0]._xs is not None:
xml_element.set("xs", nuclide[0]._xs)
if nuclide[0].xs is not None:
xml_element.set("xs", nuclide[0].xs)
if not nuclide[0].scattering is None:
xml_element.set("scattering", nuclide[0].scattering)
return xml_element
@ -389,6 +452,9 @@ class Material(object):
else:
xml_element.set("wo", str(element[1]))
if not element[0].scattering is None:
xml_element.set("scattering", element[0].scattering)
return xml_element
def _get_nuclides_xml(self, nuclides, distrib=False):
@ -563,6 +629,10 @@ class MaterialsFile(object):
self._materials.remove(material)
def make_isotropic_in_lab(self):
for material in self._materials:
material.make_isotropic_in_lab()
def _create_material_subelements(self):
subelement = ET.SubElement(self._materials_file, "default_xs")
@ -578,6 +648,9 @@ class MaterialsFile(object):
"""
# Reset xml element tree
self._materials_file.clear()
self._create_material_subelements()
# Clean the indentation in the file to be user-readable

View file

@ -4,8 +4,10 @@ from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
from openmc.checkvalue import (check_type, check_length, check_value,
check_greater_than)
import numpy as np
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
@ -54,7 +56,7 @@ class Mesh(object):
# Initialize Mesh class attributes
self.id = mesh_id
self.name = name
self._type = 'rectangular'
self._type = 'regular'
self._dimension = None
self._lower_left = None
self._upper_right = None
@ -142,47 +144,54 @@ class Mesh(object):
self._id = AUTO_MESH_ID
AUTO_MESH_ID += 1
else:
check_type('mesh ID', mesh_id, Integral)
check_greater_than('mesh ID', mesh_id, 0)
cv.check_type('mesh ID', mesh_id, Integral)
cv.check_greater_than('mesh ID', mesh_id, 0, equality=True)
self._id = mesh_id
@name.setter
def name(self, name):
check_type('name for mesh ID="{0}"'.format(self._id), name, basestring)
self._name = name
if name is not None:
cv.check_type('name for mesh ID="{0}"'.format(self._id),
name, basestring)
self._name = name
else:
self._name = ''
@type.setter
def type(self, meshtype):
check_type('type for mesh ID="{0}"'.format(self._id),
cv.check_type('type for mesh ID="{0}"'.format(self._id),
meshtype, basestring)
check_value('type for mesh ID="{0}"'.format(self._id),
meshtype, ['rectangular', 'hexagonal'])
cv.check_value('type for mesh ID="{0}"'.format(self._id),
meshtype, ['regular'])
self._type = meshtype
@dimension.setter
def dimension(self, dimension):
check_type('mesh dimension', dimension, Iterable, Integral)
check_length('mesh dimension', dimension, 2, 3)
cv.check_type('mesh dimension', dimension, Iterable, Integral)
cv.check_length('mesh dimension', dimension, 2, 3)
self._dimension = dimension
@lower_left.setter
def lower_left(self, lower_left):
check_type('mesh lower_left', lower_left, Iterable, Real)
check_length('mesh lower_left', lower_left, 2, 3)
cv.check_type('mesh lower_left', lower_left, Iterable, Real)
cv.check_length('mesh lower_left', lower_left, 2, 3)
self._lower_left = lower_left
@upper_right.setter
def upper_right(self, upper_right):
check_type('mesh upper_right', upper_right, Iterable, Real)
check_length('mesh upper_right', upper_right, 2, 3)
cv.check_type('mesh upper_right', upper_right, Iterable, Real)
cv.check_length('mesh upper_right', upper_right, 2, 3)
self._upper_right = upper_right
@width.setter
def width(self, width):
check_type('mesh width', width, Iterable, Real)
check_length('mesh width', width, 2, 3)
cv.check_type('mesh width', width, Iterable, Real)
cv.check_length('mesh width', width, 2, 3)
self._width = width
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'Mesh\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)

3
openmc/mgxs/__init__.py Normal file
View file

@ -0,0 +1,3 @@
from openmc.mgxs.groups import EnergyGroups
from openmc.mgxs.library import Library
from openmc.mgxs.mgxs import *

238
openmc/mgxs/groups.py Normal file
View file

@ -0,0 +1,238 @@
from collections import Iterable
from numbers import Real
import copy
import sys
import numpy as np
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
class EnergyGroups(object):
"""An energy groups structure used for multi-group cross-sections.
Parameters
----------
group_edges : Iterable of Real
The energy group boundaries [MeV]
Attributes
----------
group_edges : Iterable of Real
The energy group boundaries [MeV]
num_group : Integral
The number of energy groups
"""
def __init__(self, group_edges=None):
self._group_edges = None
if group_edges is not None:
self.group_edges = group_edges
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy object, create copy
if existing is None:
clone = type(self).__new__(type(self))
clone._group_edges = copy.deepcopy(self.group_edges, memo)
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
def __eq__(self, other):
if not isinstance(other, EnergyGroups):
return False
elif self.group_edges != other.group_edges:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(tuple(self.group_edges))
@property
def group_edges(self):
return self._group_edges
@property
def num_groups(self):
return len(self.group_edges) - 1
@group_edges.setter
def group_edges(self, edges):
cv.check_type('group edges', edges, Iterable, Real)
cv.check_greater_than('number of group edges', len(edges), 1)
self._group_edges = np.array(edges)
def get_group(self, energy):
"""Returns the energy group in which the given energy resides.
Parameters
----------
energy : Real
The energy of interest in MeV
Returns
-------
Integral
The energy group index, starting at 1 for the highest energies
Raises
------
ValueError
If the group edges have not yet been set.
"""
if self.group_edges is None:
msg = 'Unable to get energy group for energy "{0}" MeV since ' \
'the group edges have not yet been set'.format(energy)
raise ValueError(msg)
index = np.where(self.group_edges > energy)[0][0]
group = self.num_groups - index + 1
return group
def get_group_bounds(self, group):
"""Returns the energy boundaries for the energy group of interest.
Parameters
----------
group : Integral
The energy group index, starting at 1 for the highest energies
Returns
-------
2-tuple
The low and high energy bounds for the group in MeV
Raises
------
ValueError
If the group edges have not yet been set.
"""
if self.group_edges is None:
msg = 'Unable to get energy group bounds for group "{0}" since ' \
'the group edges have not yet been set'.format(group)
raise ValueError(msg)
cv.check_greater_than('group', group, 0)
cv.check_less_than('group', group, self.num_groups, equality=True)
lower = self.group_edges[self.num_groups-group]
upper = self.group_edges[self.num_groups-group+1]
return lower, upper
def get_group_indices(self, groups='all'):
"""Returns the array indices for one or more energy groups.
Parameters
----------
groups : str, tuple
The energy groups of interest - a tuple of the energy group indices,
starting at 1 for the highest energies (default is 'all')
Returns
-------
ndarray
The ndarray array indices for each energy group of interest
Raises
------
ValueError
If the group edges have not yet been set, or if a group is requested
that is outside the bounds of the number of energy groups.
"""
if self.group_edges is None:
msg = 'Unable to get energy group indices for groups "{0}" since ' \
'the group edges have not yet been set'.format(groups)
raise ValueError(msg)
if groups == 'all':
return np.arange(self.num_groups)
else:
indices = np.zeros(len(groups), dtype=np.int)
for i, group in enumerate(groups):
cv.check_greater_than('group', group, 0)
cv.check_less_than('group', group, self.num_groups, equality=True)
indices[i] = group - 1
return indices
def get_condensed_groups(self, coarse_groups):
"""Return a coarsened version of this EnergyGroups object.
This method merges together energy groups in this object into wider
energy groups as defined by the list of groups specified by the user,
and returns a new, coarse EnergyGroups object.
Parameters
----------
coarse_groups : Iterable of 2-tuple
The energy groups of interest - a list of 2-tuples, each directly
corresponding to one of the new coarse groups. The values in the
2-tuples are upper/lower energy groups used to construct a new
coarse group. For example, if [(1,2), (3,4)] was used as the coarse
groups, fine groups 1 and 2 would be merged into coarse group 1
while fine groups 3 and 4 would be merged into coarse group 2.
Returns
-------
EnergyGroups
A coarsened version of this EnergyGroups object.
Raises
------
ValueError
If the group edges have not yet been set.
"""
cv.check_type('group edges', coarse_groups, Iterable)
for group in coarse_groups:
cv.check_type('group edges', group, Iterable)
cv.check_length('group edges', group, 2)
cv.check_greater_than('lower group', group[0], 1, True)
cv.check_less_than('lower group', group[0], self.num_groups, True)
cv.check_greater_than('upper group', group[0], 1, True)
cv.check_less_than('upper group', group[0], self.num_groups, True)
cv.check_less_than('lower group', group[0], group[1], False)
# Compute the group indices into the coarse group
group_bounds = [group[1] for group in coarse_groups]
group_bounds.insert(0, coarse_groups[0][0])
# Determine the indices mapping the fine-to-coarse energy groups
group_bounds = np.asarray(group_bounds)
group_indices = np.flipud(self.num_groups - group_bounds)
group_indices[-1] += 1
# Determine the edges between coarse energy groups and sort
# in increasing order in case the user passed in unordered groups
group_edges = self.group_edges[group_indices]
group_edges = np.sort(group_edges)
# Create a new condensed EnergyGroups object
condensed_groups = EnergyGroups()
condensed_groups.group_edges = group_edges
return condensed_groups

681
openmc/mgxs/library.py Normal file
View file

@ -0,0 +1,681 @@
import sys
import os
import copy
import pickle
from numbers import Integral
from collections import OrderedDict
import openmc
import openmc.mgxs
import openmc.checkvalue as cv
if sys.version_info[0] >= 3:
basestring = str
class Library(object):
"""A multi-group cross section library for some energy group structure.
This class can be used for both OpenMC input generation and tally data
post-processing to compute spatially-homogenized and energy-integrated
multi-group cross sections for deterministic neutronics calculations.
This class helps automate the generation of MGXS objects for some energy
group structure and domain type. The Library serves as a collection for
MGXS objects with routines to automate the initialization of tallies for
input files, the loading of tally data from statepoint files, data storage,
energy group condensation and more.
Parameters
----------
openmc_geometry : openmc.Geometry
An geometry which has been initialized with a root universe
by_nuclide : bool
If true, computes cross sections for each nuclide in each domain
mgxs_types : Iterable of str
The types of cross sections in the library (e.g., ['total', 'scatter'])
name : str, optional
Name of the multi-group cross section. library Used as a label to
identify tallies in OpenMC 'tallies.xml' file.
Attributes
----------
openmc_geometry : openmc.Geometry
An geometry which has been initialized with a root universe
opencg_geometry : opencg.Geometry
An OpenCG geometry object equivalent to the OpenMC geometry
encapsulated by the summary file. Use of this attribute requires
installation of the OpenCG Python module.
by_nuclide : bool
If true, computes cross sections for each nuclide in each domain
mgxs_types : Iterable of str
The types of cross sections in the library (e.g., ['total', 'scatter'])
domain_type : {'material', 'cell', 'distribcell', 'universe'}
Domain type for spatial homogenization
domains : Iterable of Material, Cell or Universe
The spatial domain(s) for which MGXS in the Library are computed
correction : 'P0' or None
Apply the P0 correction to scattering matrices if set to 'P0'
energy_groups : EnergyGroups
Energy group structure for energy condensation
tally_trigger : Trigger
An (optional) tally precision trigger given to each tally used to
compute the cross section
all_mgxs : OrderedDict
MGXS objects keyed by domain ID and cross section type
sp_filename : str
The filename of the statepoint with tally data used to the
compute cross sections
keff : Real or None
The combined keff from the statepoint file with tally data used to
compute cross sections (for eigenvalue calculations only)
name : str, optional
Name of the multi-group cross section library. Used as a label to
identify tallies in OpenMC 'tallies.xml' file.
"""
def __init__(self, openmc_geometry, by_nuclide=False,
mgxs_types=None, name=''):
self._name = ''
self._openmc_geometry = None
self._opencg_geometry = None
self._by_nuclide = None
self._mgxs_types = []
self._domain_type = None
self._domains = 'all'
self._correction = 'P0'
self._energy_groups = None
self._tally_trigger = None
self._all_mgxs = OrderedDict()
self._sp_filename = None
self._keff = None
self.name = name
self.openmc_geometry = openmc_geometry
self.by_nuclide = by_nuclide
if mgxs_types is not None:
self.mgxs_types = mgxs_types
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, copy it
if existing is None:
clone = type(self).__new__(type(self))
clone._name = self.name
clone._openmc_geometry = self.openmc_geometry
clone._opencg_geometry = None
clone._by_nuclide = self.by_nuclide
clone._mgxs_types = self.mgxs_types
clone._domain_type = self.domain_type
clone._domains = self.domains
clone._correction = self.correction
clone._energy_groups = copy.deepcopy(self.energy_groups, memo)
clone._tally_trigger = copy.deepcopy(self.tally_trigger, memo)
clone._all_mgxs = self.all_mgxs
clone._sp_filename = self._sp_filename
clone._keff = self._keff
clone._all_mgxs = OrderedDict()
for domain in self.domains:
clone.all_mgxs[domain.id] = OrderedDict()
for mgxs_type in self.mgxs_types:
mgxs = copy.deepcopy(self.all_mgxs[domain.id][mgxs_type])
clone.all_mgxs[domain.id][mgxs_type] = mgxs
memo[id(self)] = clone
return clone
# If this object has been copied before, return the first copy made
else:
return existing
@property
def openmc_geometry(self):
return self._openmc_geometry
@property
def openmc_geometry(self):
return self._openmc_geometry
@property
def opencg_geometry(self):
if self._opencg_geometry is None:
from openmc.opencg_compatible import get_opencg_geometry
self._opencg_geometry = get_opencg_geometry(self._openmc_geometry)
return self._opencg_geometry
@property
def name(self):
return self._name
@property
def mgxs_types(self):
return self._mgxs_types
@property
def by_nuclide(self):
return self._by_nuclide
@property
def domain_type(self):
return self._domain_type
@property
def domains(self):
if self._domains == 'all':
if self.domain_type == 'material':
return self.openmc_geometry.get_all_materials()
elif self.domain_type in ['cell', 'distribcell']:
return self.openmc_geometry.get_all_material_cells()
elif self.domain_type == 'universe':
return self.openmc_geometry.get_all_universes()
else:
raise ValueError('Unable to get domains without a domain type')
else:
return self._domains
@property
def correction(self):
return self._correction
@property
def energy_groups(self):
return self._energy_groups
@property
def tally_trigger(self):
return self._tally_trigger
@property
def num_groups(self):
return self.energy_groups.num_groups
@property
def all_mgxs(self):
return self._all_mgxs
@property
def sp_filename(self):
return self._sp_filename
@property
def keff(self):
return self._keff
@openmc_geometry.setter
def openmc_geometry(self, openmc_geometry):
cv.check_type('openmc_geometry', openmc_geometry, openmc.Geometry)
self._openmc_geometry = openmc_geometry
self._opencg_geometry = None
@name.setter
def name(self, name):
cv.check_type('name', name, basestring)
self._name = name
@mgxs_types.setter
def mgxs_types(self, mgxs_types):
if mgxs_types == 'all':
self._mgxs_types = openmc.mgxs.MGXS_TYPES
else:
cv.check_iterable_type('mgxs_types', mgxs_types, basestring)
for mgxs_type in mgxs_types:
cv.check_value('mgxs_type', mgxs_type, openmc.mgxs.MGXS_TYPES)
self._mgxs_types = mgxs_types
@by_nuclide.setter
def by_nuclide(self, by_nuclide):
cv.check_type('by_nuclide', by_nuclide, bool)
self._by_nuclide = by_nuclide
@domain_type.setter
def domain_type(self, domain_type):
cv.check_value('domain type', domain_type, tuple(openmc.mgxs.DOMAIN_TYPES))
self._domain_type = domain_type
@domains.setter
def domains(self, domains):
# Use all materials, cells or universes in the geometry as domains
if domains == 'all':
self._domains = domains
# User specified a list of material, cell or universe domains
else:
if self.domain_type == 'material':
cv.check_iterable_type('domain', domains, openmc.Material)
all_domains = self.openmc_geometry.get_all_materials()
elif self.domain_type in ['cell', 'distribcell']:
cv.check_iterable_type('domain', domains, openmc.Cell)
all_domains = self.openmc_geometry.get_all_material_cells()
elif self.domain_type == 'universe':
cv.check_iterable_type('domain', domains, openmc.Universe)
all_domains = self.openmc_geometry.get_all_universes()
else:
msg = 'Unable to set domains with ' \
'domain type "{}"'.format(self.domain_type)
raise ValueError(msg)
# Check that each domain can be found in the geometry
for domain in domains:
if domain not in all_domains:
msg = 'Domain "{}" could not be found in the ' \
'geometry.'.format(domain)
raise ValueError(msg)
self._domains = domains
@correction.setter
def correction(self, correction):
cv.check_value('correction', correction, ('P0', None))
self._correction = correction
@energy_groups.setter
def energy_groups(self, energy_groups):
cv.check_type('energy groups', energy_groups, openmc.mgxs.EnergyGroups)
self._energy_groups = energy_groups
@tally_trigger.setter
def tally_trigger(self, tally_trigger):
cv.check_type('tally trigger', tally_trigger, openmc.Trigger)
self._tally_trigger = tally_trigger
def build_library(self):
"""Initialize MGXS objects in each domain and for each reaction type
in the library.
This routine will populate the all_mgxs instance attribute dictionary
with MGXS subclass objects keyed by each domain ID (e.g., Material IDs)
and cross section type (e.g., 'nu-fission', 'total', etc.).
"""
# Initialize MGXS for each domain and mgxs type and store in dictionary
for domain in self.domains:
self.all_mgxs[domain.id] = OrderedDict()
for mgxs_type in self.mgxs_types:
mgxs = openmc.mgxs.MGXS.get_mgxs(mgxs_type, name=self.name)
mgxs.domain = domain
mgxs.domain_type = self.domain_type
mgxs.energy_groups = self.energy_groups
mgxs.by_nuclide = self.by_nuclide
# If a tally trigger was specified, add it to the MGXS
if self.tally_trigger:
mgxs.tally_trigger = self.tally_trigger
# Specify whether to use a transport ('P0') correction
if isinstance(mgxs, openmc.mgxs.ScatterMatrixXS):
mgxs.correction = self.correction
self.all_mgxs[domain.id][mgxs_type] = mgxs
def add_to_tallies_file(self, tallies_file, merge=True):
"""Add all tallies from all MGXS objects to a tallies file.
NOTE: This assumes that build_library() has been called
Parameters
----------
tallies_file : openmc.TalliesFile
A TalliesFile object to add each MGXS' tallies to generate a
"tallies.xml" input file for OpenMC
merge : bool
Indicate whether tallies should be merged when possible. Defaults
to True.
"""
cv.check_type('tallies_file', tallies_file, openmc.TalliesFile)
# Add tallies from each MGXS for each domain and mgxs type
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = self.get_mgxs(domain, mgxs_type)
for tally_id, tally in mgxs.tallies.items():
tallies_file.add_tally(tally, merge=merge)
def load_from_statepoint(self, statepoint):
"""Extracts tallies in an OpenMC StatePoint with the data needed to
compute multi-group cross sections.
This method is needed to compute cross section data from tallies
in an OpenMC StatePoint object.
NOTE: The statepoint must first be linked with an OpenMC Summary object.
Parameters
----------
statepoint : openmc.StatePoint
An OpenMC StatePoint object with tally data
Raises
------
ValueError
When this method is called with a statepoint that has not been
linked with a summary object.
"""
cv.check_type('statepoint', statepoint, openmc.StatePoint)
if not statepoint.with_summary:
msg = 'Unable to load data from a statepoint which has not been ' \
'linked with a summary file'
raise ValueError(msg)
self._sp_filename = statepoint._f.filename
self._openmc_geometry = statepoint.summary.openmc_geometry
if statepoint.run_mode == 'k-eigenvalue':
self._keff = statepoint.k_combined[0]
# Load tallies for each MGXS for each domain and mgxs type
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = self.get_mgxs(domain, mgxs_type)
mgxs.load_from_statepoint(statepoint)
def get_mgxs(self, domain, mgxs_type):
"""Return the MGXS object for some domain and reaction rate type.
This routine searches the library for an MGXS object for the spatial
domain and reaction rate type requested by the user.
NOTE: This routine must be called after the build_library() routine.
Parameters
----------
domain : Material or Cell or Universe or Integral
The material, cell, or universe object of interest (or its ID)
mgxs_type : {'total', 'transport', 'absorption', 'capture', 'fission', 'nu-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'chi'}
The type of multi-group cross section object to return
Returns
-------
openmc.mgxs.MGXS
The MGXS object for the requested domain and reaction rate type
Raises
------
ValueError
If no MGXS object can be found for the requested domain or
multi-group cross section type
"""
if self.domain_type == 'material':
cv.check_type('domain', domain, (openmc.Material, Integral))
elif self.domain_type == 'cell' or self.domain_type == 'distribcell':
cv.check_type('domain', domain, (openmc.Cell, Integral))
elif self.domain_type == 'universe':
cv.check_type('domain', domain, (openmc.Universe, Integral))
# Check that requested domain is included in library
if cv._isinstance(domain, Integral):
domain_id = domain
for domain in self.domains:
if domain_id == domain.id:
break
else:
msg = 'Unable to find MGXS for {0} "{1}" in ' \
'library'.format(self.domain_type, domain)
raise ValueError(msg)
else:
domain_id = domain.id
# Check that requested domain is included in library
if mgxs_type not in self.mgxs_types:
msg = 'Unable to find MGXS type "{0}"'.format(mgxs_type)
raise ValueError(msg)
return self.all_mgxs[domain_id][mgxs_type]
def get_condensed_library(self, coarse_groups):
"""Construct an energy-condensed version of this library.
This routine condenses each of the multi-group cross sections in the
library to a coarse energy group structure. NOTE: This routine must
be called after the load_from_statepoint(...) routine loads the tallies
from the statepoint into each of the cross sections.
Parameters
----------
coarse_groups : openmc.mgxs.EnergyGroups
The coarse energy group structure of interest
Returns
-------
Library
A new multi-group cross section library condensed to the group
structure of interest
Raises
------
ValueError
When this method is called before a statepoint has been loaded
See also
--------
MGXS.get_condensed_xs(coarse_groups)
"""
if self.sp_filename is None:
msg = 'Unable to get a condensed coarse group cross section ' \
'library since the statepoint has not yet been loaded'
raise ValueError(msg)
cv.check_type('coarse_groups', coarse_groups, openmc.mgxs.EnergyGroups)
cv.check_less_than('coarse groups', coarse_groups.num_groups,
self.num_groups, equality=True)
cv.check_value('upper coarse energy', coarse_groups.group_edges[-1],
[self.energy_groups.group_edges[-1]])
cv.check_value('lower coarse energy', coarse_groups.group_edges[0],
[self.energy_groups.group_edges[0]])
# Clone this Library to initialize the condensed version
condensed_library = copy.deepcopy(self)
condensed_library.energy_groups = coarse_groups
# Condense the MGXS for each domain and mgxs type
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = condensed_library.get_mgxs(domain, mgxs_type)
condensed_mgxs = mgxs.get_condensed_xs(coarse_groups)
condensed_library.all_mgxs[domain.id][mgxs_type] = condensed_mgxs
return condensed_library
def get_subdomain_avg_library(self):
"""Construct a subdomain-averaged version of this library.
This routine averages each multi-group cross section across distribcell
instances. The method performs spatial homogenization to compute the
scalar flux-weighted average cross section across the subdomains.
NOTE: This method is only relevant for distribcell domain types and
simplys returns a deep copy of the library for all other domains types.
Returns
-------
Library
A new multi-group cross section library averaged across subdomains
Raises
------
ValueError
When this method is called before a statepoint has been loaded
See also
--------
MGXS.get_subdomain_avg_xs(subdomains)
"""
if self.sp_filename is None:
msg = 'Unable to get a subdomain-averaged cross section ' \
'library since the statepoint has not yet been loaded'
raise ValueError(msg)
# Clone this Library to initialize the subdomain-averaged version
subdomain_avg_library = copy.deepcopy(self)
if subdomain_avg_library.domain_type == 'distribcell':
subdomain_avg_library.domain_type = 'cell'
else:
return subdomain_avg_library
# Subdomain average the MGXS for each domain and mgxs type
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = subdomain_avg_library.get_mgxs(domain, mgxs_type)
avg_mgxs = mgxs.get_subdomain_avg_xs()
subdomain_avg_library.all_mgxs[domain.id][mgxs_type] = avg_mgxs
return subdomain_avg_library
def build_hdf5_store(self, filename='mgxs.h5', directory='mgxs',
subdomains='all', nuclides='all', xs_type='macro'):
"""Export the multi-group cross section library to an HDF5 binary file.
This method constructs an HDF5 file which stores the library's
multi-group cross section data. The data is stored in a hierarchy of
HDF5 groups from the domain type, domain id, subdomain id (for
distribcell domains), nuclides and cross section types. Two datasets for
the mean and standard deviation are stored for each subdomain entry in
the HDF5 file. The number of groups is stored as a file attribute.
NOTE: This requires the h5py Python package.
Parameters
----------
filename : str
Filename for the HDF5 file. Defaults to 'mgxs.h5'.
directory : str
Directory for the HDF5 file. Defaults to 'mgxs'.
subdomains : {'all', 'avg'}
Report all subdomains or the average of all subdomain cross sections
in the report. Defaults to 'all'.
nuclides : {'all', 'sum'}
The nuclides of the cross-sections to include in the report. This
may be a list of nuclide name strings (e.g., ['U-235', 'U-238']).
The special string 'all' will report the cross sections for all
nuclides in the spatial domain. The special string 'sum' will report
the cross sections summed over all nuclides. Defaults to 'all'.
xs_type: {'macro', 'micro'}
Store the macro or micro cross section in units of cm^-1 or barns.
Defaults to 'macro'.
Raises
------
ValueError
When this method is called before a statepoint has been loaded
See also
--------
MGXS.build_hdf5_store(filename, directory, xs_type)
"""
if self.sp_filename is None:
msg = 'Unable to export multi-group cross section library ' \
'since a statepoint has not yet been loaded'
raise ValueError(msg)
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
import h5py
# Make directory if it does not exist
if not os.path.exists(directory):
os.makedirs(directory)
# Add an attribute for the number of energy groups to the HDF5 file
full_filename = os.path.join(directory, filename)
full_filename = full_filename.replace(' ', '-')
f = h5py.File(full_filename, 'w')
f.attrs["# groups"] = self.num_groups
f.close()
# Export MGXS for each domain and mgxs type to an HDF5 file
for domain in self.domains:
for mgxs_type in self.mgxs_types:
mgxs = self.all_mgxs[domain.id][mgxs_type]
if subdomains == 'avg':
mgxs = mgxs.get_subdomain_avg_xs()
mgxs.build_hdf5_store(filename, directory,
xs_type=xs_type, nuclides=nuclides)
def dump_to_file(self, filename='mgxs', directory='mgxs'):
"""Store this Library object in a pickle binary file.
Parameters
----------
filename : str
Filename for the pickle file. Defaults to 'mgxs'.
directory : str
Directory for the pickle file. Defaults to 'mgxs'.
See also
--------
Library.load_from_file(filename, directory)
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
# Make directory if it does not exist
if not os.path.exists(directory):
os.makedirs(directory)
full_filename = os.path.join(directory, filename + '.pkl')
full_filename = full_filename.replace(' ', '-')
# Load and return pickled Library object
pickle.dump(self, open(full_filename, 'wb'))
@staticmethod
def load_from_file(filename='mgxs', directory='mgxs'):
"""Load a Library object from a pickle binary file.
Parameters
----------
filename : str
Filename for the pickle file. Defaults to 'mgxs'.
directory : str
Directory for the pickle file. Defaults to 'mgxs'.
Returns
-------
Library
A Library object loaded from the pickle binary file
See also
--------
Library.dump_to_file(mgxs_lib, filename, directory)
"""
cv.check_type('filename', filename, basestring)
cv.check_type('directory', directory, basestring)
# Make directory if it does not exist
if not os.path.exists(directory):
os.makedirs(directory)
full_filename = os.path.join(directory, filename + '.pkl')
full_filename = full_filename.replace(' ', '-')
# Load and return pickled Library object
return pickle.load(open(full_filename, 'rb'))

2276
openmc/mgxs/mgxs.py Normal file

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,8 @@ class Nuclide(object):
zaid : int
1000*(atomic number) + mass number. As an example, the zaid of U-235
would be 92235.
scattering : 'data' or 'iso-in-lab' or None
The type of angular scattering distribution to use
"""
@ -34,6 +36,7 @@ class Nuclide(object):
self._name = ''
self._xs = None
self._zaid = None
self._scattering = None
# Set the Material class attributes
self.name = name
@ -41,24 +44,31 @@ class Nuclide(object):
if xs is not None:
self.xs = xs
def __eq__(self, nuclide2):
# Check type
if not isinstance(nuclide2, Nuclide):
return False
# Check name
elif self._name != nuclide2._name:
return False
# Check xs
elif self._xs != nuclide2._xs:
return False
else:
def __eq__(self, other):
if isinstance(other, Nuclide):
if self._name != other._name:
return False
elif self._xs != other._xs:
return False
else:
return True
elif isinstance(other, basestring) and other == self.name:
return True
else:
return False
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash((self._name, self._xs))
return hash(repr(self))
def __repr__(self):
string = 'Nuclide - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
if self._zaid is not None:
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self._zaid)
return string
@property
def name(self):
@ -72,6 +82,10 @@ class Nuclide(object):
def zaid(self):
return self._zaid
@property
def scattering(self):
return self._scattering
@name.setter
def name(self, name):
check_type('name', name, basestring)
@ -87,9 +101,22 @@ class Nuclide(object):
check_type('zaid', zaid, Integral)
self._zaid = zaid
@scattering.setter
def scattering(self, scattering):
if not scattering in ['data', 'iso-in-lab']:
msg = 'Unable to set scattering for Nuclide to {0} ' \
'which is not "data" or "iso-in-lab"'.format(scattering)
raise ValueError(msg)
self._scattering = scattering
def __repr__(self):
string = 'Nuclide - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
if self._zaid is not None:
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self._zaid)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self.xs)
if self.zaid is not None:
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self.zaid)
if self.scattering is not None:
string += '{0: <16}{1}{2}\n'.format('\tscattering', '=\t',
self.scattering)
return string

View file

@ -9,6 +9,8 @@ except ImportError:
raise ImportError(msg)
import openmc
from openmc.region import Intersection
from openmc.surface import Halfspace
# A dictionary of all OpenMC Materials created
@ -83,14 +85,14 @@ def get_opencg_material(openmc_material):
raise ValueError(msg)
global OPENCG_MATERIALS
material_id = openmc_material._id
material_id = openmc_material.id
# If this Material was already created, use it
if material_id in OPENCG_MATERIALS:
return OPENCG_MATERIALS[material_id]
# Create an OpenCG Material to represent this OpenMC Material
name = openmc_material._name
name = openmc_material.name
opencg_material = opencg.Material(material_id=material_id, name=name)
# Add the OpenMC Material to the global collection of all OpenMC Materials
@ -123,14 +125,14 @@ def get_openmc_material(opencg_material):
raise ValueError(msg)
global OPENMC_MATERIALS
material_id = opencg_material._id
material_id = opencg_material.id
# If this Material was already created, use it
if material_id in OPENMC_MATERIALS:
return OPENMC_MATERIALS[material_id]
# Create an OpenMC Material to represent this OpenCG Material
name = opencg_material._name
name = opencg_material.name
openmc_material = openmc.Material(material_id=material_id, name=name)
# Add the OpenMC Material to the global collection of all OpenMC Materials
@ -168,8 +170,8 @@ def is_opencg_surface_compatible(opencg_surface):
'since "{0}" is not a Surface'.format(opencg_surface)
raise ValueError(msg)
if opencg_surface._type in ['x-squareprism',
'y-squareprism', 'z-squareprism']:
if opencg_surface.type in ['x-squareprism',
'y-squareprism', 'z-squareprism']:
return False
else:
return True
@ -196,59 +198,59 @@ def get_opencg_surface(openmc_surface):
raise ValueError(msg)
global OPENCG_SURFACES
surface_id = openmc_surface._id
surface_id = openmc_surface.id
# If this Material was already created, use it
if surface_id in OPENCG_SURFACES:
return OPENCG_SURFACES[surface_id]
# Create an OpenCG Surface to represent this OpenMC Surface
name = openmc_surface._name
name = openmc_surface.name
# Correct for OpenMC's syntax for Surfaces dividing Cells
boundary = openmc_surface._boundary_type
boundary = openmc_surface.boundary_type
if boundary == 'transmission':
boundary = 'interface'
opencg_surface = None
if openmc_surface._type == 'plane':
A = openmc_surface._coeffs['A']
B = openmc_surface._coeffs['B']
C = openmc_surface._coeffs['C']
D = openmc_surface._coeffs['D']
if openmc_surface.type == 'plane':
A = openmc_surface.a
B = openmc_surface.b
C = openmc_surface.c
D = openmc_surface.d
opencg_surface = opencg.Plane(surface_id, name, boundary, A, B, C, D)
elif openmc_surface._type == 'x-plane':
x0 = openmc_surface._coeffs['x0']
elif openmc_surface.type == 'x-plane':
x0 = openmc_surface.x0
opencg_surface = opencg.XPlane(surface_id, name, boundary, x0)
elif openmc_surface._type == 'y-plane':
y0 = openmc_surface._coeffs['y0']
elif openmc_surface.type == 'y-plane':
y0 = openmc_surface.y0
opencg_surface = opencg.YPlane(surface_id, name, boundary, y0)
elif openmc_surface._type == 'z-plane':
z0 = openmc_surface._coeffs['z0']
elif openmc_surface.type == 'z-plane':
z0 = openmc_surface.z0
opencg_surface = opencg.ZPlane(surface_id, name, boundary, z0)
elif openmc_surface._type == 'x-cylinder':
y0 = openmc_surface._coeffs['y0']
z0 = openmc_surface._coeffs['z0']
R = openmc_surface._coeffs['R']
elif openmc_surface.type == 'x-cylinder':
y0 = openmc_surface.y0
z0 = openmc_surface.z0
R = openmc_surface.r
opencg_surface = opencg.XCylinder(surface_id, name,
boundary, y0, z0, R)
elif openmc_surface._type == 'y-cylinder':
x0 = openmc_surface._coeffs['x0']
z0 = openmc_surface._coeffs['z0']
R = openmc_surface._coeffs['R']
elif openmc_surface.type == 'y-cylinder':
x0 = openmc_surface.x0
z0 = openmc_surface.z0
R = openmc_surface.r
opencg_surface = opencg.YCylinder(surface_id, name,
boundary, x0, z0, R)
elif openmc_surface._type == 'z-cylinder':
x0 = openmc_surface._coeffs['x0']
y0 = openmc_surface._coeffs['y0']
R = openmc_surface._coeffs['R']
elif openmc_surface.type == 'z-cylinder':
x0 = openmc_surface.x0
y0 = openmc_surface.y0
R = openmc_surface.r
opencg_surface = opencg.ZCylinder(surface_id, name,
boundary, x0, y0, R)
@ -282,61 +284,61 @@ def get_openmc_surface(opencg_surface):
raise ValueError(msg)
global openmc_surface
surface_id = opencg_surface._id
surface_id = opencg_surface.id
# If this Surface was already created, use it
if surface_id in OPENMC_SURFACES:
return OPENMC_SURFACES[surface_id]
# Create an OpenMC Surface to represent this OpenCG Surface
name = opencg_surface._name
name = opencg_surface.name
# Correct for OpenMC's syntax for Surfaces dividing Cells
boundary = opencg_surface._boundary_type
boundary = opencg_surface.boundary_type
if boundary == 'interface':
boundary = 'transmission'
if opencg_surface._type == 'plane':
A = opencg_surface._coeffs['A']
B = opencg_surface._coeffs['B']
C = opencg_surface._coeffs['C']
D = opencg_surface._coeffs['D']
if opencg_surface.type == 'plane':
A = opencg_surface.a
B = opencg_surface.b
C = opencg_surface.c
D = opencg_surface.d
openmc_surface = openmc.Plane(surface_id, boundary, A, B, C, D, name)
elif opencg_surface._type == 'x-plane':
x0 = opencg_surface._coeffs['x0']
elif opencg_surface.type == 'x-plane':
x0 = opencg_surface.x0
openmc_surface = openmc.XPlane(surface_id, boundary, x0, name)
elif opencg_surface._type == 'y-plane':
y0 = opencg_surface._coeffs['y0']
elif opencg_surface.type == 'y-plane':
y0 = opencg_surface.y0
openmc_surface = openmc.YPlane(surface_id, boundary, y0, name)
elif opencg_surface._type == 'z-plane':
z0 = opencg_surface._coeffs['z0']
elif opencg_surface.type == 'z-plane':
z0 = opencg_surface.z0
openmc_surface = openmc.ZPlane(surface_id, boundary, z0, name)
elif opencg_surface._type == 'x-cylinder':
y0 = opencg_surface._coeffs['y0']
z0 = opencg_surface._coeffs['z0']
R = opencg_surface._coeffs['R']
elif opencg_surface.type == 'x-cylinder':
y0 = opencg_surface.y0
z0 = opencg_surface.z0
R = opencg_surface.r
openmc_surface = openmc.XCylinder(surface_id, boundary, y0, z0, R, name)
elif opencg_surface._type == 'y-cylinder':
x0 = opencg_surface._coeffs['x0']
z0 = opencg_surface._coeffs['z0']
R = opencg_surface._coeffs['R']
elif opencg_surface.type == 'y-cylinder':
x0 = opencg_surface.x0
z0 = opencg_surface.z0
R = opencg_surface.r
openmc_surface = openmc.YCylinder(surface_id, boundary, x0, z0, R, name)
elif opencg_surface._type == 'z-cylinder':
x0 = opencg_surface._coeffs['x0']
y0 = opencg_surface._coeffs['y0']
R = opencg_surface._coeffs['R']
elif opencg_surface.type == 'z-cylinder':
x0 = opencg_surface.x0
y0 = opencg_surface.y0
R = opencg_surface.r
openmc_surface = openmc.ZCylinder(surface_id, boundary, x0, y0, R, name)
else:
msg = 'Unable to create an OpenMC Surface from an OpenCG ' \
'Surface of type "{0}" since it is not a compatible ' \
'Surface type in OpenMC'.format(opencg_surface._type)
'Surface type in OpenMC'.format(opencg_surface.type)
raise ValueError(msg)
# Add the OpenMC Surface to the global collection of all OpenMC Surfaces
@ -373,20 +375,20 @@ def get_compatible_opencg_surfaces(opencg_surface):
raise ValueError(msg)
global OPENMC_SURFACES
surface_id = opencg_surface._id
surface_id = opencg_surface.id
# If this Surface was already created, use it
if surface_id in OPENMC_SURFACES:
return OPENMC_SURFACES[surface_id]
# Create an OpenMC Surface to represent this OpenCG Surface
name = opencg_surface._name
boundary = opencg_surface._boundary_type
name = opencg_surface.name
boundary = opencg_surface.boundary_type
if opencg_surface._type == 'x-squareprism':
y0 = opencg_surface._coeffs['y0']
z0 = opencg_surface._coeffs['z0']
R = opencg_surface._coeffs['R']
if opencg_surface.type == 'x-squareprism':
y0 = opencg_surface.y0
z0 = opencg_surface.z0
R = opencg_surface.r
# Create a list of the four planes we need
left = opencg.YPlane(name=name, boundary=boundary, y0=y0-R)
@ -395,10 +397,10 @@ def get_compatible_opencg_surfaces(opencg_surface):
top = opencg.ZPlane(name=name, boundary=boundary, z0=z0+R)
surfaces = [left, right, bottom, top]
elif opencg_surface._type == 'y-squareprism':
x0 = opencg_surface._coeffs['x0']
z0 = opencg_surface._coeffs['z0']
R = opencg_surface._coeffs['R']
elif opencg_surface.type == 'y-squareprism':
x0 = opencg_surface.x0
z0 = opencg_surface.z0
R = opencg_surface.r
# Create a list of the four planes we need
left = opencg.XPlane(name=name, boundary=boundary, x0=x0-R)
@ -407,10 +409,10 @@ def get_compatible_opencg_surfaces(opencg_surface):
top = opencg.ZPlane(name=name, boundary=boundary, z0=z0+R)
surfaces = [left, right, bottom, top]
elif opencg_surface._type == 'z-squareprism':
x0 = opencg_surface._coeffs['x0']
y0 = opencg_surface._coeffs['y0']
R = opencg_surface._coeffs['R']
elif opencg_surface.type == 'z-squareprism':
x0 = opencg_surface.x0['x0']
y0 = opencg_surface.y0['y0']
R = opencg_surface.r['R']
# Create a list of the four planes we need
left = opencg.XPlane(name=name, boundary=boundary, x0=x0-R)
@ -422,7 +424,7 @@ def get_compatible_opencg_surfaces(opencg_surface):
else:
msg = 'Unable to create a compatible OpenMC Surface an OpenCG ' \
'Surface of type "{0}" since it already a compatible ' \
'Surface type in OpenMC'.format(opencg_surface._type)
'Surface type in OpenMC'.format(opencg_surface.type)
raise ValueError(msg)
# Add the OpenMC Surface(s) to the global collection of all OpenMC Surfaces
@ -455,37 +457,51 @@ def get_opencg_cell(openmc_cell):
raise ValueError(msg)
global OPENCG_CELLS
cell_id = openmc_cell._id
cell_id = openmc_cell.id
# If this Cell was already created, use it
if cell_id in OPENCG_CELLS:
return OPENCG_CELLS[cell_id]
# Create an OpenCG Cell to represent this OpenMC Cell
name = openmc_cell._name
name = openmc_cell.name
opencg_cell = opencg.Cell(cell_id, name)
fill = openmc_cell._fill
fill = openmc_cell.fill
if (openmc_cell._type == 'normal'):
opencg_cell.setFill(get_opencg_material(fill))
elif (openmc_cell._type == 'fill'):
opencg_cell.setFill(get_opencg_universe(fill))
if (openmc_cell.fill_type == 'material'):
opencg_cell.fill = get_opencg_material(fill)
elif (openmc_cell.fill_type == 'universe'):
opencg_cell.fill = get_opencg_universe(fill)
else:
opencg_cell.setFill(get_opencg_lattice(fill))
opencg_cell.fill = get_opencg_lattice(fill)
if openmc_cell._rotation is not None:
opencg_cell.setRotation(openmc_cell._rotation)
if openmc_cell.rotation is not None:
opencg_cell.rotation = openmc_cell.rotation
if openmc_cell._translation is not None:
opencg_cell.setTranslation(openmc_cell._translation)
if openmc_cell.translation is not None:
opencg_cell.translation = openmc_cell.translation
surfaces = openmc_cell._surfaces
for surface_id in surfaces:
surface = surfaces[surface_id][0]
halfspace = surfaces[surface_id][1]
opencg_cell.addSurface(get_opencg_surface(surface), halfspace)
# Add surfaces to OpenCG cell from OpenMC cell region. Right now this only
# works if the region is a single half-space or an intersection of
# half-spaces, i.e., no complex cells.
region = openmc_cell.region
if region is not None:
if isinstance(region, Halfspace):
surface = region.surface
halfspace = -1 if region.side == '-' else 1
opencg_cell.add_surface(get_opencg_surface(surface), halfspace)
elif isinstance(region, Intersection):
for node in region.nodes:
if not isinstance(node, Halfspace):
raise NotImplementedError("Complex cells not yet "
"supported in OpenCG.")
surface = node.surface
halfspace = -1 if node.side == '-' else 1
opencg_cell.add_surface(get_opencg_surface(surface), halfspace)
else:
raise NotImplementedError("Complex cells not yet supported "
"in OpenCG.")
# Add the OpenMC Cell to the global collection of all OpenMC Cells
OPENMC_CELLS[cell_id] = openmc_cell
@ -536,8 +552,8 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
compatible_cells = []
# SquarePrism Surfaces
if opencg_surface._type in ['x-squareprism', 'y-squareprism',
'z-squareprism']:
if opencg_surface.type in ['x-squareprism', 'y-squareprism',
'z-squareprism']:
# Get the compatible Surfaces (XPlanes and YPlanes)
compatible_surfaces = get_compatible_opencg_surfaces(opencg_surface)
@ -546,10 +562,10 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
# If Cell is inside SquarePrism, add "inside" of Surface halfspaces
if halfspace == -1:
opencg_cell.addSurface(compatible_surfaces[0], +1)
opencg_cell.addSurface(compatible_surfaces[1], -1)
opencg_cell.addSurface(compatible_surfaces[2], +1)
opencg_cell.addSurface(compatible_surfaces[3], -1)
opencg_cell.add_surface(compatible_surfaces[0], +1)
opencg_cell.add_surface(compatible_surfaces[1], -1)
opencg_cell.add_surface(compatible_surfaces[2], +1)
opencg_cell.add_surface(compatible_surfaces[3], -1)
compatible_cells.append(opencg_cell)
# If Cell is outside SquarePrism, add "outside" of Surface halfspaces
@ -631,12 +647,12 @@ def make_opencg_cells_compatible(opencg_universe):
raise ValueError(msg)
# Check all OpenCG Cells in this Universe for compatibility with OpenMC
opencg_cells = opencg_universe._cells
opencg_cells = opencg_universe.cells
for cell_id, opencg_cell in opencg_cells.items():
# Check each of the OpenCG Surfaces for OpenMC compatibility
surfaces = opencg_cell._surfaces
surfaces = opencg_cell.surfaces
for surface_id in surfaces:
surface = surfaces[surface_id][0]
@ -659,7 +675,7 @@ def make_opencg_cells_compatible(opencg_universe):
opencg_universe.removeCell(opencg_cell)
# Add the compatible OpenCG Cells to the Universe
opencg_universe.addCells(cells)
opencg_universe.add_cells(cells)
# Make recursive call to look at the updated state of the
# OpenCG Universe and return
@ -690,34 +706,34 @@ def get_openmc_cell(opencg_cell):
raise ValueError(msg)
global OPENMC_CELLS
cell_id = opencg_cell._id
cell_id = opencg_cell.id
# If this Cell was already created, use it
if cell_id in OPENMC_CELLS:
return OPENMC_CELLS[cell_id]
# Create an OpenCG Cell to represent this OpenMC Cell
name = opencg_cell._name
name = opencg_cell.name
openmc_cell = openmc.Cell(cell_id, name)
fill = opencg_cell._fill
fill = opencg_cell.fill
if (opencg_cell._type == 'universe'):
if (opencg_cell.type == 'universe'):
openmc_cell.fill = get_openmc_universe(fill)
elif (opencg_cell._type == 'lattice'):
elif (opencg_cell.type == 'lattice'):
openmc_cell.fill = get_openmc_lattice(fill)
else:
openmc_cell.fill = get_openmc_material(fill)
if opencg_cell._rotation:
rotation = np.asarray(opencg_cell._rotation, dtype=np.int)
if opencg_cell.rotation:
rotation = np.asarray(opencg_cell.rotation, dtype=np.float64)
openmc_cell.rotation = rotation
if opencg_cell._translation:
translation = np.asarray(opencg_cell._translation, dtype=np.float64)
openmc_cell.setTranslation(translation)
if opencg_cell.translation:
translation = np.asarray(opencg_cell.translation, dtype=np.float64)
openmc_cell.translation = translation
surfaces = opencg_cell._surfaces
surfaces = opencg_cell.surfaces
for surface_id in surfaces:
surface = surfaces[surface_id][0]
@ -754,22 +770,22 @@ def get_opencg_universe(openmc_universe):
raise ValueError(msg)
global OPENCG_UNIVERSES
universe_id = openmc_universe._id
universe_id = openmc_universe.id
# If this Universe was already created, use it
if universe_id in OPENCG_UNIVERSES:
return OPENCG_UNIVERSES[universe_id]
# Create an OpenCG Universe to represent this OpenMC Universe
name = openmc_universe._name
name = openmc_universe.name
opencg_universe = opencg.Universe(universe_id, name)
# Convert all OpenMC Cells in this Universe to OpenCG Cells
openmc_cells = openmc_universe._cells
openmc_cells = openmc_universe.cells
for cell_id, openmc_cell in openmc_cells.items():
opencg_cell = get_opencg_cell(openmc_cell)
opencg_universe.addCell(opencg_cell)
opencg_universe.add_cell(opencg_cell)
# Add the OpenMC Universe to the global collection of all OpenMC Universes
OPENMC_UNIVERSES[universe_id] = openmc_universe
@ -801,7 +817,7 @@ def get_openmc_universe(opencg_universe):
raise ValueError(msg)
global OPENMC_UNIVERSES
universe_id = opencg_universe._id
universe_id = opencg_universe.id
# If this Universe was already created, use it
if universe_id in OPENMC_UNIVERSES:
@ -811,11 +827,11 @@ def get_openmc_universe(opencg_universe):
make_opencg_cells_compatible(opencg_universe)
# Create an OpenMC Universe to represent this OpenCSg Universe
name = opencg_universe._name
name = opencg_universe.name
openmc_universe = openmc.Universe(universe_id, name)
# Convert all OpenCG Cells in this Universe to OpenMC Cells
opencg_cells = opencg_universe._cells
opencg_cells = opencg_universe.cells
for cell_id, opencg_cell in opencg_cells.items():
openmc_cell = get_openmc_cell(opencg_cell)
@ -851,7 +867,7 @@ def get_opencg_lattice(openmc_lattice):
raise ValueError(msg)
global OPENCG_LATTICES
lattice_id = openmc_lattice._id
lattice_id = openmc_lattice.id
# If this Lattice was already created, use it
if lattice_id in OPENCG_LATTICES:
@ -863,9 +879,10 @@ def get_opencg_lattice(openmc_lattice):
pitch = openmc_lattice.pitch
lower_left = openmc_lattice.lower_left
universes = openmc_lattice.universes
outer = openmc_lattice.outer
if len(pitch) == 2:
new_pitch = np.ones(3, dtype=np.float64)
new_pitch = np.ones(3, dtype=np.float64) * np.inf
new_pitch[:2] = pitch
pitch = new_pitch
@ -888,18 +905,20 @@ def get_opencg_lattice(openmc_lattice):
for z in range(dimension[2]):
for y in range(dimension[1]):
for x in range(dimension[0]):
universe_id = universes[x][dimension[1]-y-1][z]._id
universe_id = universes[x][dimension[1]-y-1][z].id
universe_array[z][y][x] = unique_universes[universe_id]
opencg_lattice = opencg.Lattice(lattice_id, name)
opencg_lattice.setDimension(dimension)
opencg_lattice.setWidth(pitch)
opencg_lattice.setUniverses(universe_array)
opencg_lattice.dimension = dimension
opencg_lattice.width = pitch
opencg_lattice.universes = universe_array
if outer is not None:
opencg_lattice.outside = get_opencg_universe(outer)
offset = np.array(lower_left, dtype=np.float64) - \
((np.array(pitch, dtype=np.float64) *
np.array(dimension, dtype=np.float64))) / -2.0
opencg_lattice.setOffset(offset)
opencg_lattice.offset = offset
# Add the OpenMC Lattice to the global collection of all OpenMC Lattices
OPENMC_LATTICES[lattice_id] = openmc_lattice
@ -931,23 +950,24 @@ def get_openmc_lattice(opencg_lattice):
raise ValueError(msg)
global OPENMC_LATTICES
lattice_id = opencg_lattice._id
lattice_id = opencg_lattice.id
# If this Lattice was already created, use it
if lattice_id in OPENMC_LATTICES:
return OPENMC_LATTICES[lattice_id]
dimension = opencg_lattice._dimension
width = opencg_lattice._width
offset = opencg_lattice._offset
universes = opencg_lattice._universes
dimension = opencg_lattice.dimension
width = opencg_lattice.width
offset = opencg_lattice.offset
universes = opencg_lattice.universes
outer = opencg_lattice.outside
# Initialize an empty array for the OpenMC nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)),
dtype=openmc.Universe)
# Create OpenMC Universes for each unique nested Universe in this Lattice
unique_universes = opencg_lattice.getUniqueUniverses()
unique_universes = opencg_lattice.get_unique_universes()
for universe_id, universe in unique_universes.items():
unique_universes[universe_id] = get_openmc_universe(universe)
@ -956,7 +976,7 @@ def get_openmc_lattice(opencg_lattice):
for z in range(dimension[2]):
for y in range(dimension[1]):
for x in range(dimension[0]):
universe_id = universes[z][y][x]._id
universe_id = universes[z][y][x].id
universe_array[x][y][z] = unique_universes[universe_id]
# Reverse y-dimension in array to match ordering in OpenCG
@ -971,6 +991,8 @@ def get_openmc_lattice(opencg_lattice):
openmc_lattice.pitch = width
openmc_lattice.universes = universe_array
openmc_lattice.lower_left = lower_left
if outer is not None:
openmc_lattice.outer = get_openmc_universe(outer)
# Add the OpenMC Lattice to the global collection of all OpenMC Lattices
OPENMC_LATTICES[lattice_id] = openmc_lattice
@ -1011,12 +1033,12 @@ def get_opencg_geometry(openmc_geometry):
OPENMC_LATTICES.clear()
OPENCG_LATTICES.clear()
openmc_root_universe = openmc_geometry._root_universe
openmc_root_universe = openmc_geometry.root_universe
opencg_root_universe = get_opencg_universe(openmc_root_universe)
opencg_geometry = opencg.Geometry()
opencg_geometry.setRootUniverse(opencg_root_universe)
opencg_geometry.initializeCellOffsets()
opencg_geometry.root_universe = opencg_root_universe
opencg_geometry.initialize_cell_offsets()
return opencg_geometry
@ -1043,11 +1065,11 @@ def get_openmc_geometry(opencg_geometry):
# Deep copy the goemetry since it may be modified to make all Surfaces
# compatible with OpenMC's specifications
opencg_geometry.assignAutoIds()
opencg_geometry.assign_auto_ids()
opencg_geometry = copy.deepcopy(opencg_geometry)
# Update Cell bounding boxes in Geometry
opencg_geometry.updateBoundingBoxes()
opencg_geometry.update_bounding_boxes()
# Clear dictionaries and auto-generated ID
OPENMC_SURFACES.clear()
@ -1060,14 +1082,14 @@ def get_openmc_geometry(opencg_geometry):
OPENCG_LATTICES.clear()
# Make the entire geometry "compatible" before assigning auto IDs
universes = opencg_geometry.getAllUniverses()
universes = opencg_geometry.get_all_universes()
for universe_id, universe in universes.items():
if not isinstance(universe, opencg.Lattice):
make_opencg_cells_compatible(universe)
opencg_geometry.assignAutoIds()
opencg_geometry.assign_auto_ids()
opencg_root_universe = opencg_geometry._root_universe
opencg_root_universe = opencg_geometry.root_universe
openmc_root_universe = get_openmc_universe(opencg_root_universe)
openmc_geometry = openmc.Geometry()

View file

@ -12,10 +12,6 @@ class Particle(object):
Attributes
----------
filetype : int
Integer indicating the file type
revision : int
Revision of the particle restart format
current_batch : int
The batch containing the particle
gen_per_batch : int
@ -40,70 +36,55 @@ class Particle(object):
"""
def __init__(self, filename):
if filename.endswith('.h5'):
import h5py
self._f = h5py.File(filename, 'r')
self._hdf5 = True
else:
self._f = open(filename, 'rb')
self._hdf5 = False
import h5py
self._f = h5py.File(filename, 'r')
# Read all metadata
self._read_data()
# Ensure filetype and revision are correct
if 'filetype' not in self._f or self._f[
'filetype'].value.decode() != 'particle restart':
raise IOError('{} is not a particle restart file.'.format(filename))
if self._f['revision'].value != 1:
raise IOError('Particle restart file has a file revision of {} '
'which is not consistent with the revision this '
'version of OpenMC expects ({}).'.format(
self._f['revision'].value, 1))
def _read_data(self):
# Read filetype
self.filetype = self._get_int(path='filetype')[0]
@property
def current_batch(self):
return self._f['current_batch'].value
# Read statepoint revision
self.revision = self._get_int(path='revision')[0]
@property
def current_gen(self):
return self._f['current_gen'].value
# Read current batch
self.current_batch = self._get_int(path='current_batch')[0]
@property
def energy(self):
return self._f['energy'].value
# Read run information
self.gen_per_batch = self._get_int(path='gen_per_batch')[0]
self.current_gen = self._get_int(path='current_gen')[0]
self.n_particles = self._get_long(path='n_particles')[0]
self.run_mode = self._get_int(path='run_mode')[0]
@property
def gen_per_batch(self):
return self._f['gen_per_batch'].value
# Read particle properties
self.id = self._get_long(path='id')[0]
self.weight = self._get_double(path='weight')[0]
self.energy = self._get_double(path='energy')[0]
self.xyz = self._get_double(3, path='xyz')
self.uvw = self._get_double(3, path='uvw')
@property
def id(self):
return self._f['id'].value
def _get_data(self, n, typeCode, size):
return list(struct.unpack('={0}{1}'.format(n, typeCode),
self._f.read(n*size)))
@property
def n_particles(self):
return self._f['n_particles'].value
def _get_int(self, n=1, path=None):
if self._hdf5:
return [int(v) for v in self._f[path].value]
else:
return [int(v) for v in self._get_data(n, 'i', 4)]
@property
def run_mode(self):
return self._f['run_mode'].value.decode()
def _get_long(self, n=1, path=None):
if self._hdf5:
return [int(v) for v in self._f[path].value]
else:
return [int(v) for v in self._get_data(n, 'q', 8)]
@property
def uvw(self):
return self._f['uvw'].value
def _get_float(self, n=1, path=None):
if self._hdf5:
return [float(v) for v in self._f[path].value]
else:
return [float(v) for v in self._get_data(n, 'f', 4)]
@property
def weight(self):
return self._f['weight'].value
def _get_double(self, n=1, path=None):
if self._hdf5:
return [float(v) for v in self._f[path].value]
else:
return [float(v) for v in self._get_data(n, 'd', 8)]
def _get_string(self, n=1, path=None):
if self._hdf5:
return str(self._f[path].value)
else:
return str(self._get_data(n, 's', 1)[0])
@property
def xyz(self):
return self._f['xyz'].value

View file

@ -5,9 +5,9 @@ import sys
import numpy as np
import openmc
import openmc.checkvalue as cv
from openmc.clean_xml import *
from openmc.checkvalue import (check_type, check_value, check_length,
check_greater_than, check_less_than)
if sys.version_info[0] >= 3:
basestring = str
@ -143,70 +143,70 @@ class Plot(object):
self._id = AUTO_PLOT_ID
AUTO_PLOT_ID += 1
else:
check_type('plot ID', plot_id, Integral)
check_greater_than('plot ID', plot_id, 0)
cv.check_type('plot ID', plot_id, Integral)
cv.check_greater_than('plot ID', plot_id, 0, equality=True)
self._id = plot_id
@name.setter
def name(self, name):
check_type('plot name', name, basestring)
cv.check_type('plot name', name, basestring)
self._name = name
@width.setter
def width(self, width):
check_type('plot width', width, Iterable, Real)
check_length('plot width', width, 2, 3)
cv.check_type('plot width', width, Iterable, Real)
cv.check_length('plot width', width, 2, 3)
self._width = width
@origin.setter
def origin(self, origin):
check_type('plot origin', origin, Iterable, Real)
check_length('plot origin', origin, 3)
cv.check_type('plot origin', origin, Iterable, Real)
cv.check_length('plot origin', origin, 3)
self._origin = origin
@pixels.setter
def pixels(self, pixels):
check_type('plot pixels', pixels, Iterable, Integral)
check_length('plot pixels', pixels, 2, 3)
cv.check_type('plot pixels', pixels, Iterable, Integral)
cv.check_length('plot pixels', pixels, 2, 3)
for dim in pixels:
check_greater_than('plot pixels', dim, 0)
cv.check_greater_than('plot pixels', dim, 0)
self._pixels = pixels
@filename.setter
def filename(self, filename):
check_type('filename', filename, basestring)
cv.check_type('filename', filename, basestring)
self._filename = filename
@color.setter
def color(self, color):
check_type('plot color', color, basestring)
check_value('plot color', color, ['cell', 'mat'])
cv.check_type('plot color', color, basestring)
cv.check_value('plot color', color, ['cell', 'mat'])
self._color = color
@type.setter
def type(self, plottype):
check_type('plot type', plottype, basestring)
check_value('plot type', plottype, ['slice', 'voxel'])
cv.check_type('plot type', plottype, basestring)
cv.check_value('plot type', plottype, ['slice', 'voxel'])
self._type = plottype
@basis.setter
def basis(self, basis):
check_type('plot basis', basis, basestring)
check_value('plot basis', basis, ['xy', 'xz', 'yz'])
cv.check_type('plot basis', basis, basestring)
cv.check_value('plot basis', basis, ['xy', 'xz', 'yz'])
self._basis = basis
@background.setter
def background(self, background):
check_type('plot background', background, Iterable, Integral)
check_length('plot background', background, 3)
cv.check_type('plot background', background, Iterable, Integral)
cv.check_length('plot background', background, 3)
for rgb in background:
check_greater_than('plot background',rgb, 0, True)
check_less_than('plot background', rgb, 256)
cv.check_greater_than('plot background',rgb, 0, True)
cv.check_less_than('plot background', rgb, 256)
self._background = background
@col_spec.setter
def col_spec(self, col_spec):
check_type('plot col_spec parameter', col_spec, dict, Integral)
cv.check_type('plot col_spec parameter', col_spec, dict, Integral)
for key in col_spec:
if key < 0:
@ -229,18 +229,18 @@ class Plot(object):
@mask_componenets.setter
def mask_components(self, mask_components):
check_type('plot mask_components', mask_components, Iterable, Integral)
cv.check_type('plot mask_components', mask_components, Iterable, Integral)
for component in mask_components:
check_greater_than('plot mask_components', component, 0, True)
cv.check_greater_than('plot mask_components', component, 0, True)
self._mask_components = mask_components
@mask_background.setter
def mask_background(self, mask_background):
check_type('plot mask background', mask_background, Iterable, Integral)
check_length('plot mask background', mask_background, 3)
cv.check_type('plot mask background', mask_background, Iterable, Integral)
cv.check_length('plot mask background', mask_background, 3)
for rgb in mask_background:
check_greater_than('plot mask background', rgb, 0, True)
check_less_than('plot mask background', rgb, 256)
cv.check_greater_than('plot mask background', rgb, 0, True)
cv.check_less_than('plot mask background', rgb, 256)
self._mask_background = mask_background
def __repr__(self):
@ -261,6 +261,97 @@ class Plot(object):
string += '{0: <16}{1}{2}\n'.format('\tCol Spec', '=\t', self._col_spec)
return string
def colorize(self, geometry, seed=1):
"""Generate a color scheme for each domain in the plot.
This routine may be used to generate random, reproducible color schemes.
The colors generated are based upon cell/material IDs in the geometry.
Parameters
----------
geometry : openmc.Geometry
The geometry for which the plot is defined
seed : Integral
The random number seed used to generate the color scheme
"""
cv.check_type('geometry', geometry, openmc.Geometry)
cv.check_type('seed', seed, Integral)
cv.check_greater_than('seed', seed, 1, equality=True)
# Get collections of the domains which will be plotted
if self.color is 'mat':
domains = geometry.get_all_materials()
else:
domains = geometry.get_all_cells()
# Set the seed for the random number generator
np.random.seed(seed)
# Generate random colors for each feature
self.col_spec = {}
for domain in domains:
r = np.random.randint(0, 256)
g = np.random.randint(0, 256)
b = np.random.randint(0, 256)
self.col_spec[domain] = (r, g, b)
def highlight_domains(self, geometry, domains, seed=1,
alpha=0.5, background='gray'):
"""Use alpha compositing to highlight one or more domains in the plot.
This routine generates a color scheme and applies alpha compositing
to make all domains except the highlighted ones appear partially
transparent.
Parameters
----------
geometry : openmc.Geometry
The geometry for which the plot is defined
domains : Iterable of Integral
A collection of the domain IDs to highlight in the plot
seed : Integral
The random number seed used to generate the color scheme
alpha : Real in [0,1]
The value to apply in alpha compisiting
background : 3-tuple of Integral or 'white' or 'black' or 'gray'
The background color to apply in alpha compisiting
"""
cv.check_iterable_type('domains', domains, Integral)
cv.check_type('alpha', alpha, Real)
cv.check_greater_than('alpha', alpha, 0., equality=True)
cv.check_less_than('alpha', alpha, 1., equality=True)
# Get a background (R,G,B) tuple to apply in alpha compositing
if isinstance(background, basestring):
if background == 'white':
background = (255, 255, 255)
elif background == 'black':
background = (0, 0, 0)
elif background == 'gray':
background = (160, 160, 160)
else:
msg = 'The background "{}" is not defined'.format(background)
raise ValueError(msg)
cv.check_iterable_type('background', background, Integral)
# Generate a color scheme
self.colorize(geometry, seed)
# Apply alpha compositing to the colors for all domains
# other than those the user wishes to highlight
for domain_id in self.col_spec:
if domain_id not in domains:
r, g, b = self.col_spec[domain_id]
r = int(((1-alpha) * background[0]) + (alpha * r))
g = int(((1-alpha) * background[1]) + (alpha * g))
b = int(((1-alpha) * background[2]) + (alpha * b))
self._col_spec[domain_id] = (r, g, b)
def get_plot_xml(self):
"""Return XML representation of the plot
@ -349,6 +440,51 @@ class PlotsFile(object):
self._plots.remove(plot)
def colorize(self, geometry, seed=1):
"""Generate a consistent color scheme for each domain in each plot.
This routine may be used to generate random, reproducible color schemes.
The colors generated are based upon cell/material IDs in the geometry.
The color schemes will be consistent for all plots in "plots.xml".
Parameters
----------
geometry : openmc.Geometry
The geometry for which the plots are defined
seed : Integral
The random number seed used to generate the color scheme
"""
for plot in self._plots:
plot.colorize(geometry, seed)
def highlight_domains(self, geometry, domains, seed=1,
alpha=0.5, background='gray'):
"""Use alpha compositing to highlight one or more domains in the plot.
This routine generates a color scheme and applies alpha compositing
to make all domains except the highlighted ones partially transparent.
Parameters
----------
geometry : openmc.Geometry
The geometry for which the plot is defined
domains : Iterable of Integral
A collection of the domain IDs to highlight in the plot
seed : Integral
The random number seed used to generate the color scheme
alpha : Real in [0,1]
The value to apply in alpha compisiting
background : 3-tuple of Integral or 'white' or 'black' or 'gray'
The background color to apply in alpha compisiting
"""
for plot in self._plots:
plot.highlight_domains(geometry, domains, seed, alpha, background)
def _create_plot_subelements(self):
for plot in self._plots:
xml_element = plot.get_plot_xml()
@ -363,6 +499,9 @@ class PlotsFile(object):
"""
# Reset xml element tree
self._plots_file.clear()
self._create_plot_subelements()
# Clean the indentation in the file to be user-readable

362
openmc/region.py Normal file
View file

@ -0,0 +1,362 @@
from abc import ABCMeta, abstractmethod
from collections import Iterable
import numpy as np
from openmc.checkvalue import check_type
class Region(object):
"""Region of space that can be assigned to a cell.
Region is an abstract base class that is inherited by Halfspace,
Intersection, Union, and Complement. Each of those respective classes are
typically not instantiated directly but rather are created through operators
of the Surface and Region classes.
"""
__metaclass__ = ABCMeta
def __and__(self, other):
return Intersection(self, other)
def __or__(self, other):
return Union(self, other)
def __invert__(self):
return Complement(self)
@abstractmethod
def __str__(self):
return ''
def __eq__(self, other):
if not isinstance(other, type(self)):
return False
elif str(self) != str(other):
return False
else:
return True
def __ne__(self, other):
return not self == other
@staticmethod
def from_expression(expression, surfaces):
"""Generate a region given an infix expression.
Parameters
----------
expression : str
Boolean expression relating surface half-spaces. The possible
operators are union '|', intersection ' ', and complement '~'. For
example, '(1 -2) | 3 ~(4 -5)'.
surfaces : dict
Dictionary whose keys are suface IDs that appear in the Boolean
expression and whose values are Surface objects.
"""
# Strip leading and trailing whitespace
expression = expression.strip()
# Convert the string expression into a list of tokens, i.e., operators
# and surface half-spaces, representing the expression in infix
# notation.
i = 0
i_start = -1
tokens = []
while i < len(expression):
if expression[i] in '()|~ ':
# If special character appears immediately after a non-operator,
# create a token with the apporpriate half-space
if i_start >= 0:
j = int(expression[i_start:i])
if j < 0:
tokens.append(-surfaces[abs(j)])
else:
tokens.append(+surfaces[abs(j)])
if expression[i] in '()|~':
# For everything other than intersection, add the operator
# to the list of tokens
tokens.append(expression[i])
else:
# Find next non-space character
while expression[i+1] == ' ':
i += 1
# If previous token is a halfspace or right parenthesis and next token
# is not a left parenthese or union operator, that implies that the
# whitespace is to be interpreted as an intersection operator
if (i_start >= 0 or tokens[-1] == ')') and \
expression[i+1] not in ')|':
tokens.append(' ')
i_start = -1
else:
# Check for invalid characters
if expression[i] not in '-+0123456789':
raise SyntaxError("Invalid character '{}' in expression"
.format(expression[i]))
# If we haven't yet reached the start of a word, start one
if i_start < 0:
i_start = i
i += 1
# If we've reached the end and we're still in a word, create a
# half-space token and add it to the list
if i_start >= 0:
j = int(expression[i_start:])
if j < 0:
tokens.append(-surfaces[abs(j)])
else:
tokens.append(+surfaces[abs(j)])
# The functions below are used to apply an operator to operands on the
# output queue during the shunting yard algorithm.
def can_be_combined(region):
return isinstance(region, Complement) or hasattr(region, 'surface')
def apply_operator(output, operator):
r2 = output.pop()
if operator == ' ':
r1 = output.pop()
if isinstance(r1, Intersection) and can_be_combined(r2):
r1.nodes.append(r2)
output.append(r1)
elif isinstance(r2, Intersection) and can_be_combined(r1):
r2.nodes.insert(0, r1)
output.append(r2)
elif isinstance(r1, Intersection) and isinstance(r2, Intersection):
r1.nodes += r2.nodes
output.append(r1)
else:
output.append(Intersection(r1, r2))
elif operator == '|':
r1 = output.pop()
if isinstance(r1, Union) and can_be_combined(r2):
r1.nodes.append(r2)
output.append(r1)
elif isinstance(r2, Union) and can_be_combined(r1):
r2.nodes.insert(0, r1)
output.append(r2)
elif isinstance(r1, Union) and isinstance(r2, Union):
r1.nodes += r2.nodes
output.append(r1)
else:
output.append(Union(r1, r2))
elif operator == '~':
output.append(Complement(r2))
# The following is an implementation of the shunting yard algorithm to
# generate an abstract syntax tree for the region expression.
output = []
stack = []
precedence = {'|': 1, ' ': 2, '~': 3}
associativity = {'|': 'left', ' ': 'left', '~': 'right'}
for token in tokens:
if token in (' ', '|', '~'):
# Normal operators
while stack:
op = stack[-1]
if (op not in ('(', ')') and
((associativity[token] == 'right' and
precedence[token] < precedence[op]) or
(associativity[token] == 'left' and
precedence[token] <= precedence[op]))):
apply_operator(output, stack.pop())
else:
break
stack.append(token)
elif token == '(':
# Left parentheses
stack.append(token)
elif token == ')':
# Right parentheses
while stack[-1] != '(':
apply_operator(output, stack.pop())
if len(stack) == 0:
raise SyntaxError('Mismatched parentheses in '
'region specification.')
stack.pop()
else:
# Surface halfspaces
output.append(token)
while stack:
if stack[-1] in '()':
raise SyntaxError('Mismatched parentheses in region '
'specification.')
apply_operator(output, stack.pop())
# Since we are generating an abstract syntax tree rather than a reverse
# Polish notation expression, the output queue should have a single item
# at the end
return output[0]
class Intersection(Region):
"""Intersection of two or more regions.
Instances of Intersection are generally created via the __and__ operator
applied to two instances of Region. This is illustrated in the following
example:
>>> equator = openmc.surface.ZPlane(z0=0.0)
>>> earth = openmc.surface.Sphere(R=637.1e6)
>>> northern_hemisphere = -earth & +equator
>>> southern_hemisphere = -earth & -equator
>>> type(northern_hemisphere)
<class 'openmc.region.Intersection'>
Parameters
----------
*nodes
Regions to take the intersection of
Attributes
----------
nodes : tuple of Region
Regions to take the intersection of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""
def __init__(self, *nodes):
self.nodes = list(nodes)
def __str__(self):
return '(' + ' '.join(map(str, self.nodes)) + ')'
@property
def nodes(self):
return self._nodes
@property
def bounding_box(self):
lower_left = np.array([-np.inf, -np.inf, -np.inf])
upper_right = np.array([np.inf, np.inf, np.inf])
for n in self.nodes:
lower_left_n, upper_right_n = n.bounding_box
lower_left[:] = np.maximum(lower_left, lower_left_n)
upper_right[:] = np.minimum(upper_right, upper_right_n)
return lower_left, upper_right
@nodes.setter
def nodes(self, nodes):
check_type('nodes', nodes, Iterable, Region)
self._nodes = nodes
class Union(Region):
"""Union of two or more regions.
Instances of Union are generally created via the __or__ operator applied to
two instances of Region. This is illustrated in the following example:
>>> s1 = openmc.surface.ZPlane(z0=0.0)
>>> s2 = openmc.surface.Sphere(R=637.1e6)
>>> type(-s2 | +s1)
<class 'openmc.region.Union'>
Parameters
----------
*nodes
Regions to take the union of
Attributes
----------
nodes : tuple of Region
Regions to take the union of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""
def __init__(self, *nodes):
self.nodes = list(nodes)
def __str__(self):
return '(' + ' | '.join(map(str, self.nodes)) + ')'
@property
def nodes(self):
return self._nodes
@property
def bounding_box(self):
lower_left = np.array([np.inf, np.inf, np.inf])
upper_right = np.array([-np.inf, -np.inf, -np.inf])
for n in self.nodes:
lower_left_n, upper_right_n = n.bounding_box
lower_left[:] = np.minimum(lower_left, lower_left_n)
upper_right[:] = np.maximum(upper_right, upper_right_n)
return lower_left, upper_right
@nodes.setter
def nodes(self, nodes):
check_type('nodes', nodes, Iterable, Region)
self._nodes = nodes
class Complement(Region):
"""Complement of a region.
The Complement of an existing Region can be created by using the __invert__
operator as the following example demonstrates:
>>> xl = openmc.surface.XPlane(x0=-10.0)
>>> xr = openmc.surface.XPlane(x0=10.0)
>>> yl = openmc.surface.YPlane(y0=-10.0)
>>> yr = openmc.surface.YPlane(y0=10.0)
>>> inside_box = +xl & -xr & +yl & -yl
>>> outside_box = ~inside_box
>>> type(outside_box)
<class 'openmc.region.Complement'>
Parameters
----------
node : Region
Region to take the complement of
Attributes
----------
node : Region
Regions to take the complement of
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""
def __init__(self, node):
self.node = node
def __str__(self):
return '~' + str(self.node)
@property
def node(self):
return self._node
@node.setter
def node(self, node):
check_type('node', node, Region)
self._node = node
@property
def bounding_box(self):
# Use De Morgan's laws to distribute the complement operator so that it
# only applies to surface half-spaces, thus allowing us to calculate the
# bounding box in the usual recursive manner.
if isinstance(self.node, Union):
temp_region = Intersection(*[~n for n in self.node.nodes])
elif isinstance(self.node, Intersection):
temp_region = Union(*[~n for n in self.node.nodes])
elif isinstance(self.node, Complement):
temp_region = self.node.node
else:
temp_region = ~self.node
return temp_region.bounding_box

View file

@ -20,6 +20,8 @@ class SettingsFile(object):
Attributes
----------
run_mode : {'eigenvalue' or 'fixed source'}
The type of calculation to perform (default is 'eigenvalue')
batches : int
Number of batches to simulate
generations_per_batch : int
@ -122,7 +124,9 @@ class SettingsFile(object):
"""
def __init__(self):
# Eigenvalue subelement
# Run mode subelement (default is 'eigenvalue')
self._run_mode = 'eigenvalue'
self._batches = None
self._generations_per_batch = None
self._inactive = None
@ -196,9 +200,13 @@ class SettingsFile(object):
self._dd_count_interactions = False
self._settings_file = ET.Element("settings")
self._eigenvalue_subelement = None
self._run_mode_subelement = None
self._source_element = None
@property
def run_mode(self):
return self._run_mode
@property
def batches(self):
return self._batches
@ -399,6 +407,14 @@ class SettingsFile(object):
def dd_count_interactions(self):
return self._dd_count_interactions
@run_mode.setter
def run_mode(self, run_mode):
if 'run_mode' not in ['eigenvalue', 'fixed source']:
msg = 'Unable to set run mode to "{0}". Only "eigenvalue" ' \
'and "fixed source" are supported."'.format(run_mode)
raise ValueError(msg)
self._run_mode = run_mode
@batches.setter
def batches(self, batches):
check_type('batches', batches, Integral)
@ -861,57 +877,47 @@ class SettingsFile(object):
self._dd_count_interactions = interactions
def _create_eigenvalue_subelement(self):
self._create_particles_subelement()
self._create_batches_subelement()
self._create_inactive_subelement()
self._create_generations_per_batch_subelement()
self._create_keff_trigger_subelement()
def _create_run_mode_subelement(self):
if self.run_mode == 'eigenvalue':
self._run_mode_subelement = \
ET.SubElement(self._settings_file, "eigenvalue")
self._create_particles_subelement()
self._create_batches_subelement()
self._create_inactive_subelement()
self._create_generations_per_batch_subelement()
self._create_keff_trigger_subelement()
else:
if self._run_mode_subelement is None:
self._run_mode_subelement = \
ET.SubElement(self._settings_file, "fixed_source")
self._create_particles_subelement()
self._create_batches_subelement()
def _create_batches_subelement(self):
if self._batches is not None:
if self._eigenvalue_subelement is None:
self._eigenvalue_subelement = ET.SubElement(self._settings_file,
"eigenvalue")
element = ET.SubElement(self._eigenvalue_subelement, "batches")
element = ET.SubElement(self._run_mode_subelement, "batches")
element.text = str(self._batches)
def _create_generations_per_batch_subelement(self):
if self._generations_per_batch is not None:
if self._eigenvalue_subelement is None:
self._eigenvalue_subelement = ET.SubElement(self._settings_file,
"eigenvalue")
element = ET.SubElement(self._eigenvalue_subelement,
element = ET.SubElement(self._run_mode_subelement,
"generations_per_batch")
element.text = str(self._generations_per_batch)
def _create_inactive_subelement(self):
if self._inactive is not None:
if self._eigenvalue_subelement is None:
self._eigenvalue_subelement = ET.SubElement(self._settings_file,
"eigenvalue")
element = ET.SubElement(self._eigenvalue_subelement, "inactive")
element = ET.SubElement(self._run_mode_subelement, "inactive")
element.text = str(self._inactive)
def _create_particles_subelement(self):
if self._particles is not None:
if self._eigenvalue_subelement is None:
self._eigenvalue_subelement = ET.SubElement(self._settings_file,
"eigenvalue")
element = ET.SubElement(self._eigenvalue_subelement, "particles")
element = ET.SubElement(self._run_mode_subelement, "particles")
element.text = str(self._particles)
def _create_keff_trigger_subelement(self):
if self._keff_trigger is not None:
if self._eigenvalue_subelement is None:
self._eigenvalue_subelement = ET.SubElement(self._settings_file,
"eigenvalue")
element = ET.SubElement(self._eigenvalue_subelement, "keff_trigger")
element = ET.SubElement(self._run_mode_subelement, "keff_trigger")
for key in self._keff_trigger:
subelement = ET.SubElement(element, key)
@ -1178,7 +1184,14 @@ class SettingsFile(object):
"""
self._create_eigenvalue_subelement()
# Reset xml element tree
self._settings_file.clear()
self._source_subelement = None
self._trigger_subelement = None
self._run_mode_subelement = None
self._source_element = None
self._create_run_mode_subelement()
self._create_source_subelement()
self._create_output_subelement()
self._create_statepoint_subelement()

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,22 @@
import numpy as np
import openmc
from openmc.region import Region
class Summary(object):
"""Information summarizing the geometry, materials, and tallies used in a
simulation.
Attributes
----------
openmc_geometry : openmc.Geometry
An OpenMC geometry object reconstructed from the summary file
opencg_geometry : opencg.Geometry
An OpenCG geometry object equivalent to the OpenMC geometry
encapsulated by the summary file. Use of this attribute requires
installation of the OpenCG Python module.
"""
def __init__(self, filename):
@ -22,31 +32,41 @@ class Summary(object):
raise ValueError(msg)
self._f = h5py.File(filename, 'r')
self.openmc_geometry = None
self.opencg_geometry = None
self._openmc_geometry = None
self._opencg_geometry = None
self._read_metadata()
self._read_geometry()
self._read_tallies()
@property
def openmc_geometry(self):
return self._openmc_geometry
@property
def opencg_geometry(self):
if self._opencg_geometry is None:
from openmc.opencg_compatible import get_opencg_geometry
self._opencg_geometry = get_opencg_geometry(self.openmc_geometry)
return self._opencg_geometry
def _read_metadata(self):
# Read OpenMC version
self.version = [self._f['version_major'][0],
self._f['version_minor'][0],
self._f['version_release'][0]]
self.version = [self._f['version_major'].value,
self._f['version_minor'].value,
self._f['version_release'].value]
# Read date and time
self.date_and_time = self._f['date_and_time'][...]
self.n_batches = self._f['n_batches'][0]
self.n_particles = self._f['n_particles'][0]
self.n_active = self._f['n_active'][0]
self.n_inactive = self._f['n_inactive'][0]
self.gen_per_batch = self._f['gen_per_batch'][0]
self.n_procs = self._f['n_procs'][0]
self.n_batches = self._f['n_batches'].value
self.n_particles = self._f['n_particles'].value
self.n_active = self._f['n_active'].value
self.n_inactive = self._f['n_inactive'].value
self.gen_per_batch = self._f['gen_per_batch'].value
self.n_procs = self._f['n_procs'].value
def _read_geometry(self):
# Read in and initialize the Materials and Geometry
self._read_nuclides()
self._read_materials()
self._read_surfaces()
self._read_cells()
@ -54,37 +74,8 @@ class Summary(object):
self._read_lattices()
self._finalize_geometry()
def _read_nuclides(self):
self.n_nuclides = self._f['nuclides/n_nuclides'][0]
# Initialize dictionary for each Nuclide
# Keys - Nuclide ZAIDs
# Values - Nuclide objects
self.nuclides = {}
for key in self._f['nuclides'].keys():
if key == 'n_nuclides':
continue
index = self._f['nuclides'][key]['index'][0]
alias = self._f['nuclides'][key]['alias'][0]
zaid = self._f['nuclides'][key]['zaid'][0]
# Read the Nuclide's name (e.g., 'H-1' or 'U-235')
name = alias.split('.')[0]
# Read the Nuclide's cross-section identifier (e.g., '70c')
xs = alias.split('.')[1]
# Initialize this Nuclide and add to global dictionary of Nuclides
if 'nat' in name:
self.nuclides[zaid] = openmc.Element(name=name, xs=xs)
else:
self.nuclides[zaid] = openmc.Nuclide(name=name, xs=xs)
self.nuclides[zaid].zaid = zaid
def _read_materials(self):
self.n_materials = self._f['materials/n_materials'][0]
self.n_materials = self._f['n_materials'].value
# Initialize dictionary for each Material
# Keys - Material keys
@ -96,51 +87,42 @@ class Summary(object):
continue
material_id = int(key.lstrip('material '))
index = self._f['materials'][key]['index'][0]
name = self._f['materials'][key]['name'][0]
density = self._f['materials'][key]['atom_density'][0]
index = self._f['materials'][key]['index'].value
name = self._f['materials'][key]['name'].value.decode()
density = self._f['materials'][key]['atom_density'].value
nuc_densities = self._f['materials'][key]['nuclide_densities'][...]
nuclides = self._f['materials'][key]['nuclides'][...]
n_sab = self._f['materials'][key]['n_sab'][0]
sab_names = []
sab_xs = []
# Read the names of the S(a,b) tables for this Material
for i in range(1, n_sab+1):
sab_table = self._f['materials'][key]['sab_tables'][str(i)][0]
# Read the cross-section identifiers for each S(a,b) table
sab_names.append(sab_table.split('.')[0])
sab_xs.append(sab_table.split('.')[1])
nuclides = self._f['materials'][key]['nuclides'].value
# Create the Material
material = openmc.Material(material_id=material_id, name=name)
# Set the Material's density to g/cm3 - this is what is used in OpenMC
material.set_density(density=density, units='g/cm3')
# Read the names of the S(a,b) tables for this Material and add them
if 'sab_names' in self._f['materials'][key]:
sab_tables = self._f['materials'][key]['sab_names'].value
for sab_table in sab_tables:
name, xs = sab_table.decode().split('.')
material.add_s_alpha_beta(name, xs)
# Add all Nuclides to the Material
for i, zaid in enumerate(nuclides):
nuclide = self.get_nuclide_by_zaid(zaid)
density = nuc_densities[i]
# Set the Material's density to atom/b-cm as used by OpenMC
material.set_density(density=density, units='atom/b-cm')
if isinstance(nuclide, openmc.Nuclide):
material.add_nuclide(nuclide, percent=density, percent_type='ao')
elif isinstance(nuclide, openmc.Element):
material.add_element(nuclide, percent=density, percent_type='ao')
# Add all nuclides to the Material
for fullname, density in zip(nuclides, nuc_densities):
fullname = fullname.decode().strip()
name, xs = fullname.split('.')
# Add S(a,b) table(s?) to the Material
for i in range(n_sab):
name = sab_names[i]
xs = sab_xs[i]
material.add_s_alpha_beta(name, xs)
if 'nat' in name:
material.add_element(openmc.Element(name=name, xs=xs),
percent=density, percent_type='ao')
else:
material.add_nuclide(openmc.Nuclide(name=name, xs=xs),
percent=density, percent_type='ao')
# Add the Material to the global dictionary of all Materials
self.materials[index] = material
def _read_surfaces(self):
self.n_surfaces = self._f['geometry/n_surfaces'][0]
self.n_surfaces = self._f['geometry/n_surfaces'].value
# Initialize dictionary for each Surface
# Keys - Surface keys
@ -152,75 +134,80 @@ class Summary(object):
continue
surface_id = int(key.lstrip('surface '))
index = self._f['geometry/surfaces'][key]['index'][0]
name = self._f['geometry/surfaces'][key]['name'][0]
surf_type = self._f['geometry/surfaces'][key]['type'][...][0]
bc = self._f['geometry/surfaces'][key]['boundary_condition'][...][0]
index = self._f['geometry/surfaces'][key]['index'].value
name = self._f['geometry/surfaces'][key]['name'].value.decode()
surf_type = self._f['geometry/surfaces'][key]['type'].value.decode()
bc = self._f['geometry/surfaces'][key]['boundary_condition'].value.decode()
coeffs = self._f['geometry/surfaces'][key]['coefficients'][...]
# Create the Surface based on its type
if surf_type == 'X Plane':
if surf_type == 'x-plane':
x0 = coeffs[0]
surface = openmc.XPlane(surface_id, bc, x0, name)
elif surf_type == 'Y Plane':
elif surf_type == 'y-plane':
y0 = coeffs[0]
surface = openmc.YPlane(surface_id, bc, y0, name)
elif surf_type == 'Z Plane':
elif surf_type == 'z-plane':
z0 = coeffs[0]
surface = openmc.ZPlane(surface_id, bc, z0, name)
elif surf_type == 'Plane':
elif surf_type == 'plane':
A = coeffs[0]
B = coeffs[1]
C = coeffs[2]
D = coeffs[3]
surface = openmc.Plane(surface_id, bc, A, B, C, D, name)
elif surf_type == 'X Cylinder':
elif surf_type == 'x-cylinder':
y0 = coeffs[0]
z0 = coeffs[1]
R = coeffs[2]
surface = openmc.XCylinder(surface_id, bc, y0, z0, R, name)
elif surf_type == 'Y Cylinder':
elif surf_type == 'y-cylinder':
x0 = coeffs[0]
z0 = coeffs[1]
R = coeffs[2]
surface = openmc.YCylinder(surface_id, bc, x0, z0, R, name)
elif surf_type == 'Z Cylinder':
elif surf_type == 'z-cylinder':
x0 = coeffs[0]
y0 = coeffs[1]
R = coeffs[2]
surface = openmc.ZCylinder(surface_id, bc, x0, y0, R, name)
elif surf_type == 'Sphere':
elif surf_type == 'sphere':
x0 = coeffs[0]
y0 = coeffs[1]
z0 = coeffs[2]
R = coeffs[3]
surface = openmc.Sphere(surface_id, bc, x0, y0, z0, R, name)
elif surf_type in ['X Cone', 'Y Cone', 'Z Cone']:
elif surf_type in ['x-cone', 'y-cone', 'z-cone']:
x0 = coeffs[0]
y0 = coeffs[1]
z0 = coeffs[2]
R2 = coeffs[3]
if surf_type == 'X Cone':
if surf_type == 'x-cone':
surface = openmc.XCone(surface_id, bc, x0, y0, z0, R2, name)
if surf_type == 'Y Cone':
if surf_type == 'y-cone':
surface = openmc.YCone(surface_id, bc, x0, y0, z0, R2, name)
if surf_type == 'Z Cone':
if surf_type == 'z-cone':
surface = openmc.ZCone(surface_id, bc, x0, y0, z0, R2, name)
elif surf_type == 'quadric':
a, b, c, d, e, f, g, h, j, k = coeffs
surface = openmc.Quadric(surface_id, bc, a, b, c, d, e, f,
g, h, j, k, name)
# Add Surface to global dictionary of all Surfaces
self.surfaces[index] = surface
def _read_cells(self):
self.n_cells = self._f['geometry/n_cells'][0]
self.n_cells = self._f['geometry/n_cells'].value
# Initialize dictionary for each Cell
# Keys - Cell keys
@ -240,41 +227,37 @@ class Summary(object):
continue
cell_id = int(key.lstrip('cell '))
index = self._f['geometry/cells'][key]['index'][0]
name = self._f['geometry/cells'][key]['name'][0]
fill_type = self._f['geometry/cells'][key]['fill_type'][...][0]
index = self._f['geometry/cells'][key]['index'].value
name = self._f['geometry/cells'][key]['name'].value.decode()
fill_type = self._f['geometry/cells'][key]['fill_type'].value.decode()
if fill_type == 'normal':
fill = self._f['geometry/cells'][key]['material'][0]
fill = self._f['geometry/cells'][key]['material'].value
elif fill_type == 'universe':
fill = self._f['geometry/cells'][key]['fill'][0]
fill = self._f['geometry/cells'][key]['fill'].value
else:
fill = self._f['geometry/cells'][key]['lattice'][0]
fill = self._f['geometry/cells'][key]['lattice'].value
if 'surfaces' in self._f['geometry/cells'][key].keys():
surfaces = self._f['geometry/cells'][key]['surfaces'][...]
if 'region' in self._f['geometry/cells'][key].keys():
region = self._f['geometry/cells'][key]['region'].value.decode()
else:
surfaces = []
region = []
# Create this Cell
cell = openmc.Cell(cell_id=cell_id, name=name)
if fill_type == 'universe':
maps = self._f['geometry/cells'][key]['maps'][0]
if maps > 0:
if 'offset' in self._f['geometry/cells'][key]:
offset = self._f['geometry/cells'][key]['offset'][...]
cell.set_offset(offset)
cell.offsets = offset
translated = self._f['geometry/cells'][key]['translated'][0]
if translated:
if 'translation' in self._f['geometry/cells'][key]:
translation = \
self._f['geometry/cells'][key]['translation'][...]
translation = np.asarray(translation, dtype=np.float64)
cell.translation = translation
rotated = self._f['geometry/cells'][key]['rotated'][0]
if rotated:
if 'rotation' in self._f['geometry/cells'][key]:
rotation = \
self._f['geometry/cells'][key]['rotation'][...]
rotation = np.asarray(rotation, dtype=np.int)
@ -283,19 +266,16 @@ class Summary(object):
# Store Cell fill information for after Universe/Lattice creation
self._cell_fills[index] = (fill_type, fill)
# Iterate over all Surfaces and add them to the Cell
for surface_halfspace in surfaces:
halfspace = np.sign(surface_halfspace)
surface_id = np.abs(surface_halfspace)
surface = self.surfaces[surface_id]
cell.add_surface(surface, halfspace)
# Generate Region object given infix expression
if region:
cell.region = Region.from_expression(
region, {s.id: s for s in self.surfaces.values()})
# Add the Cell to the global dictionary of all Cells
self.cells[index] = cell
def _read_universes(self):
self.n_universes = self._f['geometry/n_universes'][0]
self.n_universes = self._f['geometry/n_universes'].value
# Initialize dictionary for each Universe
# Keys - Universe keys
@ -307,7 +287,7 @@ class Summary(object):
continue
universe_id = int(key.lstrip('universe '))
index = self._f['geometry/universes'][key]['index'][0]
index = self._f['geometry/universes'][key]['index'].value
cells = self._f['geometry/universes'][key]['cells'][...]
# Create this Universe
@ -322,7 +302,7 @@ class Summary(object):
self.universes[index] = universe
def _read_lattices(self):
self.n_lattices = self._f['geometry/n_lattices'][0]
self.n_lattices = self._f['geometry/n_lattices'].value
# Initialize lattices for each Lattice
# Keys - Lattice keys
@ -334,21 +314,21 @@ class Summary(object):
continue
lattice_id = int(key.lstrip('lattice '))
index = self._f['geometry/lattices'][key]['index'][0]
name = self._f['geometry/lattices'][key]['name'][0]
lattice_type = self._f['geometry/lattices'][key]['type'][...][0]
maps = self._f['geometry/lattices'][key]['maps'][0]
offset_size = self._f['geometry/lattices'][key]['offset_size'][0]
index = self._f['geometry/lattices'][key]['index'].value
name = self._f['geometry/lattices'][key]['name'].value.decode()
lattice_type = self._f['geometry/lattices'][key]['type'].value.decode()
if offset_size > 0:
if 'offsets' in self._f['geometry/lattices'][key]:
offsets = self._f['geometry/lattices'][key]['offsets'][...]
else:
offsets = None
if lattice_type == 'rectangular':
dimension = self._f['geometry/lattices'][key]['dimension'][...]
lower_left = \
self._f['geometry/lattices'][key]['lower_left'][...]
pitch = self._f['geometry/lattices'][key]['pitch'][...]
outer = self._f['geometry/lattices'][key]['outer'][0]
outer = self._f['geometry/lattices'][key]['outer'].value
universe_ids = \
self._f['geometry/lattices'][key]['universes'][...]
@ -382,7 +362,7 @@ class Summary(object):
universes = universes[:, ::-1, :]
lattice.universes = universes
if offset_size > 0:
if offsets is not None:
offsets = np.swapaxes(offsets, 0, 1)
offsets = np.swapaxes(offsets, 1, 2)
lattice.offsets = offsets
@ -476,7 +456,7 @@ class Summary(object):
# Lattice is 2D; extract the only axial level
lattice.universes = universes[0]
if offset_size > 0:
if offsets is not None:
lattice.offsets = offsets
# Add the Lattice to the global dictionary of all Lattices
@ -484,7 +464,7 @@ class Summary(object):
def _finalize_geometry(self):
# Initialize Geometry object
self.openmc_geometry = openmc.Geometry()
self._openmc_geometry = openmc.Geometry()
# Iterate over all Cells and add fill Materials, Universes and Lattices
for cell_key in self._cell_fills.keys():
@ -521,7 +501,7 @@ class Summary(object):
self.n_tallies = 0
return
self.n_tallies = self._f['tallies/n_tallies'][0]
self.n_tallies = self._f['tallies/n_tallies'].value
# OpenMC Tally keys
all_keys = self._f['tallies/'].keys()
@ -531,43 +511,34 @@ class Summary(object):
# Iterate over all Tallies
for tally_key in tally_keys:
tally_id = int(tally_key.strip('tally '))
subbase = '{0}{1}'.format(base, tally_id)
# Read Tally name metadata
name_size = self._f['{0}/name_size'.format(subbase)][0]
if (name_size > 0):
tally_name = self._f['{0}/name'.format(subbase)][0]
tally_name = tally_name.lstrip('[\'')
tally_name = tally_name.rstrip('\']')
else:
tally_name = ''
tally_name = self._f['{0}/name'.format(subbase)].value.decode()
# Create Tally object and assign basic properties
tally = openmc.Tally(tally_id, tally_name)
# Read score metadata
score_bins = self._f['{0}/score_bins'.format(subbase)][...]
for score_bin in score_bins:
tally.add_score(openmc.SCORE_TYPES[score_bin])
scores = self._f['{0}/score_bins'.format(subbase)].value
for score in scores:
tally.add_score(score.decode())
num_score_bins = self._f['{0}/n_score_bins'.format(subbase)][...]
tally.num_score_bins = num_score_bins
# Read filter metadata
num_filters = self._f['{0}/n_filters'.format(subbase)][0]
num_filters = self._f['{0}/n_filters'.format(subbase)].value
# Initialize all Filters
for j in range(1, num_filters+1):
subsubbase = '{0}/filter {1}'.format(subbase, j)
# Read filter type (e.g., "cell", "energy", etc.)
filter_type_code = self._f['{0}/type'.format(subsubbase)][0]
filter_type = openmc.FILTER_TYPES[filter_type_code]
filter_type = self._f['{0}/type'.format(subsubbase)].value.decode()
# Read the filter bins
num_bins = self._f['{0}/n_bins'.format(subsubbase)][0]
num_bins = self._f['{0}/n_bins'.format(subsubbase)].value
bins = self._f['{0}/bins'.format(subsubbase)][...]
# Create Filter object
@ -580,45 +551,6 @@ class Summary(object):
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_id] = tally
def make_opencg_geometry(self):
"""Create OpenCG geometry based on the information contained in the summary
file. The geometry is stored as the 'opencg_geometry' attribute.
"""
try:
from openmc.opencg_compatible import get_opencg_geometry
except ImportError:
msg = 'Unable to import opencg which is needed ' \
'by Summary.make_opencg_geometry()'
raise ImportError(msg)
if self.opencg_geometry is None:
self.opencg_geometry = get_opencg_geometry(self.openmc_geometry)
def get_nuclide_by_zaid(self, zaid):
"""Return a Nuclide object given the 'zaid' identifier for the nuclide.
Parameters
----------
zaid : int
1000*Z + A, where Z is the atomic number of the nuclide and A is the
mass number. For example, the zaid for U-235 is 92235.
Returns
-------
nuclide : openmc.nuclide.Nuclide or None
Nuclide matching the specified zaid, or None if no matching object
is found.
"""
for index, nuclide in self.nuclides.items():
if nuclide._zaid == zaid:
return nuclide
return None
def get_material_by_id(self, material_id):
"""Return a Material object given the material id

View file

@ -3,8 +3,10 @@ from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import numpy as np
from openmc.checkvalue import check_type, check_value, check_greater_than
from openmc.constants import BC_TYPES
from openmc.region import Region
if sys.version_info[0] >= 3:
basestring = str
@ -12,6 +14,8 @@ if sys.version_info[0] >= 3:
# A static variable for auto-generated Surface IDs
AUTO_SURFACE_ID = 10000
_BC_TYPES = ['transmission', 'vacuum', 'reflective', 'periodic']
def reset_auto_surface_id():
global AUTO_SURFACE_ID
@ -37,17 +41,17 @@ class Surface(object):
Attributes
----------
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface.
coeffs : dict
Dictionary of surface coefficients
id : int
Unique identifier for the surface
name : str
Name of the surface
type : str
Type of the surface, e.g. 'x-plane'
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface.
coeffs : dict
Dictionary of surface coefficients
"""
@ -67,6 +71,28 @@ class Surface(object):
# proper order
self._coeff_keys = []
def __neg__(self):
return Halfspace(self, '-')
def __pos__(self):
return Halfspace(self, '+')
def __repr__(self):
string = 'Surface\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
string += '{0: <16}{1}{2}\n'.format('\tBoundary', '=\t', self._boundary_type)
coeffs = '{0: <16}'.format('\tCoefficients') + '\n'
for coeff in self._coeffs:
coeffs += '{0: <16}{1}{2}\n'.format(coeff, '=\t', self._coeffs[coeff])
string += coeffs
return string
@property
def id(self):
return self._id
@ -95,35 +121,49 @@ class Surface(object):
AUTO_SURFACE_ID += 1
else:
check_type('surface ID', surface_id, Integral)
check_greater_than('surface ID', surface_id, 0)
check_greater_than('surface ID', surface_id, 0, equality=True)
self._id = surface_id
@name.setter
def name(self, name):
check_type('surface name', name, basestring)
self._name = name
if name is not None:
check_type('surface name', name, basestring)
self._name = name
else:
self._name = ''
@boundary_type.setter
def boundary_type(self, boundary_type):
check_type('boundary type', boundary_type, basestring)
check_value('boundary type', boundary_type, BC_TYPES.values())
check_value('boundary type', boundary_type, _BC_TYPES)
self._boundary_type = boundary_type
def __repr__(self):
string = 'Surface\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
string += '{0: <16}{1}{2}\n'.format('\tBoundary', '=\t', self._boundary_type)
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
coeffs = '{0: <16}'.format('\tCoefficients') + '\n'
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. If the half-space is
unbounded in a particular direction, numpy.inf is used to represent
infinity.
for coeff in self._coeffs:
coeffs += '{0: <16}{1}{2}\n'.format(coeff, '=\t', self._coeffs[coeff])
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
string += coeffs
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
return string
"""
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
def create_xml_subelement(self):
element = ET.Element("surface")
@ -134,7 +174,7 @@ class Surface(object):
element.set("type", self._type)
element.set("boundary", self._boundary_type)
element.set("coeffs", ' '.join([str(self._coeffs[key])
element.set("coeffs", ' '.join([str(self._coeffs.setdefault(key, 0.0))
for key in self._coeff_keys]))
return element
@ -183,6 +223,10 @@ class Plane(Surface):
self._type = 'plane'
self._coeff_keys = ['A', 'B', 'C', 'D']
self._coeffs['A'] = 1.
self._coeffs['B'] = 0.
self._coeffs['C'] = 0.
self._coeffs['D'] = 0.
if A is not None:
self.a = A
@ -265,19 +309,51 @@ class XPlane(Plane):
self._type = 'x-plane'
self._coeff_keys = ['x0']
self._coeffs['x0'] = 0.
if x0 is not None:
self.x0 = x0
@property
def x0(self):
return self.coeff['x0']
return self.coeffs['x0']
@x0.setter
def x0(self, x0):
check_type('x0 coefficient', x0, Real)
self._coeffs['x0'] = x0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the x-plane surface, the
half-spaces are unbounded in their y- and z- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([self.x0, np.inf, np.inf]))
elif side == '+':
return (np.array([self.x0, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class YPlane(Plane):
"""A plane perpendicular to the y axis, i.e. a surface of the form :math:`y -
@ -311,6 +387,7 @@ class YPlane(Plane):
self._type = 'y-plane'
self._coeff_keys = ['y0']
self._coeffs['y0'] = 0.
if y0 is not None:
self.y0 = y0
@ -324,6 +401,37 @@ class YPlane(Plane):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the y-plane surface, the
half-spaces are unbounded in their x- and z- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, self.y0, np.inf]))
elif side == '+':
return (np.array([-np.inf, self.y0, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class ZPlane(Plane):
"""A plane perpendicular to the z axis, i.e. a surface of the form :math:`z -
@ -357,6 +465,7 @@ class ZPlane(Plane):
self._type = 'z-plane'
self._coeff_keys = ['z0']
self._coeffs['z0'] = 0.
if z0 is not None:
self.z0 = z0
@ -370,6 +479,37 @@ class ZPlane(Plane):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the z-plane surface, the
half-spaces are unbounded in their x- and y- directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, self.z0]))
elif side == '+':
return (np.array([-np.inf, -np.inf, self.z0]),
np.array([np.inf, np.inf, np.inf]))
class Cylinder(Surface):
"""A cylinder whose length is parallel to the x-, y-, or z-axis.
@ -404,6 +544,7 @@ class Cylinder(Surface):
super(Cylinder, self).__init__(surface_id, boundary_type, name=name)
self._coeff_keys = ['R']
self._coeffs['R'] = 1.
if R is not None:
self.r = R
@ -457,6 +598,8 @@ class XCylinder(Cylinder):
self._type = 'x-cylinder'
self._coeff_keys = ['y0', 'z0', 'R']
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
if y0 is not None:
self.y0 = y0
@ -482,6 +625,38 @@ class XCylinder(Cylinder):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the x-cylinder surface,
the negative half-space is unbounded in the x- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([-np.inf, self.y0 - self.r, self.z0 - self.r]),
np.array([np.inf, self.y0 + self.r, self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class YCylinder(Cylinder):
"""An infinite cylinder whose length is parallel to the y-axis. This is a
@ -522,6 +697,8 @@ class YCylinder(Cylinder):
self._type = 'y-cylinder'
self._coeff_keys = ['x0', 'z0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['z0'] = 0.
if x0 is not None:
self.x0 = x0
@ -547,6 +724,38 @@ class YCylinder(Cylinder):
check_type('z0 coefficient', z0, Real)
self._coeffs['z0'] = z0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the y-cylinder surface,
the negative half-space is unbounded in the y- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, -np.inf, self.z0 - self.r]),
np.array([self.x0 + self.r, np.inf, self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class ZCylinder(Cylinder):
"""An infinite cylinder whose length is parallel to the z-axis. This is a
@ -587,6 +796,8 @@ class ZCylinder(Cylinder):
self._type = 'z-cylinder'
self._coeff_keys = ['x0', 'y0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
if x0 is not None:
self.x0 = x0
@ -612,6 +823,38 @@ class ZCylinder(Cylinder):
check_type('y0 coefficient', y0, Real)
self._coeffs['y0'] = y0
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. For the z-cylinder surface,
the negative half-space is unbounded in the z- direction and the
positive half-space is unbounded in all directions. To represent
infinity, numpy.inf is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, self.y0 - self.r, -np.inf]),
np.array([self.x0 + self.r, self.y0 + self.r, np.inf]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class Sphere(Surface):
"""A sphere of the form :math:`(x - x_0)^2 + (y - y_0)^2 + (z - z_0)^2 = R^2`.
@ -656,6 +899,10 @@ class Sphere(Surface):
self._type = 'sphere'
self._coeff_keys = ['x0', 'y0', 'z0', 'R']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
self._coeffs['R'] = 1.
if x0 is not None:
self.x0 = x0
@ -705,6 +952,39 @@ class Sphere(Surface):
check_type('R coefficient', R, Real)
self._coeffs['R'] = R
def bounding_box(self, side):
"""Determine an axis-aligned bounding box.
An axis-aligned bounding box for surface half-spaces is represented by
its lower-left and upper-right coordinates. The positive half-space of a
sphere is unbounded in all directions. To represent infinity, numpy.inf
is used.
Parameters
----------
side : {'+', '-'}
Indicates the negative or positive half-space
Returns
-------
numpy.array
Lower-left coordinates of the axis-aligned bounding box for the
desired half-space
numpy.array
Upper-right coordinates of the axis-aligned bounding box for the
desired half-space
"""
if side == '-':
return (np.array([self.x0 - self.r, self.y0 - self.r,
self.z0 - self.r]),
np.array([self.x0 + self.r, self.y0 + self.r,
self.z0 + self.r]))
elif side == '+':
return (np.array([-np.inf, -np.inf, -np.inf]),
np.array([np.inf, np.inf, np.inf]))
class Cone(Surface):
"""A conical surface parallel to the x-, y-, or z-axis.
@ -750,6 +1030,10 @@ class Cone(Surface):
super(Cone, self).__init__(surface_id, boundary_type, name=name)
self._coeff_keys = ['x0', 'y0', 'z0', 'R2']
self._coeffs['x0'] = 0.
self._coeffs['y0'] = 0.
self._coeffs['z0'] = 0.
self._coeffs['R2'] = 1.
if x0 is not None:
self.x0 = x0
@ -936,3 +1220,222 @@ class ZCone(Cone):
R2, name=name)
self._type = 'z-cone'
class Quadric(Surface):
"""A sphere of the form :math:`Ax^2 + By^2 + Cz^2 + Dxy + Eyz + Fxz + Gx + Hy +
Jz + K`.
Parameters
----------
surface_id : int
Unique identifier for the surface. If not specified, an identifier will
automatically be assigned.
boundary_type : {'transmission, 'vacuum', 'reflective', 'periodic'}
Boundary condition that defines the behavior for particles hitting the
surface. Defaults to transmissive boundary condition where particles
freely pass through the surface.
a, b, c, d, e, f, g, h, j, k : float
coefficients for the surface
name : str
Name of the sphere. If not specified, the name will be the empty string.
Attributes
----------
a, b, c, d, e, f, g, h, j, k : float
coefficients for the surface
"""
def __init__(self, surface_id=None, boundary_type='transmission',
a=None, b=None, c=None, d=None, e=None, f=None, g=None,
h=None, j=None, k=None, name=''):
# Initialize Quadric class attributes
super(Quadric, self).__init__(surface_id, boundary_type, name=name)
self._type = 'quadric'
self._coeff_keys = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k']
for key in self._coeff_keys:
self._coeffs[key] = 0.
if a is not None:
self.a = a
if b is not None:
self.b = b
if c is not None:
self.c = c
if d is not None:
self.d = d
if e is not None:
self.e = e
if f is not None:
self.f = f
if g is not None:
self.g = g
if h is not None:
self.h = h
if j is not None:
self.j = j
if k is not None:
self.k = k
@property
def a(self):
return self.coeffs['a']
@property
def b(self):
return self.coeffs['b']
@property
def c(self):
return self.coeffs['c']
@property
def d(self):
return self.coeffs['d']
@property
def e(self):
return self.coeffs['e']
@property
def f(self):
return self.coeffs['f']
@property
def g(self):
return self.coeffs['g']
@property
def h(self):
return self.coeffs['h']
@property
def j(self):
return self.coeffs['j']
@property
def k(self):
return self.coeffs['k']
@a.setter
def a(self, a):
check_type('a coefficient', a, Real)
self._coeffs['a'] = a
@b.setter
def b(self, b):
check_type('b coefficient', b, Real)
self._coeffs['b'] = b
@c.setter
def c(self, c):
check_type('c coefficient', c, Real)
self._coeffs['c'] = c
@d.setter
def d(self, d):
check_type('d coefficient', d, Real)
self._coeffs['d'] = d
@e.setter
def e(self, e):
check_type('e coefficient', e, Real)
self._coeffs['e'] = e
@f.setter
def f(self, f):
check_type('f coefficient', f, Real)
self._coeffs['f'] = f
@g.setter
def g(self, g):
check_type('g coefficient', g, Real)
self._coeffs['g'] = g
@h.setter
def h(self, h):
check_type('h coefficient', h, Real)
self._coeffs['h'] = h
@j.setter
def j(self, j):
check_type('j coefficient', j, Real)
self._coeffs['j'] = j
@k.setter
def k(self, k):
check_type('k coefficient', k, Real)
self._coeffs['k'] = k
class Halfspace(Region):
"""A positive or negative half-space region.
A half-space is either of the two parts into which a two-dimension surface
divides the three-dimensional Euclidean space. If the equation of the
surface is :math:`f(x,y,z) = 0`, the region for which :math:`f(x,y,z) < 0`
is referred to as the negative half-space and the region for which
:math:`f(x,y,z) > 0` is referred to as the positive half-space.
Instances of Halfspace are generally not instantiated directly. Rather, they
can be created from an existing Surface through the __neg__ and __pos__
operators, as the following example demonstrates:
>>> sphere = openmc.surface.Sphere(surface_id=1, R=10.0)
>>> inside_sphere = -sphere
>>> outside_sphere = +sphere
>>> type(inside_sphere)
<class 'openmc.surface.Halfspace'>
Parameters
----------
surface : Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
Attributes
----------
surface : Surface
Surface which divides Euclidean space.
side : {'+', '-'}
Indicates whether the positive or negative half-space is used.
bounding_box : tuple of numpy.array
Lower-left and upper-right coordinates of an axis-aligned bounding box
"""
def __init__(self, surface, side):
self.surface = surface
self.side = side
def __invert__(self):
return -self.surface if self.side == '+' else +self.surface
@property
def surface(self):
return self._surface
@surface.setter
def surface(self, surface):
check_type('surface', surface, Surface)
self._surface = surface
@property
def side(self):
return self._side
@side.setter
def side(self, side):
check_value('side', side, ('+', '-'))
self._side = side
@property
def bounding_box(self):
return self.surface.bounding_box(self.side)
def __str__(self):
return '-' + str(self.surface.id) if self.side == '-' \
else str(self.surface.id)

File diff suppressed because it is too large Load diff

View file

@ -1,12 +0,0 @@
from checkvalue import *
from checkvalue import _isinstance
import numpy as np
zs = np.zeros((2,))
print _isinstance(zs[0], Integral)
print _isinstance(zs[0], Real)
print _isinstance(zs[0], (Integral, Real))
print check_iterable_type('thing', zs, (Real, Integral))

View file

@ -58,6 +58,22 @@ class Trigger(object):
else:
return existing
def __eq__(self, other):
if str(self) == str(other):
return True
else:
return False
def __ne__(self, other):
return not self == other
def __repr__(self):
string = 'Trigger\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._trigger_type)
string += '{0: <16}{1}{2}\n'.format('\tThreshold', '=\t', self._threshold)
string += '{0: <16}{1}{2}\n'.format('\tScores', '=\t', self._scores)
return string
@property
def trigger_type(self):
return self._trigger_type
@ -102,13 +118,6 @@ class Trigger(object):
else:
self._scores.append(score)
def __repr__(self):
string = 'Trigger\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._trigger_type)
string += '{0: <16}{1}{2}\n'.format('\tThreshold', '=\t', self._threshold)
string += '{0: <16}{1}{2}\n'.format('\tScores', '=\t', self._scores)
return string
def get_trigger_xml(self, element):
"""Return XML representation of the trigger

View file

@ -3,15 +3,22 @@ from collections import OrderedDict, Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import warnings
import numpy as np
import openmc
import openmc.checkvalue as cv
from openmc.surface import Halfspace
from openmc.region import Region, Intersection, Complement
if sys.version_info[0] >= 3:
basestring = str
# DeprecationWarning filter for the Cell.add_surface(...) method
warnings.simplefilter('always', DeprecationWarning)
# A static variable for auto-generated Cell IDs
AUTO_CELL_ID = 10000
@ -45,10 +52,8 @@ class Cell(object):
Name of the cell
fill : Material or Universe or Lattice or 'void'
Indicates what the region of space is filled with
surfaces : dict
Dictionary whose keys are surface IDs and values are 2-tuples of a
Surface object and an integer identify whether the positive or negative
half-space is to be used
region : openmc.region.Region
Region of space that is assigned to the cell.
rotation : ndarray
If the cell is filled with a universe, this array specifies the angles
in degrees about the x, y, and z axes that the filled universe should be
@ -67,11 +72,59 @@ class Cell(object):
self.name = name
self._fill = None
self._type = None
self._surfaces = {}
self._region = None
self._rotation = None
self._translation = None
self._offsets = None
def __eq__(self, other):
if not isinstance(other, Cell):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
elif self.fill != other.fill:
return False
elif self.region != other.region:
return False
elif self.rotation != other.rotation:
return False
elif self.translation != other.translation:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'Cell\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
if isinstance(self._fill, openmc.Material):
string += '{0: <16}{1}{2}\n'.format('\tMaterial', '=\t',
self._fill._id)
elif isinstance(self._fill, (Universe, Lattice)):
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t',
self._fill._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', self._fill)
string += '{0: <16}{1}{2}\n'.format('\tRegion', '=\t', self._region)
string += '{0: <16}{1}{2}\n'.format('\tRotation', '=\t',
self._rotation)
string += '{0: <16}{1}{2}\n'.format('\tTranslation', '=\t',
self._translation)
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offsets)
return string
@property
def id(self):
return self._id
@ -85,12 +138,19 @@ class Cell(object):
return self._fill
@property
def type(self):
return self._fill
def fill_type(self):
if isinstance(self.fill, openmc.Material):
return 'material'
elif isinstance(self.fill, openmc.Universe):
return 'universe'
elif isinstance(self.fill, openmc.Lattice):
return 'lattice'
else:
return None
@property
def surfaces(self):
return self._surfaces
def region(self):
return self._region
@property
def rotation(self):
@ -112,13 +172,16 @@ class Cell(object):
AUTO_CELL_ID += 1
else:
cv.check_type('cell ID', cell_id, Integral)
cv.check_greater_than('cell ID', cell_id, 0)
cv.check_greater_than('cell ID', cell_id, 0, equality=True)
self._id = cell_id
@name.setter
def name(self, name):
cv.check_type('cell name', name, basestring)
self._name = name
if name is not None:
cv.check_type('cell name', name, basestring)
self._name = name
else:
self._name = ''
@fill.setter
def fill(self, fill):
@ -163,6 +226,11 @@ class Cell(object):
cv.check_type('cell offsets', offsets, Iterable)
self._offsets = offsets
@region.setter
def region(self, region):
cv.check_type('cell region', region, Region)
self._region = region
def add_surface(self, surface, halfspace):
"""Add a half-space to the list of half-spaces whose intersection defines the
cell.
@ -176,6 +244,11 @@ class Cell(object):
"""
warnings.warn("Cell.add_surface(...) has been deprecated and may be "
"removed in a future version. The region for a Cell "
"should be defined using the region property directly.",
DeprecationWarning)
if not isinstance(surface, openmc.Surface):
msg = 'Unable to add Surface "{0}" to Cell ID="{1}" since it is ' \
'not a Surface object'.format(surface, self._id)
@ -186,28 +259,17 @@ class Cell(object):
'"{2}" since it is not +/-1'.format(surface, self._id, halfspace)
raise ValueError(msg)
# If the Cell does not already contain the Surface, add it
if surface._id not in self._surfaces:
self._surfaces[surface._id] = (surface, halfspace)
def remove_surface(self, surface):
"""Remove the half-space associated with a particular surface.
Parameters
----------
surface : openmc.surface.Surface
Surface to remove from definition
"""
if not isinstance(surface, openmc.Surface):
msg = 'Unable to remove Surface "{0}" from Cell ID="{1}" since it is ' \
'not a Surface object'.format(surface, self._id)
raise ValueError(msg)
# If the Cell contains the Surface, delete it
if surface._id in self._surfaces:
del self._surfaces[surface._id]
# If no region has been assigned, simply use the half-space. Otherwise,
# take the intersection of the current region and the half-space
# specified
region = +surface if halfspace == 1 else -surface
if self.region is None:
self.region = region
else:
if isinstance(self.region, Intersection):
self.region.nodes.append(region)
else:
self.region = Intersection(self.region, region)
def get_offset(self, path, filter_offset):
# Get the current element and remove it from the list
@ -240,7 +302,7 @@ class Cell(object):
"""
nuclides = {}
nuclides = OrderedDict()
if self._type != 'void':
nuclides.update(self._fill.get_all_nuclides())
@ -258,13 +320,34 @@ class Cell(object):
"""
cells = {}
cells = OrderedDict()
if self._type == 'fill' or self._type == 'lattice':
cells.update(self._fill.get_all_cells())
return cells
def get_all_materials(self):
"""Return all materials that are contained within the cell
Returns
-------
materials : dict
Dictionary whose keys are material IDs and values are Material instances
"""
materials = OrderedDict()
if self.fill_type == 'material':
materials[self.fill.id] = self.fill
# Append all Cells in each Cell in the Universe to the dictionary
cells = self.get_all_cells()
for cell_id, cell in cells.items():
materials.update(cell.get_all_materials())
return materials
def get_all_universes(self):
"""Return all universes that are contained within this one if any of
its cells are filled with a universe or lattice.
@ -277,7 +360,7 @@ class Cell(object):
"""
universes = {}
universes = OrderedDict()
if self._type == 'fill':
universes[self._fill._id] = self._fill
@ -287,36 +370,6 @@ class Cell(object):
return universes
def __repr__(self):
string = 'Cell\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
if isinstance(self._fill, openmc.Material):
string += '{0: <16}{1}{2}\n'.format('\tMaterial', '=\t',
self._fill._id)
elif isinstance(self._fill, (Universe, Lattice)):
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t',
self._fill._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tFill', '=\t', self._fill)
string += '{0: <16}{1}\n'.format('\tSurfaces', '=\t')
for surface_id in self._surfaces:
halfspace = self._surfaces[surface_id][1]
string += '{0} '.format(halfspace * surface_id)
string = string.rstrip(' ') + '\n'
string += '{0: <16}{1}{2}\n'.format('\tRotation', '=\t',
self._rotation)
string += '{0: <16}{1}{2}\n'.format('\tTranslation', '=\t',
self._translation)
string += '{0: <16}{1}{2}\n'.format('\tOffset', '=\t', self._offsets)
return string
def create_xml_subelement(self, xml_element):
element = ET.Element("cell")
element.set("id", str(self._id))
@ -338,26 +391,30 @@ class Cell(object):
element.set("fill", str(self._fill))
self._fill.create_xml_subelement(xml_element)
if self._surfaces is not None:
surfaces = ''
if self.region is not None:
# Set the region attribute with the region specification
element.set("region", str(self.region))
for surface_id in self._surfaces:
# Determine if XML element already includes this Surface
path = './surface[@id=\'{0}\']'.format(surface_id)
test = xml_element.find(path)
# Only surfaces that appear in a region are added to the geometry
# file, so the appropriate check is performed here. First we create
# a function which is called recursively to navigate through the CSG
# tree. When it reaches a leaf (a Halfspace), it creates a <surface>
# element for the corresponding surface if none has been created
# thus far.
def create_surface_elements(node, element):
if isinstance(node, Halfspace):
path = './surface[@id=\'{0}\']'.format(node.surface.id)
if xml_element.find(path) is None:
surface_subelement = node.surface.create_xml_subelement()
xml_element.append(surface_subelement)
elif isinstance(node, Complement):
create_surface_elements(node.node, element)
else:
for subnode in node.nodes:
create_surface_elements(subnode, element)
# If the element does not contain the Surface subelement
if test is None:
# Create the XML subelement for this Surface
surface = self._surfaces[surface_id][0]
surface_subelement = surface.create_xml_subelement()
xml_element.append(surface_subelement)
# Append the halfspace and Surface ID
halfspace = self._surfaces[surface_id][1]
surfaces += '{0} '.format(halfspace * surface_id)
element.set("surfaces", surfaces.rstrip(' '))
# Call the recursive function from the top node
create_surface_elements(self.region, xml_element)
if self._translation is not None:
element.set("translation", ' '.join(map(str, self._translation)))
@ -406,13 +463,41 @@ class Universe(object):
# Keys - Cell IDs
# Values - Cells
self._cells = {}
self._cells = OrderedDict()
# Keys - Cell IDs
# Values - Offsets
self._cell_offsets = OrderedDict()
self._num_regions = 0
def __eq__(self, other):
if not isinstance(other, Universe):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
elif self.cells != other.cells:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'Universe\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tCells', '=\t',
list(self._cells.keys()))
string += '{0: <16}{1}{2}\n'.format('\t# Regions', '=\t',
self._num_regions)
return string
@property
def id(self):
return self._id
@ -433,13 +518,16 @@ class Universe(object):
AUTO_UNIVERSE_ID += 1
else:
cv.check_type('universe ID', universe_id, Integral)
cv.check_greater_than('universe ID', universe_id, 0, True)
cv.check_greater_than('universe ID', universe_id, 0, equality=True)
self._id = universe_id
@name.setter
def name(self, name):
cv.check_type('universe name', name, basestring)
self._name = name
if name is not None:
cv.check_type('universe name', name, basestring)
self._name = name
else:
self._name = ''
def add_cell(self, cell):
"""Add a cell to the universe.
@ -494,11 +582,9 @@ class Universe(object):
'not a Cell'.format(self._id, cell)
raise ValueError(msg)
cell_id = cell.getId()
# If the Cell is in the Universe's list of Cells, delete it
if cell_id in self._cells:
del self._cells[cell_id]
if cell.id in self._cells:
del self._cells[cell.id]
def clear_cells(self):
"""Remove all cells from the universe."""
@ -529,7 +615,7 @@ class Universe(object):
"""
nuclides = {}
nuclides = OrderedDict()
# Append all Nuclides in each Cell in the Universe to the dictionary
for cell_id, cell in self._cells.items():
@ -547,7 +633,7 @@ class Universe(object):
"""
cells = {}
cells = OrderedDict()
# Add this Universe's cells to the dictionary
cells.update(self._cells)
@ -558,6 +644,25 @@ class Universe(object):
return cells
def get_all_materials(self):
"""Return all materials that are contained within the universe
Returns
-------
materials : dict
Dictionary whose keys are material IDs and values are Material instances
"""
materials = OrderedDict()
# Append all Cells in each Cell in the Universe to the dictionary
cells = self.get_all_cells()
for cell_id, cell in cells.items():
materials.update(cell.get_all_materials())
return materials
def get_all_universes(self):
"""Return all universes that are contained within this one.
@ -572,7 +677,7 @@ class Universe(object):
# Get all Cells in this Universe
cells = self.get_all_cells()
universes = {}
universes = OrderedDict()
# Append all Universes containing each Cell to the dictionary
for cell_id, cell in cells.items():
@ -580,17 +685,8 @@ class Universe(object):
return universes
def __repr__(self):
string = 'Universe\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tCells', '=\t',
list(self._cells.keys()))
string += '{0: <16}{1}{2}\n'.format('\t# Regions', '=\t',
self._num_regions)
return string
def create_xml_subelement(self, xml_element):
# Iterate over all Cells
for cell_id, cell in self._cells.items():
@ -644,6 +740,25 @@ class Lattice(object):
self._outer = None
self._universes = None
def __eq__(self, other):
if not isinstance(other, Lattice):
return False
elif self.id != other.id:
return False
elif self.name != other.name:
return False
elif self.pitch != other.pitch:
return False
elif self.outer != other.outer:
return False
elif self.universes != other.universes:
return False
else:
return True
def __ne__(self, other):
return not self == other
@property
def id(self):
return self._id
@ -672,13 +787,16 @@ class Lattice(object):
AUTO_UNIVERSE_ID += 1
else:
cv.check_type('lattice ID', lattice_id, Integral)
cv.check_greater_than('lattice ID', lattice_id, 0)
cv.check_greater_than('lattice ID', lattice_id, 0, equality=True)
self._id = lattice_id
@name.setter
def name(self, name):
cv.check_type('lattice name', name, basestring)
self._name = name
if name is not None:
cv.check_type('lattice name', name, basestring)
self._name = name
else:
self._name = ''
@outer.setter
def outer(self, outer):
@ -702,7 +820,7 @@ class Lattice(object):
"""
univs = dict()
univs = OrderedDict()
for k in range(len(self._universes)):
for j in range(len(self._universes[k])):
if isinstance(self._universes[k][j], Universe):
@ -730,7 +848,7 @@ class Lattice(object):
"""
nuclides = {}
nuclides = OrderedDict()
# Get all unique Universes contained in each of the lattice cells
unique_universes = self.get_unique_universes()
@ -751,7 +869,7 @@ class Lattice(object):
"""
cells = {}
cells = OrderedDict()
unique_universes = self.get_unique_universes()
for universe_id, universe in unique_universes.items():
@ -759,6 +877,25 @@ class Lattice(object):
return cells
def get_all_materials(self):
"""Return all materials that are contained within the lattice
Returns
-------
materials : dict
Dictionary whose keys are material IDs and values are Material instances
"""
materials = OrderedDict()
# Append all Cells in each Cell in the Universe to the dictionary
cells = self.get_all_cells()
for cell_id, cell in cells.items():
materials.update(cell.get_all_materials())
return materials
def get_all_universes(self):
"""Return all universes that are contained within the lattice
@ -772,7 +909,7 @@ class Lattice(object):
# Initialize a dictionary of all Universes contained by the Lattice
# in each nested Universe level
all_universes = {}
all_universes = OrderedDict()
# Get all unique Universes contained in each of the lattice cells
unique_universes = self.get_unique_universes()
@ -821,6 +958,68 @@ class RectLattice(Lattice):
self._lower_left = None
self._offsets = None
def __eq__(self, other):
if not isinstance(other, RectLattice):
return False
elif not super(RectLattice, self).__eq__(other):
return False
elif self.dimension != other.dimension:
return False
elif self.lower_left != other.lower_left:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'RectLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tDimension', '=\t',
self._dimension)
string += '{0: <16}{1}{2}\n'.format('\tLower Left', '=\t',
self._lower_left)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
# Lattice nested Universe IDs - column major for Fortran
for i, universe in enumerate(np.ravel(self._universes)):
string += '{0} '.format(universe._id)
# Add a newline character every time we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
if self._offsets is not None:
string += '{0: <16}\n'.format('\tOffsets')
# Lattice cell offsets
for i, offset in enumerate(np.ravel(self._offsets)):
string += '{0} '.format(offset)
# Add a newline character when we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
return string
@property
def dimension(self):
return self._dimension
@ -878,51 +1077,8 @@ class RectLattice(Lattice):
return offset
def __repr__(self):
string = 'RectLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\tDimension', '=\t',
self._dimension)
string += '{0: <16}{1}{2}\n'.format('\tLower Left', '=\t',
self._lower_left)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
# Lattice nested Universe IDs - column major for Fortran
for i, universe in enumerate(np.ravel(self._universes)):
string += '{0} '.format(universe._id)
# Add a newline character every time we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
if self._offsets is not None:
string += '{0: <16}\n'.format('\tOffsets')
# Lattice cell offsets
for i, offset in enumerate(np.ravel(self._offsets)):
string += '{0} '.format(offset)
# Add a newline character when we reach end of row of cells
if (i+1) % self._dimension[-1] == 0:
string += '\n'
string = string.rstrip('\n')
return string
def create_xml_subelement(self, xml_element):
# Determine if XML element already contains subelement for this Lattice
path = './lattice[@id=\'{0}\']'.format(self._id)
test = xml_element.find(path)
@ -1037,6 +1193,54 @@ class HexLattice(Lattice):
self._num_axial = None
self._center = None
def __eq__(self, other):
if not isinstance(other, HexLattice):
return False
elif not super(HexLattice, self).__eq__(other):
return False
elif self.num_rings != other.num_rings:
return False
elif self.num_axial != other.num_axial:
return False
elif self.center != other.center:
return False
else:
return True
def __ne__(self, other):
return not self == other
def __hash__(self):
return hash(repr(self))
def __repr__(self):
string = 'HexLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings)
string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial)
string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t',
self._center)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
if self._num_axial is not None:
slices = [self._repr_axial_slice(x) for x in self._universes]
string += '\n'.join(slices)
else:
string += self._repr_axial_slice(self._universes)
return string
@property
def num_rings(self):
return self._num_rings
@ -1157,34 +1361,6 @@ class HexLattice(Lattice):
6*(self._num_rings - 1 - r))
raise ValueError(msg)
def __repr__(self):
string = 'HexLattice\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings)
string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial)
string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t',
self._center)
string += '{0: <16}{1}{2}\n'.format('\tPitch', '=\t', self._pitch)
if self._outer is not None:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer._id)
else:
string += '{0: <16}{1}{2}\n'.format('\tOuter', '=\t',
self._outer)
string += '{0: <16}\n'.format('\tUniverses')
if self._num_axial is not None:
slices = [self._repr_axial_slice(x) for x in self._universes]
string += '\n'.join(slices)
else:
string += self._repr_axial_slice(self._universes)
return string
def create_xml_subelement(self, xml_element):
# Determine if XML element already contains subelement for this Lattice
path = './hex_lattice[@id=\'{0}\']'.format(self._id)

View file

@ -33,7 +33,7 @@ class MeshPlotter(tk.Frame):
self.labels = {'cell': 'Cell:', 'cellborn': 'Cell born:',
'surface': 'Surface:', 'material': 'Material:',
'universe': 'Universe:', 'energyin': 'Energy in:',
'universe': 'Universe:', 'energy': 'Energy in:',
'energyout': 'Energy out:'}
self.filterBoxes = {}
@ -133,7 +133,11 @@ class MeshPlotter(tk.Frame):
self.mesh = selectedTally.filters_by_name['mesh'].mesh
# Get mesh dimensions
self.nx, self.ny, self.nz = self.mesh.dimension
if len(self.mesh.dimension) == 2:
self.nx, self.ny = self.mesh.dimension
self.nz = 1
else:
self.nx, self.ny, self.nz = self.mesh.dimension
# Repopulate comboboxes baesd on current basis selection
text = self.basisBox.get()
@ -176,9 +180,9 @@ class MeshPlotter(tk.Frame):
self.filterBoxes[filterType] = combobox
# Set combobox items
if filterType in ['energyin', 'energyout']:
if filterType in ['energy', 'energyout']:
combobox['values'] = ['{0} to {1}'.format(*f.bins[i:i+2])
for i in range(f.length)]
for i in range(len(f.bins) - 1)]
else:
combobox['values'] = [str(i) for i in f.bins]
@ -209,8 +213,13 @@ class MeshPlotter(tk.Frame):
if f.type == 'mesh':
mesh_filter = f
continue
index = self.filterBoxes[f.type].current()
spec_list.append((f, index))
elif f.type in ['energy', 'energyout']:
index = self.filterBoxes[f.type].current()
ebin = (f.bins[index], f.bins[index + 1])
spec_list.append((f.type, (ebin,)))
else:
index = self.filterBoxes[f.type].current()
spec_list.append((f.type, (index,)))
text = self.basisBox.get()
if text == 'xy':
@ -229,11 +238,11 @@ class MeshPlotter(tk.Frame):
else:
meshtuple = (i + 1, axial_level, j + 1)
filters, filter_bins = zip(*spec_list + [
(mesh_filter, meshtuple)])
mean = selectedTally.get_value(
self.scoreBox.get(), filters, filter_bins)
stdev = selectedTally.get_value(
self.scoreBox.get(), filters, filter_bins,
(mesh_filter.type, (meshtuple,))])
mean = selectedTally.get_values(
[self.scoreBox.get()], filters, filter_bins)
stdev = selectedTally.get_values(
[self.scoreBox.get()], filters, filter_bins,
value='std_dev')
if mbvalue == 'Mean':
matrix[i, j] = mean
@ -264,8 +273,6 @@ class MeshPlotter(tk.Frame):
def get_file_data(self, filename):
# Create StatePoint object and read in data
self.datafile = StatePoint(filename)
self.datafile.read_results()
self.datafile.compute_stdev()
# Find which tallies are mesh tallies
self.meshTallies = []

View file

@ -219,7 +219,7 @@ def main(file_, o):
for y in range(1,ny+1):
for z in range(1,nz+1):
filterspec[0][1] = (x,y,z)
val = sp.get_value(tally.id-1, filterspec, sid)[o.valerr]
val = sp.get_values(tally.id-1, filterspec, sid)[o.valerr]
if o.vtk:
# vtk cells go z, y, x, so we store it now and enter it later
i = (z-1)*nx*ny + (y-1)*nx + x-1

View file

@ -1,43 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
from sys import argv
from math import sqrt
import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
from openmc.statepoint import StatePoint
# Get filename
filename = argv[1]
# Create StatePoint object
sp = StatePoint(filename)
sp.read_results()
sp.compute_ci()
# Check if tallies are present
if not sp.tallies_present:
raise Exception("No tally data in state point!")
# Loop over all tallies
for i, t in sp.tallies.items():
# Determine relative error and fraction of bins with less than 1% half-width
# of CI
n_bins = t.mean.size
relative_error = t.std_dev[t.mean > 0.] / t.mean[t.mean > 0.]
fraction = float(sum(relative_error < 0.01))/n_bins
# Display results
print("Tally " + str(i))
print(" Fraction under 1% = {0}".format(fraction))
print(" Min relative error = {0}".format(min(relative_error)))
print(" Max relative error = {0}".format(max(relative_error)))
print(" Non-scoring bins = {0}".format(
1.0 - float(relative_error.size)/n_bins))
# Plot histogram
plt.hist(relative_error, 100)
plt.show()

View file

@ -1,375 +0,0 @@
#!/usr/bin/env python
# This program takes OpenMC statepoint binary files and creates a variety of
# outputs from them which should provide the user with an idea of the
# convergence behavior of all the tallies and filters defined by the user in
# tallies.xml. The program can directly plot the value and errors of each
# tally, filter, score combination; it can save these plots to a file; and
# it can also save the data used in these plots to a CSV file for importing in
# to other plotting packages such as Excel, gnuplot, MathGL, or Veusz.
# To use the program, run this program from the working directory of the openMC
# problem to analyze.
# The USER OPTIONS block below provides four options for the user to set:
# fileType, printxs, showImg, and savetoCSV. See the options block for more
# information.
from __future__ import print_function
from math import sqrt, pow
from glob import glob
import numpy as np
import scipy.stats
import matplotlib.pyplot as plt
from openmc.statepoint import StatePoint
##################################### USER OPTIONS
# Set filetype (the file extension desired, without the period.)
# Options are backend dependent, but most backends support png, pdf, ps, eps
# and svg. Write "none" if no saved files are desired.
fileType = "none"
# Set if cross-sections or reaction rates are desired printxs = True means X/S
printxs = False
# Set if the figures should be displayed to screen or not (True means show)
showImg = False
# Save to CSV for use in more advanced plotting programs like GNUPlot, MathGL
savetoCSV = True
##################################### END USER OPTIONS
## Find if tallies.xml exists.
#if glob('./tallies.xml') != None:
# # It exists
# tallyData = talliesXML('tallies.xml')
#else:
# # It does not exist.
# tallyData = None
# Find all statepoints in this directory.
files = glob('./statepoint.*.binary')
fileNums = []
begin = 13
# Arrange the file list in increasing batch order
for i in range(len(files)):
end = files[i].find(".binary")
fileNums.append(int(files[i][begin:end]))
fileNums.sort()
# Re-make filenames
files = []
for i in range(len(fileNums)):
files.append("./statepoint." + str(fileNums[i]) + ".binary")
# Initialize arrays as needed
mean = [None for x in range(len(files))]
uncert = [None for x in range(len(files))]
scoreType = [None for x in range(len(files))]
active_batches = [None for x in range(len(files))]
for i_batch in range(len(files)):
# Get filename
batch_filename = files[i_batch]
# Create StatePoint object
sp = StatePoint(batch_filename)
# Read number of realizations for global tallies
sp.n_realizations = sp._get_int()[0]
# Read global tallies
n_global_tallies = sp._get_int()[0]
sp.global_tallies = np.array(sp._get_double(2*n_global_tallies))
sp.global_tallies.shape = (n_global_tallies, 2)
# Flag indicating if tallies are present
tallies_present = sp._get_int()[0]
# Check if tallies are present
if not tallies_present:
raise Exception("No tally data in state point!")
# Increase the dimensionality of our main variables
mean[i_batch] = [None for x in range(len(sp.tallies))]
uncert[i_batch] = [None for x in range(len(sp.tallies))]
scoreType[i_batch] = [None for x in range(len(sp.tallies))]
# Loop over all tallies
for i_tally, t in enumerate(sp.tallies):
# Calculate t-value for 95% two-sided CI
n = t.n_realizations
t_value = scipy.stats.t.ppf(0.975, n - 1)
# Store the batch count
active_batches[i_batch] = n
# Resize the 2nd dimension
mean[i_batch][i_tally] = [None for x in range(t.total_filter_bins)]
uncert[i_batch][i_tally] = [None for x in range(t.total_filter_bins)]
scoreType[i_batch][i_tally] = [None for x in range(t.total_filter_bins)]
for i_filter in range(t.total_filter_bins):
# Resize the 3rd dimension
mean[i_batch][i_tally][i_filter] = [None for x in range(t.n_nuclides)]
uncert[i_batch][i_tally][i_filter] = [None for x in range(t.n_nuclides)]
scoreType[i_batch][i_tally][i_filter] = [None for x in range(t.n_nuclides)]
print(t.total_filter_bins,t.n_nuclides)
for i_nuclide in range(t.n_nuclides):
mean[i_batch][i_tally][i_filter][i_nuclide] = \
[None for x in range(t.n_scores)]
uncert[i_batch][i_tally][i_filter][i_nuclide] = \
[None for x in range(t.n_scores)]
scoreType[i_batch][i_tally][i_filter][i_nuclide] = \
[None for x in range(t.n_scores)]
for i_score in range(t.n_scores):
scoreType[i_batch][i_tally][i_filter][i_nuclide][i_score] = \
t.scores[i_score]
s, s2 = sp._get_double(2)
s /= n
mean[i_batch][i_tally][i_filter][i_nuclide][i_score] = s
if s != 0.0:
relative_error = t_value*sqrt((s2/n - s*s)/(n-1))/s
else:
relative_error = 0.0
uncert[i_batch][i_tally][i_filter][i_nuclide][i_score] = relative_error
# Reorder the data lists in to a list order more conducive for plotting:
# The indexing should be: [tally][filter][score][batch]
meanPlot = [None for x in range(len(mean[0]))] # Set to the number of tallies
uncertPlot = [None for x in range(len(mean[0]))] # Set to the number of tallies
absUncertPlot = [None for x in range(len(mean[0]))] # Set to number of tallies
filterLabel = [None for x in range(len(mean[0]))] #Set to the number of tallies
fluxLoc = [None for x in range(len(mean[0]))] # Set to the number of tallies
printxs = [False for x in range(len(mean[0]))] # Set to the number of tallies
# Get and set the correct sizes for the rest of the dimensions
for i_tally in range(len(meanPlot)):
# Set 2nd (score) dimension
meanPlot[i_tally] = [None for x in range(len(mean[0][i_tally]))]
uncertPlot[i_tally] = [None for x in range(len(mean[0][i_tally]))]
absUncertPlot[i_tally] = [None for x in range(len(mean[0][i_tally]))]
filterLabel[i_tally] = [None for x in range(len(mean[0][i_tally]))]
# Initialize flux location so it will be -1 if not found
fluxLoc[i_tally] = -1
for i_filter in range(len(meanPlot[i_tally])):
# Set 3rd (filter) dimension
meanPlot[i_tally][i_filter] = \
[None for x in range(len(mean[0][i_tally][i_filter]))]
uncertPlot[i_tally][i_filter] = \
[None for x in range(len(mean[0][i_tally][i_filter]))]
absUncertPlot[i_tally][i_filter] = \
[None for x in range(len(mean[0][i_tally][i_filter]))]
filterLabel[i_tally][i_filter] = \
[None for x in range(len(mean[0][i_tally][i_filter]))]
for i_nuclide in range(len(meanPlot[i_tally][i_filter])):
# Set 4th (nuclide)) dimension
meanPlot[i_tally][i_filter][i_nuclide] = \
[None for x in range(len(mean[0][i_tally][i_filter][i_nuclide]))]
uncertPlot[i_tally][i_filter][i_nuclide] = \
[None for x in range(len(mean[0][i_tally][i_filter][i_nuclide]))]
absUncertPlot[i_tally][i_filter][i_nuclide] = \
[None for x in range(len(mean[0][i_tally][i_filter][i_nuclide]))]
for i_score in range(len(meanPlot[i_tally][i_filter][i_nuclide])):
# Set 5th (batch) dimension
meanPlot[i_tally][i_filter][i_nuclide][i_score] = \
[None for x in range(len(mean))]
uncertPlot[i_tally][i_filter][i_nuclide][i_score] = \
[None for x in range(len(mean))]
absUncertPlot[i_tally][i_filter][i_nuclide][i_score] = \
[None for x in range(len(mean))]
# Get filterLabel (this should be moved to its own function)
#??? How to do?
# Set flux location if found
# all batches and all tallies will have the same score ordering, hence
# the 0's in the 1st, 3rd, and 4th dimensions.
if scoreType[0][i_tally][0][0][i_score] == 'flux':
fluxLoc[i_tally] = i_score
# Set printxs array according to the printxs input
if printxs:
for i_tally in range(len(fluxLoc)):
if fluxLoc[i_tally] != -1:
printxs[i_tally] = True
# Now rearrange the data as suitable, and perform xs conversion if necessary
for i_batch in range(len(mean)):
for i_tally in range(len(mean[i_batch])):
for i_filter in range(len(mean[i_batch][i_tally])):
for i_nuclide in range(len(mean[i_batch][i_tally][i_filter])):
for i_score in range(len(mean[i_batch][i_tally][i_filter][i_nuclide])):
if (printxs[i_tally] and \
((scoreType[0][i_tally][i_filter][i_nuclide][i_score] != 'flux') and \
(scoreType[0][i_tally][i_filter][i_nuclide][i_score] != 'current'))):
# Perform rate to xs conversion
# mean is mean/fluxmean
meanPlot[i_tally][i_filter][i_nuclide][i_score][i_batch] = \
mean[i_batch][i_tally][i_filter][i_nuclide][i_score] / \
mean[i_batch][i_tally][i_filter][i_nuclide][fluxLoc[i_tally]]
# Update the relative uncertainty via error propagation
uncertPlot[i_tally][i_filter][i_nuclide][i_score][i_batch] = \
sqrt(pow(uncert[i_batch][i_tally][i_filter][i_nuclide][i_score],2) \
+ pow(uncert[i_batch][i_tally][i_filter][i_nuclide][fluxLoc[i_tally]],2))
else:
# Do not perform rate to xs conversion
meanPlot[i_tally][i_filter][i_nuclide][i_score][i_batch] = \
mean[i_batch][i_tally][i_filter][i_nuclide][i_score]
uncertPlot[i_tally][i_filter][i_nuclide][i_score][i_batch] = \
uncert[i_batch][i_tally][i_filter][i_nuclide][i_score]
# Both have the same absolute uncertainty calculation
absUncertPlot[i_tally][i_filter][i_nuclide][i_score][i_batch] = \
uncert[i_batch][i_tally][i_filter][i_nuclide][i_score] * \
mean[i_batch][i_tally][i_filter][i_nuclide][i_score]
# Set plotting constants
xLabel = "Batches"
xLabel = xLabel.title() # not necessary for now, but is left in to handle if
# the previous line changes
# Begin plotting
for i_tally in range(len(meanPlot)):
# Set tally string (placeholder until I put tally labels in statePoint)
tallyStr = "Tally " + str(i_tally + 1)
for i_filter in range(len(meanPlot[i_tally])):
# Set filter string
filterStr = "Filter " + str(i_filter + 1)
for i_nuclide in range(len(meanPlot[i_tally][i_filter])):
nuclideStr = "Nuclide " + str(i_nuclide + 1)
for i_score in range(len(meanPlot[i_tally][i_filter][i_nuclide])):
# Set score string
scoreStr = scoreType[i_batch][i_tally][i_filter][i_nuclide][i_score]
scoreStr = scoreStr.title()
if (printxs[i_tally] and ((scoreStr != 'Flux') and \
(scoreStr != 'Current'))):
scoreStr = scoreStr + "-XS"
# set Title
title = "Convergence of " + scoreStr + " in " + tallyStr + " for "\
+ filterStr + " and " + nuclideStr
# set yLabel
yLabel = scoreStr
yLabel = yLabel.title()
# Set saving filename
fileName = "tally_" + str(i_tally + 1) + "_" + scoreStr + \
"_filter_" + str(i_filter+1) + "_nuclide_" + str(i_nuclide+1) \
+ "." + fileType
REfileName = "tally_" + str(i_tally + 1) + "_" + scoreStr + \
"RE_filter_" + str(i_filter+1) + "_nuclide_" + str(i_nuclide+1) \
+ "." + fileType
# Plot mean with absolute error bars
plt.errorbar(active_batches, \
meanPlot[i_tally][i_filter][i_nuclide][i_score][:], \
absUncertPlot[i_tally][i_filter][i_nuclide][i_score][:],fmt='o-',aa=True)
plt.xlabel(xLabel)
plt.ylabel(yLabel)
plt.title(title)
if (fileType != 'none'):
plt.savefig(fileName)
if showImg:
plt.show()
plt.clf()
# Plot relative uncertainty
plt.plot(active_batches, \
uncertPlot[i_tally][i_filter][i_nuclide][i_score][:],'o-',aa=True)
plt.xlabel(xLabel)
plt.ylabel("Relative Error of " + yLabel)
plt.title("Relative Error of " + title)
if (fileType != 'none'):
plt.savefig(REfileName)
if showImg:
plt.show()
plt.clf()
if savetoCSV:
# This block loops through each tally, and for each tally:
# Creates a new file
# Writes the scores and filters for that tally in csv format.
# The columns will be: batches,then for each filter: all the scores
# The rows, of course, are the data points per batch.
for i_tally in range(len(meanPlot)):
# Set tally string (placeholder until I put tally labels in statePoint)
tallyStr = "Tally " + str(i_tally + 1)
CSV_filename = "./tally" + str(i_tally+1)+".csv"
# Open the file
f = open(CSV_filename, 'w')
# Write the header line
lineText = "Batches"
for i_filter in range(len(meanPlot[i_tally])):
# Set filter string
filterStr = "Filter " + str(i_filter + 1)
for i_nuclide in range(len(meanPlot[i_tally][i_filter])):
nuclideStr = "Nuclide " + str(i_nuclide + 1)
for i_score in range(len(meanPlot[i_tally][i_filter][i_nuclide])):
# Set the title
scoreStr = scoreType[i_batch][i_tally][i_filter][i_nuclide][i_score]
scoreStr = scoreStr.title()
if (printxs[i_tally] and ((scoreStr != 'Flux') and \
(scoreStr != 'Current'))):
scoreStr = scoreStr + "-XS"
# set header
headerText = scoreStr + " for " + filterStr + " for " + nuclideStr
lineText = lineText + "," + headerText + \
",Abs Unc of " + headerText + \
",Rel Unc of " + headerText
f.write(lineText + "\n")
# Write the data lines, each row is a different batch
for i_batch in range(len(meanPlot[i_tally][0][0][0])):
lineText = repr(active_batches[i_batch])
for i_filter in range(len(meanPlot[i_tally])):
for i_nuclide in range(len(meanPlot[i_tally][i_filter])):
for i_score in range(len(meanPlot[i_tally][i_filter][i_nuclide])):
fieldText = \
repr(meanPlot[i_tally][i_filter][i_nuclide][i_score][i_batch]) + \
"," + \
repr(absUncertPlot[i_tally][i_filter][i_nuclide][i_score][i_batch]) +\
"," + \
repr(uncertPlot[i_tally][i_filter][i_nuclide][i_score][i_batch])
lineText = lineText + "," + fieldText
f.write(lineText + "\n")

View file

@ -1,4 +1,4 @@
#!/usr/bin/env python2
#!/usr/bin/env python
"""Convert binary particle track to VTK poly data.
Usage information can be obtained by running 'track.py --help':
@ -18,6 +18,7 @@ Usage information can be obtained by running 'track.py --help':
import os
import argparse
import h5py
import struct
import vtk
@ -39,51 +40,38 @@ def main():
# Parse commandline arguments.
args = _parse_args()
# Check input file extensions.
for fname in args.input:
if not (fname.endswith('.h5') or fname.endswith('.binary')):
raise ValueError("Input file names must either end with '.h5' or"
"'.binary'.")
# Make sure that the output filename ends with '.pvtp'.
if not args.out:
args.out = 'tracks.pvtp'
elif not args.out.endswith('.pvtp'):
args.out += '.pvtp'
# Import HDF library if HDF files are present
for fname in args.input:
if fname.endswith('.h5'):
import h5py
break
# Initialize data arrays and offset.
points = vtk.vtkPoints()
cells = vtk.vtkCellArray()
point_offset = 0
for fname in args.input:
# Write coordinate values to points array.
if fname.endswith('.binary'):
track = open(fname, 'rb').read()
coords = [struct.unpack("ddd", track[24*i : 24*(i+1)])
for i in range(len(track)/24)]
n_points = len(coords)
for triplet in coords:
points.InsertNextPoint(triplet)
else:
coords = h5py.File(fname).get('coordinates')
n_points = coords.shape[0]
for i in range(n_points):
points.InsertNextPoint(coords[i, :])
track = h5py.File(fname)
n_particles = track['n_particles'].value
n_coords = track['n_coords']
coords = []
for i in range(n_particles):
coords.append(track['coordinates_' + str(i + 1)].value)
for j in range(n_coords[i]):
points.InsertNextPoint(coords[i][j,:])
# Create VTK line and assign points to line.
line = vtk.vtkPolyLine()
line.GetPointIds().SetNumberOfIds(n_points)
for i in range(n_points):
line.GetPointIds().SetId(i, point_offset+i)
for i in range(n_particles):
# Create VTK line and assign points to line.
line = vtk.vtkPolyLine()
line.GetPointIds().SetNumberOfIds(n_coords[i])
for j in range(n_coords[i]):
line.GetPointIds().SetId(j, point_offset + j)
# Add line to cell array
cells.InsertNextCell(line)
point_offset += n_coords[i]
cells.InsertNextCell(line)
point_offset += n_points
data = vtk.vtkPolyData()
data.SetPoints(points)
data.SetLines(cells)

View file

@ -1,14 +1,15 @@
#!/usr/bin/env python
"""Update OpenMC's input XML files to the latest format.
Usage information can be obtained by running 'update_inputs.py --help':
Usage information can be obtained by running 'openmc-update-inputs --help':
usage: update_lattices.py [-h] IN [IN ...]
usage: openmc-update-inputs [-h] IN [IN ...]
Update lattices in geometry.xml files to the latest format. This will remove
'outside' attributes/elements and replace them with 'outer' attributes. Note
that this script will not delete the given files; it will append '.original'
to the given files and write new ones.
Update geometry.xml files to the latest format. This will remove 'outside'
attributes/elements from lattices and replace them with 'outer' attributes. For
'cell' elements, any 'surfaces' attributes/elements will be renamed
'region'. Note that this script will not delete the given files; it will append
'.original' to the given files and write new ones.
positional arguments:
IN Input geometry.xml file(s).
@ -35,9 +36,10 @@ they will be moved to a new file with '.original' appended to their name.
Formatting changes that will be made:
geometry.xml: Lattices containing 'outside' attributes/tags will be replaced
geometry.xml: Lattices containing 'outside' attributes/tags will be replaced
with lattices containing 'outer' attributes, and the appropriate
cells/universes will be added.
cells/universes will be added. Any 'surfaces' attributes/elements on a cell
will be renamed 'region'.
"""
@ -173,9 +175,6 @@ def update_geometry(geometry_root):
root = geometry_root
was_updated = False
# Ignore files that do not contain lattices.
if all([child.tag != 'lattice' for child in root]): return False
# Get a set of already-used universe and cell ids.
uids = get_universe_ids(root)
cids = get_cell_ids(root)
@ -233,6 +232,17 @@ def update_geometry(geometry_root):
del lat.attrib['width']
was_updated = True
# Change 'surfaces' to 'region' in cell definitions
for cell in root.iter('cell'):
elem = cell.find('surfaces')
if elem is not None:
elem.tag = 'region'
was_updated = True
if 'surfaces' in cell.attrib:
cell.attrib['region'] = cell.attrib['surfaces']
del cell.attrib['surfaces']
was_updated = True
return was_updated

View file

@ -1,9 +1,11 @@
#!/usr/bin/env python2
#!/usr/bin/env python
from __future__ import division, print_function
import struct
import sys
import numpy as np
import h5py
def parse_options():
"""Process command line arguments"""
@ -22,14 +24,16 @@ def parse_options():
return parsed
def main(file_, o):
print(file_)
fh = open(file_, 'rb')
header = get_header(fh)
meshparms = (header['dimension'] + header['lower_left'] +
header['upper_right'])
nx, ny, nz = meshparms[:3]
ll = header['lower_left']
def main(filename, o):
# Read data from voxel file
fh = h5py.File(filename, 'r')
dimension = fh['num_voxels'].value
width = fh['voxel_width'].value
lower_left = fh['lower_left'].value
voxel_data = fh['data'].value
nx, ny, nz = dimension
upper_right = lower_left + width*dimension
if o.vtk:
try:
@ -40,13 +44,10 @@ def main(file_, o):
'See: http://www.vtk.org/')
return
origin = [(l + w*n/2.) for n, l, w in
zip((nx, ny, nz), ll, header['width'])]
grid = vtk.vtkImageData()
grid.SetDimensions(nx+1, ny+1, nz+1)
grid.SetOrigin(*ll)
grid.SetSpacing(*header['width'])
grid.SetOrigin(*lower_left)
grid.SetSpacing(*width)
data = vtk.vtkDoubleArray()
data.SetName("id")
@ -57,8 +58,7 @@ def main(file_, o):
for y in range(ny):
for z in range(nz):
i = z*nx*ny + y*nx + x
id_ = get_int(fh)[0]
data.SetValue(i, id_)
data.SetValue(i, voxel_data[x,y,z])
grid.GetCellData().AddArray(data)
writer = vtk.vtkXMLImageDataWriter()
@ -81,44 +81,23 @@ def main(file_, o):
if not o.output.endswith(".silo"):
o.output += ".silo"
silomesh.init_silo(o.output)
silomesh.init_mesh('plot', *meshparms)
meshparams = list(map(int, dimension)) + list(map(float, lower_left)) + \
list(map(float, upper_right))
silomesh.init_mesh('plot', *meshparams)
silomesh.init_var("id")
for x in range(1, nx+1):
for x in range(nx):
sys.stdout.write(" {0}%\r".format(int(x/nx*100)))
sys.stdout.flush()
for y in range(1, ny+1):
for z in range(1, nz+1):
id_ = get_int(fh)[0]
silomesh.set_value(float(id_), x, y, z)
for y in range(ny):
for z in range(nz):
silomesh.set_value(float(voxel_data[x,y,z]),
x + 1, y + 1, z + 1)
print()
silomesh.finalize_var()
silomesh.finalize_mesh()
silomesh.finalize_silo()
def get_header(file_):
nx, ny, nz = get_int(file_, 3)
wx, wy, wz = get_double(file_, 3)
lx, ly, lz = get_double(file_, 3)
header = {'dimension': [nx, ny, nz], 'width': [wx, wy, wz],
'lower_left': [lx, ly, lz],
'upper_right': [lx+wx*nx, ly+wy*ny, lz+wz*nz]}
return header
def get_data(file_, n, typeCode, size):
return list(struct.unpack('={0}{1}'.format(n, typeCode),
file_.read(n*size)))
def get_int(file_, n=1, path=None):
return get_data(file_, n, 'i', 4)
def get_double(file_, n=1, path=None):
return get_data(file_, n, 'd', 8)
if __name__ == '__main__':
(options, args) = parse_options()
if args:

View file

@ -10,8 +10,8 @@ except ImportError:
have_setuptools = False
kwargs = {'name': 'openmc',
'version': '0.7.0',
'packages': ['openmc'],
'version': '0.7.1',
'packages': ['openmc', 'openmc.mgxs'],
'scripts': glob.glob('scripts/openmc-*'),
# Metadata
@ -32,7 +32,7 @@ kwargs = {'name': 'openmc',
if have_setuptools:
kwargs.update({
# Required dependencies
'install_requires': ['numpy', 'scipy', 'h5py', 'matplotlib'],
'install_requires': ['numpy', 'h5py', 'matplotlib'],
# Optional dependencies
'extras_require': {

View file

@ -45,9 +45,9 @@ contains
integer :: temp_table ! temporary value for sorting
character(12) :: name ! name of isotope, e.g. 92235.03c
character(12) :: alias ! alias of nuclide, e.g. U-235.03c
type(Material), pointer :: mat => null()
type(Nuclide), pointer :: nuc => null()
type(SAlphaBeta), pointer :: sab => null()
type(Material), pointer :: mat
type(Nuclide), pointer :: nuc
type(SAlphaBeta), pointer :: sab
type(SetChar) :: already_read
! allocate arrays for ACE table storage and cross section cache
@ -215,6 +215,16 @@ contains
end do MATERIAL_LOOP3
! Show which nuclide results in lowest energy for neutron transport
do i = 1, n_nuclides_total
if (nuclides(i)%energy(nuclides(i)%n_grid) == energy_max_neutron) then
call write_message("Maximum neutron transport energy: " // &
trim(to_str(energy_max_neutron)) // " MeV for " // &
trim(adjustl(nuclides(i)%name)), 6)
exit
end if
end do
end subroutine read_xs
!===============================================================================
@ -224,7 +234,6 @@ contains
!===============================================================================
subroutine read_ace_table(i_table, i_listing)
integer, intent(in) :: i_table ! index in nuclides/sab_tables
integer, intent(in) :: i_listing ! index in xs_listings
@ -234,7 +243,7 @@ contains
integer :: location ! location of ACE table
integer :: entries ! number of entries on each record
integer :: length ! length of ACE table
integer :: in = 7 ! file unit
integer :: unit_ace ! file unit
integer :: zaids(16) ! list of ZAIDs (only used for S(a,b))
integer :: filetype ! filetype (ASCII or BINARY)
real(8) :: kT ! temperature of table
@ -248,9 +257,9 @@ contains
character(10) :: mat ! material identifier
character(70) :: comment ! comment for ACE table
character(MAX_FILE_LEN) :: filename ! path to ACE cross section library
type(Nuclide), pointer :: nuc => null()
type(SAlphaBeta), pointer :: sab => null()
type(XsListing), pointer :: listing => null()
type(Nuclide), pointer :: nuc
type(SAlphaBeta), pointer :: sab
type(XsListing), pointer :: listing
! determine path, record length, and location of table
listing => xs_listings(i_listing)
@ -277,14 +286,14 @@ contains
! READ ACE TABLE IN ASCII FORMAT
! Find location of table
open(UNIT=in, FILE=filename, STATUS='old', ACTION='read')
rewind(UNIT=in)
open(NEWUNIT=unit_ace, FILE=filename, STATUS='old', ACTION='read')
rewind(UNIT=unit_ace)
do i = 1, location - 1
read(UNIT=in, FMT=*)
read(UNIT=unit_ace, FMT=*)
end do
! Read first line of header
read(UNIT=in, FMT='(A10,2G12.0,1X,A10)') name, awr, kT, date_
read(UNIT=unit_ace, FMT='(A10,2G12.0,1X,A10)') name, awr, kT, date_
! Check that correct xs was found -- if cross_sections.xml is broken, the
! location of the table may be wrong
@ -294,7 +303,7 @@ contains
end if
! Read more header and NXS and JXS
read(UNIT=in, FMT=100) comment, mat, &
read(UNIT=unit_ace, FMT=100) comment, mat, &
(zaids(i), awrs(i), i=1,16), NXS, JXS
100 format(A70,A10/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/4(I7,F11.0)/&
,8I9/8I9/8I9/8I9/8I9/8I9)
@ -304,21 +313,21 @@ contains
allocate(XSS(length))
! Read XSS array
read(UNIT=in, FMT='(4G20.0)') XSS
read(UNIT=unit_ace, FMT='(4G20.0)') XSS
! Close ACE file
close(UNIT=in)
close(UNIT=unit_ace)
elseif (filetype == BINARY) then
! =======================================================================
! READ ACE TABLE IN BINARY FORMAT
! Open ACE file
open(UNIT=in, FILE=filename, STATUS='old', ACTION='read', &
open(NEWUNIT=unit_ace, FILE=filename, STATUS='old', ACTION='read', &
ACCESS='direct', RECL=record_length)
! Read all header information
read(UNIT=in, REC=location) name, awr, kT, date_, &
read(UNIT=unit_ace, REC=location) name, awr, kT, date_, &
comment, mat, (zaids(i), awrs(i), i=1,16), NXS, JXS
! determine table length
@ -329,11 +338,11 @@ contains
do i = 1, (length + entries - 1)/entries
j1 = 1 + (i-1)*entries
j2 = min(length, j1 + entries - 1)
read(UNIT=IN, REC=location + i) (XSS(j), j=j1,j2)
read(UNIT=UNIT_ACE, REC=location + i) (XSS(j), j=j1,j2)
end do
! Close ACE file
close(UNIT=in)
close(UNIT=unit_ace)
end if
! ==========================================================================
@ -396,8 +405,6 @@ contains
end select
deallocate(XSS)
if(associated(nuc)) nullify(nuc)
if(associated(sab)) nullify(sab)
end subroutine read_ace_table
@ -407,10 +414,8 @@ contains
!===============================================================================
subroutine read_esz(nuc, data_0K)
type(Nuclide), pointer :: nuc
logical :: data_0K ! are we reading 0K data?
type(Nuclide), intent(inout) :: nuc
logical, intent(in) :: data_0K ! are we reading 0K data?
integer :: NE ! number of energy points for total and elastic cross sections
integer :: i ! index in 0K elastic xs array for this nuclide
@ -482,6 +487,10 @@ contains
! Continue reading elastic scattering and heating
nuc % elastic = get_real(NE)
! Determine if minimum/maximum energy for this nuclide is greater/less
! than the previous
energy_min_neutron = max(energy_min_neutron, nuc%energy(1))
energy_max_neutron = min(energy_max_neutron, nuc%energy(NE))
end if
end subroutine read_esz
@ -493,8 +502,7 @@ contains
!===============================================================================
subroutine read_nu_data(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: i ! loop index
integer :: JXS2 ! location for fission nu data
@ -510,7 +518,7 @@ contains
integer :: LOCC ! location of energy distributions for given MT
integer :: lc ! locator
integer :: length ! length of data to allocate
type(DistEnergy), pointer :: edist => null()
type(DistEnergy), pointer :: edist
JXS2 = JXS(2)
JXS24 = JXS(24)
@ -635,6 +643,15 @@ contains
! Allocate space for secondary energy distribution
NPCR = NXS(8)
! Check to make sure nuclide does not have more than the maximum number
! of delayed groups
if (NPCR > MAX_DELAYED_GROUPS) then
call fatal_error("Encountered nuclide with " // trim(to_str(NPCR)) &
// " delayed groups while the maximum number of delayed groups &
&set in constants.F90 is " // trim(to_str(MAX_DELAYED_GROUPS)))
end if
nuc % n_precursor = NPCR
allocate(nuc % nu_d_edist(NPCR))
@ -672,6 +689,7 @@ contains
else
nuc % nu_d_type = NU_NONE
nuc % n_precursor = 0
end if
end subroutine read_nu_data
@ -683,8 +701,7 @@ contains
!===============================================================================
subroutine read_reactions(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: i ! loop indices
integer :: i_fission ! index in nuc % index_fission
@ -698,7 +715,6 @@ contains
integer :: IE ! reaction's starting index on energy grid
integer :: NE ! number of energies
integer :: NR ! number of interpolation regions
type(Reaction), pointer :: rxn => null()
type(ListInt) :: MTs
LMT = JXS(3)
@ -716,14 +732,15 @@ contains
! Store elastic scattering cross-section on reaction one -- note that the
! sigma array is not allocated or stored for elastic scattering since it is
! already stored in nuc % elastic
rxn => nuc % reactions(1)
rxn % MT = 2
rxn % Q_value = ZERO
rxn % multiplicity = 1
rxn % threshold = 1
rxn % scatter_in_cm = .true.
rxn % has_angle_dist = .false.
rxn % has_energy_dist = .false.
associate (rxn => nuc % reactions(1))
rxn%MT = 2
rxn%Q_value = ZERO
rxn%multiplicity = 1
rxn%threshold = 1
rxn%scatter_in_cm = .true.
rxn%has_angle_dist = .false.
rxn%has_energy_dist = .false.
end associate
! Add contribution of elastic scattering to total cross section
nuc % total = nuc % total + nuc % elastic
@ -736,123 +753,125 @@ contains
i_fission = 0
do i = 1, NMT
rxn => nuc % reactions(i+1)
associate (rxn => nuc % reactions(i+1))
! set defaults
rxn % has_angle_dist = .false.
rxn % has_energy_dist = .false.
! set defaults
rxn % has_angle_dist = .false.
rxn % has_energy_dist = .false.
! read MT number, Q-value, and neutrons produced
rxn % MT = int(XSS(LMT + i - 1))
rxn % Q_value = XSS(JXS4 + i - 1)
rxn % multiplicity = abs(nint(XSS(JXS5 + i - 1)))
rxn % scatter_in_cm = (nint(XSS(JXS5 + i - 1)) < 0)
! read MT number, Q-value, and neutrons produced
rxn % MT = int(XSS(LMT + i - 1))
rxn % Q_value = XSS(JXS4 + i - 1)
rxn % multiplicity = abs(nint(XSS(JXS5 + i - 1)))
rxn % scatter_in_cm = (nint(XSS(JXS5 + i - 1)) < 0)
! Read energy-dependent multiplicities
if (rxn % multiplicity > 100) then
! Set flag and allocate space for Tab1 to store yield
rxn % multiplicity_with_E = .true.
allocate(rxn % multiplicity_E)
! Read energy-dependent multiplicities
if (rxn % multiplicity > 100) then
! Set flag and allocate space for Tab1 to store yield
rxn % multiplicity_with_E = .true.
allocate(rxn % multiplicity_E)
XSS_index = JXS(11) + rxn % multiplicity - 101
NR = nint(XSS(XSS_index))
rxn % multiplicity_E % n_regions = NR
XSS_index = JXS(11) + rxn % multiplicity - 101
NR = nint(XSS(XSS_index))
rxn % multiplicity_E % n_regions = NR
! allocate space for ENDF interpolation parameters
if (NR > 0) then
allocate(rxn % multiplicity_E % nbt(NR))
allocate(rxn % multiplicity_E % int(NR))
end if
! allocate space for ENDF interpolation parameters
if (NR > 0) then
allocate(rxn % multiplicity_E % nbt(NR))
allocate(rxn % multiplicity_E % int(NR))
! read ENDF interpolation parameters
XSS_index = XSS_index + 1
if (NR > 0) then
rxn % multiplicity_E % nbt = get_int(NR)
rxn % multiplicity_E % int = get_int(NR)
end if
! allocate space for yield data
XSS_index = XSS_index + 2*NR
NE = nint(XSS(XSS_index))
rxn % multiplicity_E % n_pairs = NE
allocate(rxn % multiplicity_E % x(NE))
allocate(rxn % multiplicity_E % y(NE))
! read yield data
XSS_index = XSS_index + 1
rxn % multiplicity_E % x = get_real(NE)
rxn % multiplicity_E % y = get_real(NE)
end if
! read ENDF interpolation parameters
XSS_index = XSS_index + 1
if (NR > 0) then
rxn % multiplicity_E % nbt = get_int(NR)
rxn % multiplicity_E % int = get_int(NR)
end if
! read starting energy index
LOCA = int(XSS(LXS + i - 1))
IE = int(XSS(JXS7 + LOCA - 1))
rxn % threshold = IE
! allocate space for yield data
XSS_index = XSS_index + 2*NR
NE = nint(XSS(XSS_index))
rxn % multiplicity_E % n_pairs = NE
allocate(rxn % multiplicity_E % x(NE))
allocate(rxn % multiplicity_E % y(NE))
! read yield data
XSS_index = XSS_index + 1
rxn % multiplicity_E % x = get_real(NE)
rxn % multiplicity_E % y = get_real(NE)
end if
! read starting energy index
LOCA = int(XSS(LXS + i - 1))
IE = int(XSS(JXS7 + LOCA - 1))
rxn % threshold = IE
! read number of energies cross section values
NE = int(XSS(JXS7 + LOCA))
allocate(rxn % sigma(NE))
XSS_index = JXS7 + LOCA + 1
rxn % sigma = get_real(NE)
! read number of energies cross section values
NE = int(XSS(JXS7 + LOCA))
allocate(rxn % sigma(NE))
XSS_index = JXS7 + LOCA + 1
rxn % sigma = get_real(NE)
end associate
end do
! Create set of MT values
do i = 1, size(nuc % reactions)
call MTs % append(nuc % reactions(i) % MT)
call nuc%reaction_index%add_key(nuc%reactions(i)%MT, i)
end do
! Create total, absorption, and fission cross sections
do i = 2, size(nuc % reactions)
rxn => nuc % reactions(i)
IE = rxn % threshold
NE = size(rxn % sigma)
associate (rxn => nuc % reactions(i))
IE = rxn % threshold
NE = size(rxn % sigma)
! Skip total inelastic level scattering, gas production cross sections
! (MT=200+), etc.
if (rxn % MT == N_LEVEL) cycle
if (rxn % MT > N_5N2P .and. rxn % MT < N_P0) cycle
! Skip total inelastic level scattering, gas production cross sections
! (MT=200+), etc.
if (rxn % MT == N_LEVEL) cycle
if (rxn % MT > N_5N2P .and. rxn % MT < N_P0) cycle
! Skip level cross sections if total is available
if (rxn % MT >= N_P0 .and. rxn % MT <= N_PC .and. MTs % contains(N_P)) cycle
if (rxn % MT >= N_D0 .and. rxn % MT <= N_DC .and. MTs % contains(N_D)) cycle
if (rxn % MT >= N_T0 .and. rxn % MT <= N_TC .and. MTs % contains(N_T)) cycle
if (rxn % MT >= N_3HE0 .and. rxn % MT <= N_3HEC .and. MTs % contains(N_3HE)) cycle
if (rxn % MT >= N_A0 .and. rxn % MT <= N_AC .and. MTs % contains(N_A)) cycle
if (rxn % MT >= N_2N0 .and. rxn % MT <= N_2NC .and. MTs % contains(N_2N)) cycle
! Skip level cross sections if total is available
if (rxn % MT >= N_P0 .and. rxn % MT <= N_PC .and. MTs % contains(N_P)) cycle
if (rxn % MT >= N_D0 .and. rxn % MT <= N_DC .and. MTs % contains(N_D)) cycle
if (rxn % MT >= N_T0 .and. rxn % MT <= N_TC .and. MTs % contains(N_T)) cycle
if (rxn % MT >= N_3HE0 .and. rxn % MT <= N_3HEC .and. MTs % contains(N_3HE)) cycle
if (rxn % MT >= N_A0 .and. rxn % MT <= N_AC .and. MTs % contains(N_A)) cycle
if (rxn % MT >= N_2N0 .and. rxn % MT <= N_2NC .and. MTs % contains(N_2N)) cycle
! Add contribution to total cross section
nuc % total(IE:IE+NE-1) = nuc % total(IE:IE+NE-1) + rxn % sigma
! Add contribution to total cross section
nuc % total(IE:IE+NE-1) = nuc % total(IE:IE+NE-1) + rxn % sigma
! Add contribution to absorption cross section
if (is_disappearance(rxn % MT)) then
nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma
end if
! Add contribution to absorption cross section
if (is_disappearance(rxn % MT)) then
nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma
end if
! Information about fission reactions
if (rxn % MT == N_FISSION) then
allocate(nuc % index_fission(1))
elseif (rxn % MT == N_F) then
allocate(nuc % index_fission(PARTIAL_FISSION_MAX))
nuc % has_partial_fission = .true.
end if
! Information about fission reactions
if (rxn % MT == N_FISSION) then
allocate(nuc % index_fission(1))
elseif (rxn % MT == N_F) then
allocate(nuc % index_fission(PARTIAL_FISSION_MAX))
nuc % has_partial_fission = .true.
end if
! Add contribution to fission cross section
if (is_fission(rxn % MT)) then
nuc % fissionable = .true.
nuc % fission(IE:IE+NE-1) = nuc % fission(IE:IE+NE-1) + rxn % sigma
! Add contribution to fission cross section
if (is_fission(rxn % MT)) then
nuc % fissionable = .true.
nuc % fission(IE:IE+NE-1) = nuc % fission(IE:IE+NE-1) + rxn % sigma
! Also need to add fission cross sections to absorption
nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma
! Also need to add fission cross sections to absorption
nuc % absorption(IE:IE+NE-1) = nuc % absorption(IE:IE+NE-1) + rxn % sigma
! If total fission reaction is present, there's no need to store the
! reaction cross-section since it was copied to nuc % fission
if (rxn % MT == N_FISSION) deallocate(rxn % sigma)
! If total fission reaction is present, there's no need to store the
! reaction cross-section since it was copied to nuc % fission
if (rxn % MT == N_FISSION) deallocate(rxn % sigma)
! Keep track of this reaction for easy searching later
i_fission = i_fission + 1
nuc % index_fission(i_fission) = i
nuc % n_fission = nuc % n_fission + 1
end if
! Keep track of this reaction for easy searching later
i_fission = i_fission + 1
nuc % index_fission(i_fission) = i
nuc % n_fission = nuc % n_fission + 1
end if
end associate
end do
! Clear MTs set
@ -866,8 +885,7 @@ contains
!===============================================================================
subroutine read_angular_dist(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: JXS8 ! location of angular distribution locators
integer :: JXS9 ! location of angular distributions
@ -878,7 +896,6 @@ contains
integer :: i ! index in reactions array
integer :: j ! index over incoming energies
integer :: length ! length of data array to allocate
type(Reaction), pointer :: rxn => null()
JXS8 = JXS(8)
JXS9 = JXS(9)
@ -886,71 +903,72 @@ contains
! loop over all reactions with secondary neutrons -- NXS(5) does not include
! elastic scattering
do i = 1, NXS(5) + 1
rxn => nuc%reactions(i)
associate (rxn => nuc%reactions(i))
! find location of angular distribution
LOCB = int(XSS(JXS8 + i - 1))
if (LOCB == -1) then
! Angular distribution data are specified through LAWi = 44 in the DLW
! block
cycle
elseif (LOCB == 0) then
! No angular distribution data are given for this reaction, isotropic
! scattering is asssumed (in CM if TY < 0 and in LAB if TY > 0)
cycle
end if
rxn % has_angle_dist = .true.
! allocate space for incoming energies and locations
NE = int(XSS(JXS9 + LOCB - 1))
rxn % adist % n_energy = NE
allocate(rxn % adist % energy(NE))
allocate(rxn % adist % type(NE))
allocate(rxn % adist % location(NE))
! read incoming energy grid and location of nucs
XSS_index = JXS9 + LOCB
rxn % adist % energy = get_real(NE)
rxn % adist % location = get_int(NE)
! determine dize of data block
length = 0
do j = 1, NE
LC = rxn % adist % location(j)
if (LC == 0) then
! isotropic
rxn % adist % type(j) = ANGLE_ISOTROPIC
elseif (LC > 0) then
! 32 equiprobable bins
rxn % adist % type(j) = ANGLE_32_EQUI
length = length + 33
elseif (LC < 0) then
! tabular distribution
rxn % adist % type(j) = ANGLE_TABULAR
NP = int(XSS(JXS9 + abs(LC)))
length = length + 2 + 3*NP
! find location of angular distribution
LOCB = int(XSS(JXS8 + i - 1))
if (LOCB == -1) then
! Angular distribution data are specified through LAWi = 44 in the DLW
! block
cycle
elseif (LOCB == 0) then
! No angular distribution data are given for this reaction, isotropic
! scattering is assumed (in CM if TY < 0 and in LAB if TY > 0)
cycle
end if
end do
rxn % has_angle_dist = .true.
! allocate angular distribution data and read
allocate(rxn % adist % data(length))
! allocate space for incoming energies and locations
NE = int(XSS(JXS9 + LOCB - 1))
rxn % adist % n_energy = NE
allocate(rxn % adist % energy(NE))
allocate(rxn % adist % type(NE))
allocate(rxn % adist % location(NE))
! read angular distribution -- currently this does not actually parse the
! angular distribution tables for each incoming energy, that must be done
! on-the-fly
XSS_index = JXS9 + LOCB + 2 * NE
rxn % adist % data = get_real(length)
! read incoming energy grid and location of nucs
XSS_index = JXS9 + LOCB
rxn % adist % energy = get_real(NE)
rxn % adist % location = get_int(NE)
! change location pointers since they are currently relative to JXS(9)
LC = LOCB + 2 * NE + 1
do j = 1, NE
! For consistency, leave location as 0 if type is isotropic.
! This is not necessary for current correctness, but can avoid
! future issues
if (rxn % adist % location(j) /= 0) then
rxn % adist % location(j) = abs(rxn % adist % location(j)) - LC
end if
end do
! determine dize of data block
length = 0
do j = 1, NE
LC = rxn % adist % location(j)
if (LC == 0) then
! isotropic
rxn % adist % type(j) = ANGLE_ISOTROPIC
elseif (LC > 0) then
! 32 equiprobable bins
rxn % adist % type(j) = ANGLE_32_EQUI
length = length + 33
elseif (LC < 0) then
! tabular distribution
rxn % adist % type(j) = ANGLE_TABULAR
NP = int(XSS(JXS9 + abs(LC)))
length = length + 2 + 3*NP
end if
end do
! allocate angular distribution data and read
allocate(rxn % adist % data(length))
! read angular distribution -- currently this does not actually parse the
! angular distribution tables for each incoming energy, that must be done
! on-the-fly
XSS_index = JXS9 + LOCB + 2 * NE
rxn % adist % data = get_real(length)
! change location pointers since they are currently relative to JXS(9)
LC = LOCB + 2 * NE + 1
do j = 1, NE
! For consistency, leave location as 0 if type is isotropic.
! This is not necessary for current correctness, but can avoid
! future issues
if (rxn % adist % location(j) /= 0) then
rxn % adist % location(j) = abs(rxn % adist % location(j)) - LC
end if
end do
end associate
end do
end subroutine read_angular_dist
@ -961,29 +979,28 @@ contains
!===============================================================================
subroutine read_energy_dist(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: LED ! location of energy distribution locators
integer :: LOCC ! location of energy distributions for given MT
integer :: i ! loop index
type(Reaction), pointer :: rxn => null()
LED = JXS(10)
! Loop over all reactions
do i = 1, NXS(5)
rxn => nuc % reactions(i+1) ! skip over elastic scattering
rxn % has_energy_dist = .true.
associate (rxn => nuc % reactions(i+1)) ! skip over elastic scattering
rxn % has_energy_dist = .true.
! find location of energy distribution data
LOCC = int(XSS(LED + i - 1))
! find location of energy distribution data
LOCC = int(XSS(LED + i - 1))
! allocate energy distribution
allocate(rxn % edist)
! allocate energy distribution
allocate(rxn % edist)
! read data for energy distribution
call get_energy_dist(rxn % edist, LOCC)
! read data for energy distribution
call get_energy_dist(rxn % edist, LOCC)
end associate
end do
end subroutine read_energy_dist
@ -995,10 +1012,9 @@ contains
!===============================================================================
recursive subroutine get_energy_dist(edist, loc_law, delayed_n)
type(DistEnergy), pointer :: edist ! energy distribution
integer, intent(in) :: loc_law ! locator for data
logical, optional :: delayed_n ! is this for delayed neutrons?
type(DistEnergy), intent(inout) :: edist ! energy distribution
integer, intent(in) :: loc_law ! locator for data
logical, intent(in), optional :: delayed_n ! is this for delayed neutrons?
integer :: LDIS ! location of all energy distributions
integer :: LNW ! location of next energy distribution if multiple
@ -1078,7 +1094,6 @@ contains
!===============================================================================
function length_energy_dist(lc, law, LOCC, lid) result(length)
integer, intent(in) :: lc ! location in XSS array
integer, intent(in) :: law ! energy distribution law
integer, intent(in) :: LOCC ! location of energy distribution
@ -1122,7 +1137,7 @@ contains
NR = int(XSS(lc + 1))
NE = int(XSS(lc + 2 + 2*NR))
allocate(L(NE))
L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
L(:) = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
! Continue with finding data length
length = length + 2 + 2*NR + 2*NE
@ -1180,7 +1195,7 @@ contains
NR = int(XSS(lc + 1))
NE = int(XSS(lc + 2 + 2*NR))
allocate(L(NE))
L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
L(:) = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
! Continue with finding data length
length = length + 2 + 2*NR + 2*NE
@ -1210,7 +1225,7 @@ contains
NR = int(XSS(lc + 1))
NE = int(XSS(lc + 2 + 2*NR))
allocate(L(NE))
L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
L(:) = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
! Continue with finding data length
length = length + 2 + 2*NR + 2*NE
@ -1261,7 +1276,7 @@ contains
! in a way inconsistent with the current form of the ACE Format Guide
! (MCNP5 Manual, Vol 3)
allocate(L(NE))
L = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
L(:) = int(XSS(lc + 3 + 2*NR + NE: lc + 3 + 2*NR + 2*NE - 1))
! Don't currently do anything with L
deallocate(L)
! Continue with finding data length
@ -1277,8 +1292,7 @@ contains
!===============================================================================
subroutine read_unr_res(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: JXS23 ! location of URR data
integer :: lc ! locator
@ -1366,8 +1380,7 @@ contains
!===============================================================================
subroutine generate_nu_fission(nuc)
type(Nuclide), pointer :: nuc
type(Nuclide), intent(inout) :: nuc
integer :: i ! index on nuclide energy grid
real(8) :: E ! energy
@ -1393,8 +1406,7 @@ contains
!===============================================================================
subroutine read_thermal_data(table)
type(SAlphaBeta), pointer :: table
type(SAlphaBeta), intent(inout) :: table
integer :: i ! index for incoming energies
integer :: j ! index for outgoing energies
@ -1576,7 +1588,7 @@ contains
do i = 1, n_nuclides_total
do j = 1, n_nuclides_total
if (nuclides(i) % zaid == nuclides(j) % zaid) then
call nuclides(i) % nuc_list % append(j)
call nuclides(i) % nuc_list % push_back(j)
end if
end do
end do

View file

@ -1,8 +1,9 @@
module ace_header
use constants, only: MAX_FILE_LEN, ZERO
use dict_header, only: DictIntInt
use endf_header, only: Tab1
use list_header, only: ListInt
use stl_vector, only: VectorInt
implicit none
@ -17,10 +18,6 @@ module ace_header
integer, allocatable :: type(:) ! type of distribution
integer, allocatable :: location(:) ! location of each table
real(8), allocatable :: data(:) ! angular distribution data
! Type-Bound procedures
contains
procedure :: clear => distangle_clear ! Deallocates DistAngle
end type DistAngle
!===============================================================================
@ -51,7 +48,7 @@ module ace_header
integer :: MT ! ENDF MT value
real(8) :: Q_value ! Reaction Q value
integer :: multiplicity ! Number of secondary particles released
type(Tab1), pointer :: multiplicity_E => null() ! Energy-dependent neutron yield
type(Tab1), allocatable :: multiplicity_E ! Energy-dependent neutron yield
integer :: threshold ! Energy grid index of threshold
logical :: scatter_in_cm ! scattering system in center-of-mass?
logical :: multiplicity_with_E = .false. ! Flag to indicate E-dependent multiplicity
@ -79,10 +76,6 @@ module ace_header
logical :: multiply_smooth ! multiply by smooth cross section?
real(8), allocatable :: energy(:) ! incident energies
real(8), allocatable :: prob(:,:,:) ! actual probabibility tables
! Type-Bound procedures
contains
procedure :: clear => urrdata_clear ! Deallocates UrrData
end type UrrData
!===============================================================================
@ -99,7 +92,7 @@ module ace_header
real(8) :: kT ! temperature in MeV (k*T)
! Linked list of indices in nuclides array of instances of this same nuclide
type(ListInt) :: nuc_list
type(VectorInt) :: nuc_list
! Energy grid information
integer :: n_grid ! # of nuclide grid points
@ -153,7 +146,9 @@ module ace_header
! Reactions
integer :: n_reaction ! # of reactions
type(Reaction), pointer :: reactions(:) => null()
type(Reaction), allocatable :: reactions(:)
type(DictIntInt) :: reaction_index ! map MT values to index in reactions
! array; used at tally-time
! Type-Bound procedures
contains
@ -166,14 +161,12 @@ module ace_header
!===============================================================================
type Nuclide0K
character(10) :: nuclide ! name of nuclide, e.g. U-238
character(16) :: scheme = 'ares' ! target velocity sampling scheme
character(10) :: name ! name of nuclide, e.g. 92235.03c
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
real(8) :: E_min = 0.01e-6_8 ! lower cutoff energy for res scattering
real(8) :: E_max = 1000.0e-6_8 ! upper cutoff energy for res scattering
end type Nuclide0K
!===============================================================================
@ -265,7 +258,6 @@ module ace_header
real(8) :: absorption ! microscopic absorption xs
real(8) :: fission ! microscopic fission xs
real(8) :: nu_fission ! microscopic production xs
real(8) :: kappa_fission ! microscopic energy-released from fission
! Information for S(a,b) use
integer :: index_sab ! index in sab_tables (zero means no table)
@ -288,24 +280,10 @@ module ace_header
real(8) :: absorption ! macroscopic absorption xs
real(8) :: fission ! macroscopic fission xs
real(8) :: nu_fission ! macroscopic production xs
real(8) :: kappa_fission ! macroscopic energy-released from fission
end type MaterialMacroXS
contains
!===============================================================================
! DISTANGLE_CLEAR resets and deallocates data in Reaction.
!===============================================================================
subroutine distangle_clear(this)
class(DistAngle), intent(inout) :: this ! The DistAngle object to clear
if (allocated(this % energy)) &
deallocate(this % energy, this % type, this % location, this % data)
end subroutine distangle_clear
!===============================================================================
! DISTENERGY_CLEAR resets and deallocates data in DistEnergy.
!===============================================================================
@ -314,12 +292,6 @@ module ace_header
class(DistEnergy), intent(inout) :: this ! The DistEnergy object to clear
! Clear p_valid
call this % p_valid % clear()
if (allocated(this % data)) &
deallocate(this % data)
if (associated(this % next)) then
! recursively clear this item
call this % next % clear()
@ -336,32 +308,13 @@ module ace_header
class(Reaction), intent(inout) :: this ! The Reaction object to clear
if (allocated(this % sigma)) deallocate(this % sigma)
if (associated(this % multiplicity_E)) deallocate(this % multiplicity_E)
if (associated(this % edist)) then
call this % edist % clear()
deallocate(this % edist)
end if
call this % adist % clear()
end subroutine reaction_clear
!===============================================================================
! URRDATA_CLEAR resets and deallocates data in Reaction.
!===============================================================================
subroutine urrdata_clear(this)
class(UrrData), intent(inout) :: this ! The UrrData object to clear
if (allocated(this % energy)) &
deallocate(this % energy, this % prob)
end subroutine urrdata_clear
!===============================================================================
! NUCLIDE_CLEAR resets and deallocates data in Nuclide.
!===============================================================================
@ -372,31 +325,6 @@ module ace_header
integer :: i ! Loop counter
if (allocated(this % energy)) &
deallocate(this % energy, this % total, this % elastic, &
& this % fission, this % nu_fission, this % absorption)
if (allocated(this % energy_0K)) &
deallocate(this % energy_0K)
if (allocated(this % elastic_0K)) &
deallocate(this % elastic_0K)
if (allocated(this % xs_cdf)) &
deallocate(this % xs_cdf)
if (allocated(this % heating)) &
deallocate(this % heating)
if (allocated(this % index_fission)) deallocate(this % index_fission)
if (allocated(this % nu_t_data)) deallocate(this % nu_t_data)
if (allocated(this % nu_p_data)) deallocate(this % nu_p_data)
if (allocated(this % nu_d_data)) deallocate(this % nu_d_data)
if (allocated(this % nu_d_precursor_data)) &
deallocate(this % nu_d_precursor_data)
if (associated(this % nu_d_edist)) then
do i = 1, size(this % nu_d_edist)
call this % nu_d_edist(i) % clear()
@ -405,18 +333,16 @@ module ace_header
end if
if (associated(this % urr_data)) then
call this % urr_data % clear()
deallocate(this % urr_data)
end if
if (associated(this % reactions)) then
if (allocated(this % reactions)) then
do i = 1, size(this % reactions)
call this % reactions(i) % clear()
end do
deallocate(this % reactions)
end if
call this % nuc_list % clear()
call this % reaction_index % clear()
end subroutine nuclide_clear

View file

@ -1,5 +1,7 @@
module bank_header
use, intrinsic :: ISO_C_BINDING
implicit none
!===============================================================================
@ -8,16 +10,12 @@ module bank_header
! stored with less memory
!===============================================================================
type Bank
! The 'sequence' attribute is used here to ensure that the data listed
! appears in the given order. This is important for MPI purposes when bank
! sites are sent from one processor to another.
sequence
real(8) :: wgt ! weight of bank site
real(8) :: xyz(3) ! location of bank particle
real(8) :: uvw(3) ! diretional cosines
real(8) :: E ! energy
type, bind(C) :: Bank
real(C_DOUBLE) :: wgt ! weight of bank site
real(C_DOUBLE) :: xyz(3) ! location of bank particle
real(C_DOUBLE) :: uvw(3) ! diretional cosines
real(C_DOUBLE) :: E ! energy
integer(C_INT) :: delayed_group ! delayed group
end type Bank
end module bank_header

View file

@ -57,7 +57,7 @@ contains
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes,&
matching_bins
use mesh, only: mesh_indices_to_bin
use mesh_header, only: StructuredMesh
use mesh_header, only: RegularMesh
use string, only: to_str
use tally_header, only: TallyObject
@ -79,8 +79,8 @@ contains
integer :: i_filter_eout ! index for outgoing energy filter
integer :: i_filter_surf ! index for surface filter
real(8) :: flux ! temp variable for flux
type(TallyObject), pointer :: t => null() ! pointer for tally object
type(StructuredMesh), pointer :: m => null() ! pointer for mesh object
type(TallyObject), pointer :: t ! pointer for tally object
type(RegularMesh), pointer :: m ! pointer for mesh object
! Extract spatial and energy indices from object
nx = cmfd % indices(1)

View file

@ -217,7 +217,7 @@ contains
use error, only: warning, fatal_error
use global, only: meshes, source_bank, work, n_user_meshes, cmfd, &
master
use mesh_header, only: StructuredMesh
use mesh_header, only: RegularMesh
use mesh, only: count_bank_sites, get_mesh_indices
use search, only: binary_search
use string, only: to_str
@ -239,8 +239,7 @@ contains
integer :: n_groups ! number of energy groups
logical :: outside ! any source sites outside mesh
logical :: in_mesh ! source site is inside mesh
type(StructuredMesh), pointer :: m ! point to mesh
type(RegularMesh), pointer :: m ! point to mesh
! Associate pointer
m => meshes(n_user_meshes + 1)

Some files were not shown because too many files have changed in this diff Show more