Merge branch 'develop' into photon-new

This commit is contained in:
Paul Romano 2017-11-11 14:28:38 -06:00
commit 970fc566a8
150 changed files with 11993 additions and 7991 deletions

4
.gitignore vendored
View file

@ -95,3 +95,7 @@ examples/jupyter/plots
*.c
*.html
*.so
.cache/
.tox/
.python-version

View file

@ -28,21 +28,7 @@ matrix:
env: OPENMC_CONFIG="check_source"
before_install:
# ============== Handle Python third-party packages ==============
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh;
else
wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh;
fi
- bash miniconda.sh -b -p $HOME/miniconda
- export PATH="$HOME/miniconda/bin:$PATH"
- hash -r
- conda config --set always_yes yes --set changeps1 no
- conda update -q conda
- conda info -a
- if [[ $OPENMC_CONFIG != "check_source" ]]; then
conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION six numpy scipy h5py=2.5 pandas;
source activate test-environment;
sudo add-apt-repository ppa:nschloe/hdf5-backports -y;
sudo apt-get update -q;
sudo apt-get install libhdf5-serial-dev libhdf5-mpich-dev -y;
@ -52,7 +38,11 @@ before_install:
export HDF5_DIR=/usr;
fi
install: true
install:
- if [[ $OPENMC_CONFIG != "check_source" ]]; then
pip install numpy cython;
pip install -e .[test];
fi
before_script:
- if [[ $OPENMC_CONFIG != "check_source" ]]; then
@ -71,6 +61,7 @@ script:
- if [[ $OPENMC_CONFIG == "check_source" ]]; then
./check_source.py;
else
./run_tests.py -C $OPENMC_CONFIG -j 2;
./run_tests.py -C $OPENMC_CONFIG -j 2 &&
pytest --cov=../openmc unit_tests/;
fi
- cd ..

View file

@ -119,8 +119,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
list(APPEND f90flags -cpp -std=f2008ts -fbacktrace -O2)
if(debug)
list(REMOVE_ITEM f90flags -O2)
list(APPEND f90flags -g -Wall -pedantic -fbounds-check
-ffpe-trap=invalid,overflow,underflow)
list(APPEND f90flags -g -Wall -Wno-unused-dummy-argument -pedantic
-fbounds-check -ffpe-trap=invalid,overflow,underflow)
list(APPEND ldflags -g)
endif()
if(profile)
@ -320,10 +320,8 @@ set(LIBOPENMC_FORTRAN_SRC
src/endf_header.F90
src/energy_distribution.F90
src/error.F90
src/finalize.F90
src/geometry.F90
src/geometry_header.F90
src/global.F90
src/hdf5_interface.F90
src/initialize.F90
src/input_xml.F90
@ -362,6 +360,8 @@ set(LIBOPENMC_FORTRAN_SRC
src/secondary_nbody.F90
src/secondary_uncorrelated.F90
src/set_header.F90
src/settings.F90
src/simulation_header.F90
src/simulation.F90
src/source.F90
src/source_header.F90
@ -370,21 +370,37 @@ set(LIBOPENMC_FORTRAN_SRC
src/string.F90
src/summary.F90
src/surface_header.F90
src/tally.F90
src/tally_filter.F90
src/tally_filter_header.F90
src/tally_header.F90
src/tally_initialize.F90
src/timer_header.F90
src/tracking.F90
src/track_output.F90
src/trigger.F90
src/trigger_header.F90
src/urr_header.F90
src/vector_header.F90
src/volume_calc.F90
src/volume_header.F90
src/xml_interface.F90)
src/xml_interface.F90
src/tallies/tally.F90
src/tallies/tally_derivative_header.F90
src/tallies/tally_filter.F90
src/tallies/tally_filter_header.F90
src/tallies/tally_filter_azimuthal.F90
src/tallies/tally_filter_cell.F90
src/tallies/tally_filter_cellborn.F90
src/tallies/tally_filter_cellfrom.F90
src/tallies/tally_filter_delayedgroup.F90
src/tallies/tally_filter_distribcell.F90
src/tallies/tally_filter_energy.F90
src/tallies/tally_filter_energyfunc.F90
src/tallies/tally_filter_material.F90
src/tallies/tally_filter_mesh.F90
src/tallies/tally_filter_mu.F90
src/tallies/tally_filter_particle.F90
src/tallies/tally_filter_polar.F90
src/tallies/tally_filter_surface.F90
src/tallies/tally_filter_universe.F90
src/tallies/tally_header.F90
src/tallies/trigger.F90
src/tallies/trigger_header.F90
)
add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC})
set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc)
add_executable(${program} src/main.F90)
@ -415,7 +431,7 @@ target_link_libraries(${program} ${ldflags} libopenmc)
add_custom_command(TARGET libopenmc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
$<TARGET_FILE:libopenmc>
${CMAKE_CURRENT_SOURCE_DIR}/openmc/_$<TARGET_FILE_NAME:libopenmc>
${CMAKE_CURRENT_SOURCE_DIR}/openmc/capi/$<TARGET_FILE_NAME:libopenmc>
COMMENT "Copying libopenmc to Python module directory")
#===============================================================================
@ -455,6 +471,11 @@ file(GLOB_RECURSE TESTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/test_*.py)
# Loop through all the tests
foreach(test ${TESTS})
# Remove unit tests
if(test MATCHES ".*unit_tests.*")
continue()
endif()
# Get test information
get_filename_component(TEST_NAME ${test} NAME)
get_filename_component(TEST_PATH ${test} PATH)

View file

@ -1,25 +0,0 @@
## This file should be placed in the root directory of your project.
## Then modify the CMakeLists.txt file in the root directory of your
## project to incorporate the testing dashboard.
## # The following are required to uses Dart and the Cdash dashboard
## ENABLE_TESTING()
## INCLUDE(CTest)
# Generic information about CDASH site
set(CTEST_PROJECT_NAME "OpenMC")
set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "openmc.mit.edu")
set(CTEST_DROP_LOCATION "/cdash/submit.php?project=OpenMC")
set(CTEST_DROP_SITE_CDASH TRUE)
# Set file size larger to see more output
set(CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE "20000")
set(CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE "20000")
# User/password to CDASH site
# Please contact Nick Horelik <nhorelik@mit.edu> or
# Bryan Herman <bherman@mit.edu> if you want to push
# test suite information to our CDASH site.
set(CTEST_DROP_SITE_USER "")
set(CTEST_DROP_SITE_PASSWORD "")

35
MANIFEST.in Normal file
View file

@ -0,0 +1,35 @@
include CMakeLists.txt
include LICENSE
include schemas.xml
include openmc/data/reconstruct.pyx
include docs/source/_templates/layout.html
include docs/sphinxext/LICENSE
recursive-include . *.rst
recursive-include cmake *.cmake
recursive-include docs *.css
recursive-include docs *.dia
recursive-include docs *.png
recursive-include docs *.py
recursive-include docs *.svg
recursive-include docs *.tex
recursive-include docs *.txt
recursive-include docs Makefile
recursive-include examples *.h5
recursive-include examples *.ipynb
recursive-include examples *.png
recursive-include examples *.py
recursive-include examples *.xml
recursive-include man *.1
recursive-include src *.F90
recursive-include src *.c
recursive-include src *.cc
recursive-include src *.cpp
recursive-include src *.h
recursive-include src *.hpp
recursive-include src *.rnc
recursive-include src *.rng
recursive-include tests *.dat
recursive-include tests *.h5
recursive-include tests *.py
recursive-include tests *.xml
prune docs/build

View file

@ -1,14 +0,0 @@
all:
mkdir -p build
cmake -H. -Bbuild
make -s -C build
clean:
make -s -C build clean
distclean:
rm -fr build
test:
make -s -C build test
install:
make -s -C build install
.PHONY: all clean distclean test install

View file

@ -4,21 +4,32 @@
C API
=====
.. c:function:: void openmc_calculate_voumes()
.. c:function:: void openmc_calculate_volumes()
Run a stochastic volume calculation
.. c:function:: int openmc_cell_set_temperature(int id, double T, int* instance)
.. c:function:: int openmc_cell_get_id(int32_t index, int32_t* id)
Get the ID of a cell
:param index: Index in the cells array
:type index: int32_t
:param id: ID of the cell
:type id: int32_t*
:return: Return status (negative if an error occurred)
:rtype: int
.. c:function:: int openmc_cell_set_temperature(index index, double T, int32_t* instance)
Set the temperature of a cell.
:param id: ID of the cell
:type id: int
:param index: Index in the cells array
:type index: int32_t
:param T: Temperature in Kelvin
:type T: double
:param instance: Which instance of the cell. To set the temperature for all
instances, pass a null pointer.
:type instance: int*
:type instance: int32_t*
:return: Return status (negative if an error occurred)
:rtype: int
@ -26,7 +37,7 @@ C API
Finalize a simulation
.. c:function:: void openmc_find(double* xyz, int rtype, int* id, int* instance)
.. c:function:: void openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance)
Determine the ID of the cell/material containing a given point
@ -36,10 +47,65 @@ C API
:type rtype: int
:param id: ID of the cell/material found. If a material is requested and the
point is in a void, the ID is 0. If an error occurs, the ID is -1.
:type id: int
:type id: int32_t*
:param instance: If a cell is repetaed in the geometry, the instance of the
cell that was found and zero otherwise.
:type instance: int
:type instance: int32_t*
.. c:function:: int openmc_get_cell_index(int32_t id, int32_t* index)
Get the index in the cells array for a cell with a given ID
:param id: ID of the cell
:type id: int32_t
:param index: Index in the cells array
:type index: int32_t*
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_get_keff(double k_combined[])
:param k_combined: Combined estimate of k-effective
:type k_combined: double[2]
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_get_nuclide_index(char name[], int* index)
Get the index in the nuclides array for a nuclide with a given name
:param name: Name of the nuclide
:type name: char[]
:param index: Index in the nuclides array
:type index: int*
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_get_tally_index(int32_t id, int32_t* index)
Get the index in the tallies array for a tally with a given ID
:param id: ID of the tally
:type id: int32_t
:param index: Index in the tallies array
:type index: int32_t*
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_get_material_index(int32_t id, int32_t* index)
Get the index in the materials array for a material with a given ID
:param id: ID of the material
:type id: int32_t
:param index: Index in the materials array
:type index: int32_t*
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: void openmc_hard_reset()
Reset tallies, timers, and pseudo-random number generator state
.. c:function:: void openmc_init(int intracomm)
@ -57,13 +123,13 @@ C API
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_material_add_nuclide(int id, char name[], double density)
.. c:function:: int openmc_material_add_nuclide(int32_t index, char name[], double density)
Add a nuclide to an existing material. If the nuclide already exists, the
density is overwritten.
:param id: ID of the material
:type id: int
:param index: Index in the materials array
:type index: int32_t
:param name: Name of the nuclide
:type name: char[]
:param density: Density in atom/b-cm
@ -71,28 +137,67 @@ C API
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_material_get_densities(int id, double* ptr)
.. c:function:: int openmc_material_get_densities(int32_t index, int* nuclides[], double* densities[])
Get an array of nuclide densities for a material.
Get density for each nuclide in a material.
:param id: ID of the material
:type id: int
:param ptr: Pointer to the array of densities
:type ptr: double*
:return: Length of the array
:param index: Index in the materials array
:type index: int32_t
:param nuclides: Pointer to array of nuclide indices
:type nuclides: int**
:param densities: Pointer to the array of densities
:type densities: double**
:param n: Length of the array
:type n: int
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_material_set_density(int id, double density)
.. c:function:: int openmc_material_get_id(int32_t index, int32_t* id)
Get the ID of a material
:param index: Index in the materials array
:type index: int32_t
:param id: ID of the material
:type id: int32_t*
:return: Return status (negative if an error occurred)
:rtype: int
.. c:function:: int openmc_material_set_density(int32_t index, double density)
Set the density of a material.
:param id: ID of the material
:type id: int
:param index: Index in the materials array
:type index: int32_t
:param density: Density of the material in atom/b-cm
:type density: double
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_material_set_densities(int32_t, n, char* name[], double density[])
:param index: Index in the materials array
:type index: int32_t
:param n: Length of name/density
:type n: int
:param name: Array of nuclide names
:type name: char**
:param density: Array of densities
:type density: double[]
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: int openmc_nuclide_name(int index, char* name[])
Get name of a nuclide
:param index: Index in the nuclides array
:type index: int
:param name: Name of the nuclide
:type name: char**
:return: Return status (negative if an error occurs)
:rtype: int
.. c:function:: void openmc_plot_geometry()
Run plotting mode.
@ -105,13 +210,52 @@ C API
Run a simulation
.. c:function:: void openmc_tally_results(int id, double** ptr, int shape_[3])
.. c:function:: int openmc_tally_get_id(int32_t index, int32_t* id)
Get the ID of a tally
:param index: Index in the tallies array
:type index: int32_t
:param id: ID of the tally
:type id: int32_t*
:return: Return status (negative if an error occurred)
:rtype: int
.. c:function:: int openmc_tally_get_nuclides(int32_t index, int* nuclides[], int* n)
Get nuclides specified in a tally
:param index: Index in the tallies array
:type index: int32_t
:param nuclides: Array of nuclide indices
:type nuclides: int**
:param n: Number of nuclides
:type n: int*
:return: Return status (negative if an error occurred)
:rtype: int
.. c:function:: int openmc_tally_results(int32_t index, double** ptr, int shape_[3])
Get a pointer to tally results array.
:param id: ID of the tally
:type id: int
:param index: Index in the tallies array
:type index: int32_t
:param ptr: Pointer to the results array
:type ptr: double**
:param shape_: Shape of the results array
:type shape_: int[3]
:return: Return status (negative if an error occurred)
:rtype: int
.. c:function:: int openmc_tally_set_nuclides(int32_t index, int n, char* nuclides[])
Set the nuclides for a tally
:param index: Index in the tallies array
:type index: int32_t
:param n: Number of nuclides
:type n: int
:param nuclides: Array of nuclide names
:type nuclides: char**
:return: Return status (negative if an error occurred)
:rtype: int

View file

@ -123,9 +123,9 @@ to the scored values. The ``filter`` element has the following
attributes/sub-elements:
:type:
The type of the filter. Accepted options are "cell", "cellborn",
"material", "universe", "energy", "energyout", "mu", "polar",
"azimuthal", "mesh", "distribcell", "delayedgroup", and
The type of the filter. Accepted options are "cell", "cellfrom",
"cellborn", "surface", "material", "universe", "energy", "energyout", "mu",
"polar", "azimuthal", "mesh", "distribcell", "delayedgroup", and
"energyfunction".
:bins:
@ -154,14 +154,31 @@ For each filter type, the following table describes what the ``bins`` attribute
should be set to:
:cell:
A list of unique IDs for cells in which the tally should be accumulated.
A list of unique IDs for cells in which the tally should be
accumulated.
:surface:
This filter allows the tally to be scored when crossing a surface. A list of
surface IDs should be given. By default, net currents are tallied, and to
tally a partial current from one cell to another, this should be used in
combination with a cell or cell_from filter that defines the other cell.
This filter should not be used in combination with a meshfilter.
:cellfrom:
This filter allows the tally to be scored when crossing a surface and the
particle came from a specified cell. A list of cell IDs should be
given.
To tally a partial current from a cell to another, this filter should be
used in combination with a cell filter, to define the other cell.
This filter should not be used in combination with a meshfilter.
:cellborn:
This filter allows the tally to be scored to only when particles were
originally born in a specified cell. A list of cell IDs should be given.
originally born in a specified cell. A list of cell IDs should be
given.
:material:
A list of unique IDs for matreials in which the tally should be accumulated.
A list of unique IDs for materials in which the tally should be accumulated.
:universe:
A list of unique IDs for universes in which the tally should be accumulated.

View file

@ -117,6 +117,7 @@ Constructing Tallies
openmc.UniverseFilter
openmc.MaterialFilter
openmc.CellFilter
openmc.CellFromFilter
openmc.CellbornFilter
openmc.SurfaceFilter
openmc.MeshFilter

View file

@ -2,16 +2,40 @@
:data:`openmc.capi` -- Python bindings to the C API
---------------------------------------------------
.. automodule:: openmc.capi
Functions
---------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myfunction.rst
openmc.capi.lib_context
openmc.capi.calculate_volumes
openmc.capi.finalize
openmc.capi.find_cell
openmc.capi.find_material
openmc.capi.hard_reset
openmc.capi.init
openmc.capi.keff
openmc.capi.load_nuclide
openmc.capi.plot_geometry
openmc.capi.reset
openmc.capi.run
openmc.capi.run_in_memory
Classes
-------
.. autosummary::
:toctree: generated
:nosignatures:
:template: myclass.rst
openmc.capi.OpenMCLibrary
openmc.capi.Cell
openmc.capi.EnergyFilter
openmc.capi.MaterialFilter
openmc.capi.Material
openmc.capi.Nuclide
openmc.capi.Tally

View file

@ -414,16 +414,19 @@ distributions.
various HDF5 files, h5py is needed to provide access to data within these
files from Python.
.. admonition:: Optional
:class: note
`Matplotlib <http://matplotlib.org/>`_
Matplotlib is used to providing plotting functionality in the API like the
:meth:`Universe.plot` method and the :func:`openmc.plot_xs` function.
`uncertainties <https://pythonhosted.org/uncertainties/>`_
Uncertainties are optionally used for decay data in the :mod:`openmc.data`
module.
Uncertainties are used for decay data in the :mod:`openmc.data` module.
`lxml <http://lxml.de/>`_
lxml is used for the :ref:`scripts_validate` script and various other
parts of the Python API.
.. admonition:: Optional
:class: note
`Cython <http://cython.org/>`_
Cython is used for resonance reconstruction for ENDF data converted to
@ -437,8 +440,8 @@ distributions.
The silomesh package is needed to convert voxel and track files to SILO
format.
`lxml <http://lxml.de/>`_
lxml is used for the :ref:`scripts_validate` script.
`pytest <https://docs.pytest.org>`_
The pytest framework is used for unit testing the Python API.
.. _usersguide_nxml:

View file

@ -261,12 +261,19 @@ The following tables show all valid scores:
+----------------------+---------------------------------------------------+
|Score | Description |
+======================+===================================================+
|current |Partial currents on the boundaries of each cell in |
| |a mesh. Units are particles per source |
| |particle. Note that this score can only be used if |
| |a mesh filter has been specified. Furthermore, it |
| |may not be used in conjunction with any other |
| |score. |
|current |Used in combination with a mesh filter: |
| |Partial currents on the boundaries of each cell in |
| |a mesh. It may not be used in conjunction with any |
| |other score. Only energy and mesh filters may be |
| |used. |
| |Used in combination with a surface filter: |
| |Net currents on any surface previously defined in |
| |the geometry. It may be used along with any other |
| |filter, except mesh filters. |
| |Surfaces can alternatively be defined with cell |
| |from and cell filters thereby resulting in tallying|
| |partial currents. |
| |Units are particles per source particle. |
+----------------------+---------------------------------------------------+
|events |Number of scoring events. Units are events per |
| |source particle. |

View file

@ -131,7 +131,7 @@
"outputs": [],
"source": [
"# Instantiate a Materials collection\n",
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
"materials_file = openmc.Materials([fuel, water, zircaloy])\n",
"\n",
"# Export to \"materials.xml\"\n",
"materials_file.export_to_xml()"

View file

@ -134,7 +134,7 @@
"outputs": [],
"source": [
"# Instantiate a Materials object\n",
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
"materials_file = openmc.Materials([fuel, water, zircaloy])\n",
"\n",
"# Export to \"materials.xml\"\n",
"materials_file.export_to_xml()"

File diff suppressed because one or more lines are too long

View file

@ -79,7 +79,7 @@
"outputs": [],
"source": [
"# Instantiate a Materials collection\n",
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
"materials_file = openmc.Materials([fuel, water, zircaloy])\n",
"\n",
"# Export to \"materials.xml\"\n",
"materials_file.export_to_xml()"

View file

@ -105,7 +105,7 @@
"outputs": [],
"source": [
"# Instantiate a Materials collection\n",
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
"materials_file = openmc.Materials([fuel, water, zircaloy])\n",
"\n",
"# Export to \"materials.xml\"\n",
"materials_file.export_to_xml()"

View file

@ -71,7 +71,7 @@
" water.add_element('B', ppm_Boron * 1E-6)\n",
" \n",
" # Instantiate a Materials object\n",
" materials = openmc.Materials((fuel, zircaloy, water))\n",
" materials = openmc.Materials([fuel, zircaloy, water])\n",
" \n",
" # Create cylinders for the fuel and clad\n",
" fuel_outer_radius = openmc.ZCylinder(R=0.39218)\n",

View file

@ -11,7 +11,7 @@
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -19,7 +19,6 @@
"\n",
"from IPython.display import Image\n",
"import numpy as np\n",
"\n",
"import openmc"
]
},
@ -65,7 +64,7 @@
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -100,12 +99,12 @@
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
"# Instantiate a Materials collection\n",
"materials_file = openmc.Materials((fuel, water, zircaloy))\n",
"materials_file = openmc.Materials([fuel, water, zircaloy])\n",
"\n",
"# Export to \"materials.xml\"\n",
"materials_file.export_to_xml()"
@ -122,7 +121,7 @@
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -151,7 +150,7 @@
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -188,7 +187,7 @@
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -215,7 +214,7 @@
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -228,7 +227,7 @@
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -283,7 +282,7 @@
"cell_type": "code",
"execution_count": 11,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -310,9 +309,7 @@
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
@ -333,13 +330,11 @@
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX////pgJFyEhJNv8RV\nUZDeAAAAAWJLR0QAiAUdSAAAAAd0SU1FB+EEDRUZLksd2dYAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMDQtMTNUMTc6MjU6\nNDUtMDQ6MDCJ1tNgAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTA0LTEzVDE3OjI1OjQ1LTA0OjAw\n+Itr3AAAAABJRU5ErkJggg==\n",
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+EKGA0jE/weoLoAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTAtMjRUMTM6MzU6\nMTktMDU6MDCdcfAWAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEwLTI0VDEzOjM1OjE5LTA1OjAw\n7CxIqgAAAABJRU5ErkJggg==\n",
"text/plain": [
"<IPython.core.display.Image object>"
]
@ -368,7 +363,7 @@
"cell_type": "code",
"execution_count": 14,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -380,7 +375,7 @@
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -457,7 +452,7 @@
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -472,7 +467,7 @@
"cell_type": "code",
"execution_count": 18,
"metadata": {
"collapsed": false
"collapsed": true
},
"outputs": [],
"source": [
@ -522,10 +517,25 @@
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false
},
"outputs": [],
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=1.\n",
" warn(msg, IDWarning)\n",
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another MeshFilter instance already exists with id=5.\n",
" warn(msg, IDWarning)\n",
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=6.\n",
" warn(msg, IDWarning)\n",
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=3.\n",
" warn(msg, IDWarning)\n",
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=2.\n",
" warn(msg, IDWarning)\n"
]
}
],
"source": [
"# Export to \"tallies.xml\"\n",
"tallies_file.export_to_xml()"
@ -542,7 +552,6 @@
"cell_type": "code",
"execution_count": 22,
"metadata": {
"collapsed": false,
"scrolled": true
},
"outputs": [
@ -578,25 +587,24 @@
" | The OpenMC Monte Carlo Code\n",
" Copyright | 2011-2017 Massachusetts Institute of Technology\n",
" License | http://openmc.readthedocs.io/en/latest/license.html\n",
" Version | 0.8.0\n",
" Git SHA1 | bc4683be1c853fe6d0e31bccad416ef219e3efaf\n",
" Date/Time | 2017-04-13 17:26:05\n",
" MPI Processes | 1\n",
" OpenMP Threads | 1\n",
" Version | 0.9.0\n",
" Git SHA1 | 5ca1d06b0c6ac3b56060ef289b7e5215210e7332\n",
" Date/Time | 2017-10-24 13:35:19\n",
" OpenMP Threads | 4\n",
"\n",
" Reading settings XML file...\n",
" Reading geometry XML file...\n",
" Reading materials XML file...\n",
" Reading cross sections XML file...\n",
" Reading U235 from /home/smharper/openmc/data/nndc_hdf5/U235.h5\n",
" Reading U238 from /home/smharper/openmc/data/nndc_hdf5/U238.h5\n",
" Reading O16 from /home/smharper/openmc/data/nndc_hdf5/O16.h5\n",
" Reading H1 from /home/smharper/openmc/data/nndc_hdf5/H1.h5\n",
" Reading B10 from /home/smharper/openmc/data/nndc_hdf5/B10.h5\n",
" Reading Zr90 from /home/smharper/openmc/data/nndc_hdf5/Zr90.h5\n",
" Reading geometry XML file...\n",
" Building neighboring cells lists for each surface...\n",
" Reading U235 from /home/romano/openmc/scripts/nndc_hdf5/U235.h5\n",
" Reading U238 from /home/romano/openmc/scripts/nndc_hdf5/U238.h5\n",
" Reading O16 from /home/romano/openmc/scripts/nndc_hdf5/O16.h5\n",
" Reading H1 from /home/romano/openmc/scripts/nndc_hdf5/H1.h5\n",
" Reading B10 from /home/romano/openmc/scripts/nndc_hdf5/B10.h5\n",
" Reading Zr90 from /home/romano/openmc/scripts/nndc_hdf5/Zr90.h5\n",
" Maximum neutron transport energy: 2.00000E+07 eV for U235\n",
" Reading tallies XML file...\n",
" Building neighboring cells lists for each surface...\n",
" Initializing source particles...\n",
"\n",
" ====================> K EIGENVALUE SIMULATION <====================\n",
@ -627,20 +635,20 @@
"\n",
" =======================> TIMING STATISTICS <=======================\n",
"\n",
" Total time for initialization = 2.2576E-01 seconds\n",
" Reading cross sections = 1.8005E-01 seconds\n",
" Total time in simulation = 9.6105E+00 seconds\n",
" Time in transport only = 9.5952E+00 seconds\n",
" Time in inactive batches = 1.4678E+00 seconds\n",
" Time in active batches = 8.1427E+00 seconds\n",
" Time synchronizing fission bank = 2.6325E-03 seconds\n",
" Sampling source sites = 1.5038E-03 seconds\n",
" SEND/RECV source sites = 8.8069E-04 seconds\n",
" Time accumulating tallies = 2.0568E-04 seconds\n",
" Total time for finalization = 1.5435E-03 seconds\n",
" Total time elapsed = 9.8506E+00 seconds\n",
" Calculation Rate (inactive) = 8516.30 neutrons/second\n",
" Calculation Rate (active) = 4605.35 neutrons/second\n",
" Total time for initialization = 4.1497E-01 seconds\n",
" Reading cross sections = 3.6232E-01 seconds\n",
" Total time in simulation = 3.6447E+00 seconds\n",
" Time in transport only = 3.5939E+00 seconds\n",
" Time in inactive batches = 4.4241E-01 seconds\n",
" Time in active batches = 3.2022E+00 seconds\n",
" Time synchronizing fission bank = 2.7734E-03 seconds\n",
" Sampling source sites = 1.1981E-03 seconds\n",
" SEND/RECV source sites = 1.5506E-03 seconds\n",
" Time accumulating tallies = 1.2237E-04 seconds\n",
" Total time for finalization = 1.4924E-03 seconds\n",
" Total time elapsed = 4.0823E+00 seconds\n",
" Calculation Rate (inactive) = 28254.0 neutrons/second\n",
" Calculation Rate (active) = 11710.5 neutrons/second\n",
"\n",
" ============================> RESULTS <============================\n",
"\n",
@ -689,7 +697,7 @@
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": false,
"collapsed": true,
"scrolled": true
},
"outputs": [],
@ -710,14 +718,25 @@
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -777,14 +796,25 @@
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -803,7 +833,7 @@
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>total</td>\n",
" <td>(absorption + current)</td>\n",
" <td>((absorption + current) / (absorption + current))</td>\n",
" <td>0.694368</td>\n",
" <td>0.004606</td>\n",
" </tr>\n",
@ -812,11 +842,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 0.00e+00 6.25e-01 total (absorption + current) 6.94e-01 \n",
" energy low [eV] energy high [eV] nuclide \\\n",
"0 0.00e+00 6.25e-01 total \n",
"\n",
" std. dev. \n",
"0 4.61e-03 "
" score mean std. dev. \n",
"0 ((absorption + current) / (absorption + current)) 6.94e-01 4.61e-03 "
]
},
"execution_count": 25,
@ -845,14 +875,25 @@
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -871,7 +912,7 @@
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>total</td>\n",
" <td>nu-fission</td>\n",
" <td>(nu-fission / nu-fission)</td>\n",
" <td>1.203099</td>\n",
" <td>0.009615</td>\n",
" </tr>\n",
@ -880,8 +921,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] nuclide score mean std. dev.\n",
"0 0.00e+00 6.25e-01 total nu-fission 1.20e+00 9.61e-03"
" energy low [eV] energy high [eV] nuclide score \\\n",
"0 0.00e+00 6.25e-01 total (nu-fission / nu-fission) \n",
"\n",
" mean std. dev. \n",
"0 1.20e+00 9.61e-03 "
]
},
"execution_count": 26,
@ -908,14 +952,25 @@
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -934,9 +989,9 @@
" <th>0</th>\n",
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>total</td>\n",
" <td>absorption</td>\n",
" <td>(absorption / absorption)</td>\n",
" <td>0.749423</td>\n",
" <td>0.006089</td>\n",
" </tr>\n",
@ -945,11 +1000,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] cell nuclide score mean \\\n",
"0 0.00e+00 6.25e-01 10000 total absorption 7.49e-01 \n",
" energy low [eV] energy high [eV] cell nuclide score \\\n",
"0 0.00e+00 6.25e-01 1 total (absorption / absorption) \n",
"\n",
" std. dev. \n",
"0 6.09e-03 "
" mean std. dev. \n",
"0 7.49e-01 6.09e-03 "
]
},
"execution_count": 27,
@ -974,14 +1029,25 @@
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1000,7 +1066,7 @@
" <th>0</th>\n",
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>total</td>\n",
" <td>(nu-fission / absorption)</td>\n",
" <td>1.663727</td>\n",
@ -1011,11 +1077,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] cell nuclide \\\n",
"0 0.00e+00 6.25e-01 10000 total \n",
" energy low [eV] energy high [eV] cell nuclide score \\\n",
"0 0.00e+00 6.25e-01 1 total (nu-fission / absorption) \n",
"\n",
" score mean std. dev. \n",
"0 (nu-fission / absorption) 1.66e+00 1.44e-02 "
" mean std. dev. \n",
"0 1.66e+00 1.44e-02 "
]
},
"execution_count": 28,
@ -1039,14 +1105,25 @@
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1065,7 +1142,7 @@
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>total</td>\n",
" <td>(absorption + current)</td>\n",
" <td>((absorption + current) / (absorption + current))</td>\n",
" <td>0.984668</td>\n",
" <td>0.005509</td>\n",
" </tr>\n",
@ -1074,11 +1151,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 0.00e+00 6.25e-01 total (absorption + current) 9.85e-01 \n",
" energy low [eV] energy high [eV] nuclide \\\n",
"0 0.00e+00 6.25e-01 total \n",
"\n",
" std. dev. \n",
"0 5.51e-03 "
" score mean std. dev. \n",
"0 ((absorption + current) / (absorption + current)) 9.85e-01 5.51e-03 "
]
},
"execution_count": 29,
@ -1101,14 +1178,25 @@
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1163,14 +1251,25 @@
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1189,9 +1288,9 @@
" <th>0</th>\n",
" <td>0.0</td>\n",
" <td>0.625</td>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>total</td>\n",
" <td>((((((absorption + current) * nu-fission) * ab...</td>\n",
" <td>(((((((absorption + current) / (absorption + c...</td>\n",
" <td>1.023002</td>\n",
" <td>0.018791</td>\n",
" </tr>\n",
@ -1200,11 +1299,11 @@
"</div>"
],
"text/plain": [
" energy low [eV] energy high [eV] cell nuclide \\\n",
"0 0.00e+00 6.25e-01 10000 total \n",
" energy low [eV] energy high [eV] cell nuclide \\\n",
"0 0.00e+00 6.25e-01 1 total \n",
"\n",
" score mean std. dev. \n",
"0 ((((((absorption + current) * nu-fission) * ab... 1.02e+00 1.88e-02 "
"0 (((((((absorption + current) / (absorption + c... 1.02e+00 1.88e-02 "
]
},
"execution_count": 31,
@ -1230,7 +1329,7 @@
"cell_type": "code",
"execution_count": 32,
"metadata": {
"collapsed": false,
"collapsed": true,
"scrolled": true
},
"outputs": [],
@ -1245,14 +1344,25 @@
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1269,7 +1379,7 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>(U238 / total)</td>\n",
@ -1279,7 +1389,7 @@
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>(U238 / total)</td>\n",
@ -1289,7 +1399,7 @@
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>(U235 / total)</td>\n",
@ -1299,7 +1409,7 @@
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>(U235 / total)</td>\n",
@ -1309,7 +1419,7 @@
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>(U238 / total)</td>\n",
@ -1319,7 +1429,7 @@
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>(U238 / total)</td>\n",
@ -1329,7 +1439,7 @@
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>(U235 / total)</td>\n",
@ -1339,7 +1449,7 @@
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>(U235 / total)</td>\n",
@ -1352,15 +1462,15 @@
"</div>"
],
"text/plain": [
" cell energy low [eV] energy high [eV] nuclide \\\n",
"0 10000 0.00e+00 6.25e-01 (U238 / total) \n",
"1 10000 0.00e+00 6.25e-01 (U238 / total) \n",
"2 10000 0.00e+00 6.25e-01 (U235 / total) \n",
"3 10000 0.00e+00 6.25e-01 (U235 / total) \n",
"4 10000 6.25e-01 2.00e+07 (U238 / total) \n",
"5 10000 6.25e-01 2.00e+07 (U238 / total) \n",
"6 10000 6.25e-01 2.00e+07 (U235 / total) \n",
"7 10000 6.25e-01 2.00e+07 (U235 / total) \n",
" cell energy low [eV] energy high [eV] nuclide \\\n",
"0 1 0.00e+00 6.25e-01 (U238 / total) \n",
"1 1 0.00e+00 6.25e-01 (U238 / total) \n",
"2 1 0.00e+00 6.25e-01 (U235 / total) \n",
"3 1 0.00e+00 6.25e-01 (U235 / total) \n",
"4 1 6.25e-01 2.00e+07 (U238 / total) \n",
"5 1 6.25e-01 2.00e+07 (U238 / total) \n",
"6 1 6.25e-01 2.00e+07 (U235 / total) \n",
"7 1 6.25e-01 2.00e+07 (U235 / total) \n",
"\n",
" score mean std. dev. \n",
"0 (nu-fission / flux) 6.66e-07 5.63e-09 \n",
@ -1393,9 +1503,7 @@
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stdout",
@ -1425,9 +1533,7 @@
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stdout",
@ -1449,9 +1555,7 @@
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"name": "stdout",
@ -1480,14 +1584,25 @@
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1504,7 +1619,7 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>U238</td>\n",
@ -1514,7 +1629,7 @@
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.000</td>\n",
" <td>6.250000e-01</td>\n",
" <td>U235</td>\n",
@ -1524,7 +1639,7 @@
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>U238</td>\n",
@ -1534,7 +1649,7 @@
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10000</td>\n",
" <td>1</td>\n",
" <td>0.625</td>\n",
" <td>2.000000e+07</td>\n",
" <td>U235</td>\n",
@ -1547,11 +1662,11 @@
"</div>"
],
"text/plain": [
" cell energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 10000 0.00e+00 6.25e-01 U238 nu-fission 1.60e-06 \n",
"1 10000 0.00e+00 6.25e-01 U235 nu-fission 8.55e-01 \n",
"2 10000 6.25e-01 2.00e+07 U238 nu-fission 8.30e-02 \n",
"3 10000 6.25e-01 2.00e+07 U235 nu-fission 9.06e-02 \n",
" cell energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 1 0.00e+00 6.25e-01 U238 nu-fission 1.60e-06 \n",
"1 1 0.00e+00 6.25e-01 U235 nu-fission 8.55e-01 \n",
"2 1 6.25e-01 2.00e+07 U238 nu-fission 8.30e-02 \n",
"3 1 6.25e-01 2.00e+07 U235 nu-fission 9.06e-02 \n",
"\n",
" std. dev. \n",
"0 9.68e-09 \n",
@ -1574,14 +1689,25 @@
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"collapsed": false
},
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style>\n",
" .dataframe thead tr:only-child th {\n",
" text-align: right;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: left;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
@ -1598,7 +1724,7 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.000000e-02</td>\n",
" <td>1.080060e-01</td>\n",
" <td>H1</td>\n",
@ -1608,7 +1734,7 @@
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.080060e-01</td>\n",
" <td>1.166529e+00</td>\n",
" <td>H1</td>\n",
@ -1618,7 +1744,7 @@
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.166529e+00</td>\n",
" <td>1.259921e+01</td>\n",
" <td>H1</td>\n",
@ -1628,7 +1754,7 @@
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.259921e+01</td>\n",
" <td>1.360790e+02</td>\n",
" <td>H1</td>\n",
@ -1638,7 +1764,7 @@
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.360790e+02</td>\n",
" <td>1.469734e+03</td>\n",
" <td>H1</td>\n",
@ -1648,7 +1774,7 @@
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.469734e+03</td>\n",
" <td>1.587401e+04</td>\n",
" <td>H1</td>\n",
@ -1658,7 +1784,7 @@
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.587401e+04</td>\n",
" <td>1.714488e+05</td>\n",
" <td>H1</td>\n",
@ -1668,7 +1794,7 @@
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.714488e+05</td>\n",
" <td>1.851749e+06</td>\n",
" <td>H1</td>\n",
@ -1678,7 +1804,7 @@
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>10002</td>\n",
" <td>3</td>\n",
" <td>1.851749e+06</td>\n",
" <td>2.000000e+07</td>\n",
" <td>H1</td>\n",
@ -1691,16 +1817,16 @@
"</div>"
],
"text/plain": [
" cell energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 10002 1.00e-02 1.08e-01 H1 scatter 4.54e+00 \n",
"1 10002 1.08e-01 1.17e+00 H1 scatter 2.00e+00 \n",
"2 10002 1.17e+00 1.26e+01 H1 scatter 1.64e+00 \n",
"3 10002 1.26e+01 1.36e+02 H1 scatter 1.82e+00 \n",
"4 10002 1.36e+02 1.47e+03 H1 scatter 2.03e+00 \n",
"5 10002 1.47e+03 1.59e+04 H1 scatter 2.12e+00 \n",
"6 10002 1.59e+04 1.71e+05 H1 scatter 2.18e+00 \n",
"7 10002 1.71e+05 1.85e+06 H1 scatter 2.01e+00 \n",
"8 10002 1.85e+06 2.00e+07 H1 scatter 3.73e-01 \n",
" cell energy low [eV] energy high [eV] nuclide score mean \\\n",
"0 3 1.00e-02 1.08e-01 H1 scatter 4.54e+00 \n",
"1 3 1.08e-01 1.17e+00 H1 scatter 2.00e+00 \n",
"2 3 1.17e+00 1.26e+01 H1 scatter 1.64e+00 \n",
"3 3 1.26e+01 1.36e+02 H1 scatter 1.82e+00 \n",
"4 3 1.36e+02 1.47e+03 H1 scatter 2.03e+00 \n",
"5 3 1.47e+03 1.59e+04 H1 scatter 2.12e+00 \n",
"6 3 1.59e+04 1.71e+05 H1 scatter 2.18e+00 \n",
"7 3 1.71e+05 1.85e+06 H1 scatter 2.01e+00 \n",
"8 3 1.85e+06 2.00e+07 H1 scatter 3.73e-01 \n",
"\n",
" std. dev. \n",
"0 2.52e-02 \n",
@ -1744,9 +1870,9 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
"version": "3.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 0
"nbformat_minor": 1
}

View file

@ -119,7 +119,7 @@ settings_file.export_to_xml()
###############################################################################
# Instantiate a tally mesh
mesh = openmc.Mesh(mesh_id=1)
mesh = openmc.Mesh()
mesh.type = 'regular'
mesh.dimension = [100, 100, 1]
mesh.lower_left = [-0.62992, -0.62992, -1.e50]

View file

@ -1,21 +1,30 @@
<?xml version="1.0"?>
<tallies>
<filter id="1" type="cell">
<bins>100</bins>
</filter>
<filter id="2" type="energy">
<bins>0 20.0e6</bins>
</filter>
<filter id="3" type="energyout">
<bins>0 20.0e6</bins>
</filter>
<tally id="1">
<filter type="cell" bins="100" />
<filters>1</filters>
<scores>total scatter nu-scatter absorption fission nu-fission</scores>
</tally>
<tally id="2">
<filter type="cell" bins="100" />
<filter type="energy" bins="0 20.0e6" />
<filters>1 2</filters>
<scores>total scatter nu-scatter absorption fission nu-fission</scores>
</tally>
<tally id="3">
<filter type="cell" bins="100" />
<filter type="energy" bins="0 20.0e6" />
<filter type="energyout" bins="0 20.0e6" />
<filters>1 2 3</filters>
<scores>scatter nu-scatter nu-fission</scores>
</tally>

View file

@ -8,8 +8,12 @@
<width>1.0 1.0</width>
</mesh>
<filter id="1" type="mesh">
<bins>1</bins>
</filter>
<tally id="1">
<filter type="mesh" bins="1" />
<filters>1</filters>
<scores>total</scores>
</tally>

View file

@ -8,8 +8,12 @@
<width>1.0 1.0</width>
</mesh>
<filter id="1" type="mesh">
<bins>1</bins>
</filter>
<tally id="1">
<filter type="mesh" bins="1" />
<filters>1</filters>
<scores>total</scores>
</tally>

View file

@ -22,10 +22,11 @@
<!-- To assess convergence of the source distribution, we need to define the
bounds for a mesh over which the Shannon entropy should be
calculated. The extent in the z direction is made arbitrarily large. -->
<entropy>
<mesh id="1">
<lower_left>-0.39218 -0.39218 -1.e50</lower_left>
<upper_right>0.39218 0.39218 1.e50</upper_right>
<dimension>10 10 1</dimension>
</entropy>
</mesh>
<entropy_mesh>1</entropy_mesh>
</settings>

View file

@ -1,15 +1,22 @@
<?xml version="1.0"?>
<tallies>
<mesh id="1" type="regular">
<mesh id="2" 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>
</mesh>
<filter id="1" type="mesh">
<bins>1</bins>
</filter>
<filter id="2" type="energy">
<bins>0. 4. 20.0e6</bins>
</filter>
<tally id="1">
<filter type="mesh" bins="1" />
<filter type="energy" bins="0. 4. 20.0e6" />
<filters>1 2</filters>
<scores>flux fission nu-fission</scores>
</tally>

View file

@ -5,9 +5,14 @@
<lower_left>-0.63 -0.63 -1e+50</lower_left>
<upper_right>0.63 0.63 1e+50</upper_right>
</mesh>
<filter id="1" type="energy">
<bins>1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0</bins>
</filter>
<filter id="2" type="mesh">
<bins>1</bins>
</filter>
<tally id="1" name="tally 1">
<filter bins="1e-05 0.0635 10.0 100.0 1000.0 500000.0 1000000.0 20000000.0" type="energy" />
<filter bins="1" type="mesh" />
<filters>1 2</filters>
<scores>flux fission nu-fission</scores>
</tally>
</tallies>

View file

@ -27,6 +27,5 @@ from openmc.particle_restart import *
from openmc.mixin import *
from openmc.plotter import *
from openmc.search import *
from openmc.capi import *
__version__ = '0.9.0'

View file

@ -108,9 +108,9 @@ class CrossNuclide(object):
Parameters
----------
left_nuclide : Nuclide or CrossNuclide
left_nuclide : openmc.Nuclide or CrossNuclide
The left nuclide in the outer product
right_nuclide : Nuclide or CrossNuclide
right_nuclide : openmc.Nuclide or CrossNuclide
The right nuclide in the outer product
binary_op : str
The tally arithmetic binary operator (e.g., '+', '-', etc.) used to
@ -118,9 +118,9 @@ class CrossNuclide(object):
Attributes
----------
left_nuclide : Nuclide or CrossNuclide
left_nuclide : openmc.Nuclide or CrossNuclide
The left nuclide in the outer product
right_nuclide : Nuclide or CrossNuclide
right_nuclide : openmc.Nuclide or CrossNuclide
The right nuclide in the outer product
binary_op : str
The tally arithmetic binary operator (e.g., '+', '-', etc.) used to
@ -510,7 +510,7 @@ class AggregateNuclide(object):
Parameters
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
nuclides : Iterable of str or openmc.Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used
@ -518,7 +518,7 @@ class AggregateNuclide(object):
Attributes
----------
nuclides : Iterable of str or Nuclide or CrossNuclide
nuclides : Iterable of str or openmc.Nuclide or CrossNuclide
The nuclides included in the aggregation
aggregate_op : str
The tally aggregation operator (e.g., 'sum', 'avg', etc.) used

View file

@ -1,304 +0,0 @@
from contextlib import contextmanager
from ctypes import CDLL, c_int, c_int32, c_double, c_char_p, POINTER
import sys
from warnings import warn
import numpy as np
from numpy.ctypeslib import as_array
import pkg_resources
__all__ = ['OpenMCLibrary', 'lib', 'lib_context']
_int3 = c_int*3
_double3 = c_double*3
_double_array = POINTER(POINTER(c_double))
class OpenMCLibrary(object):
"""Provides bindings to C functions defined by OpenMC shared library.
This class is normally not directly instantiated. Instead, when the
:mod:`openmc` package is imported, an instance is automatically created with
the name :data:`openmc.lib`. Calls to the OpenMC can then be made using that
instance, for example:
.. code-block:: python
openmc.lib.init()
openmc.lib.run()
"""
def __init__(self, filename):
self._dll = CDLL(filename)
# Set argument/return types
self._dll.openmc_calculate_volumes.restype = None
self._dll.openmc_cell_set_temperature.argtypes = [
c_int32, c_double, c_int32]
self._dll.openmc_cell_set_temperature.restype = c_int
self._dll.openmc_finalize.restype = None
self._dll.openmc_find.argtypes = [
POINTER(_double3), c_int, POINTER(c_int32), POINTER(c_int32)]
self._dll.openmc_find.restype = None
self._dll.openmc_init.argtypes = [POINTER(c_int)]
self._dll.openmc_init.restype = None
self._dll.openmc_load_nuclide.argtypes = [c_char_p]
self._dll.openmc_load_nuclide.restype = c_int
self._dll.openmc_material_add_nuclide.argtypes = [
c_int32, c_char_p, c_double]
self._dll.openmc_material_add_nuclide.restype = c_int
self._dll.openmc_material_get_densities.argtypes = [
c_int32, _double_array]
self._dll.openmc_material_get_densities.restype = c_int
self._dll.openmc_material_set_density.argtypes = [c_int32, c_double]
self._dll.openmc_material_set_density.restype = c_int
self._dll.openmc_plot_geometry.restype = None
self._dll.openmc_run.restype = None
self._dll.openmc_reset.restype = None
self._dll.openmc_tally_results.argtypes = [
c_int32, _double_array, POINTER(_int3)]
self._dll.openmc_tally_results.restype = None
def calculate_volumes(self):
"""Run stochastic volume calculation"""
return self._dll.openmc_calculate_volumes()
def cell_set_temperature(self, cell_id, T, instance=None):
"""Set the temperature of a cell
Parameters
----------
cell_id : int
ID of the cell
T : float
Temperature in K
instance : int or None
Which instance of the cell
"""
if instance is not None:
return self._dll.openmc_cell_set_temperature(
cell_id, T, instance)
else:
return self._dll.openmc_cell_set_temperature(cell_id, T, None)
def finalize(self):
"""Finalize simulation and free memory"""
return self._dll.openmc_finalize()
def find(self, xyz, rtype='cell'):
"""Find the cell or material at a given point
Parameters
----------
xyz : iterable of float
Cartesian coordinates of position
rtype : {'cell', 'material'}
Whether to return the cell or material ID
Returns
-------
int or None
ID of the cell or material. If 'material' is requested and no
material exists at the given coordinate, None is returned.
int
If the cell at the given point is repeated in the geometry, this
indicates which instance it is, i.e., 0 would be the first instance.
"""
# Set second argument to openmc_find
if rtype == 'cell':
r_int = 1
elif rtype == 'material':
r_int = 2
else:
raise ValueError('Unknown return type: {}'.format(rtype))
# Call openmc_find
uid = c_int32()
instance = c_int32()
self._dll.openmc_find(_double3(*xyz), r_int, uid, instance)
return (uid.value if uid != 0 else None), instance.value
def init(self, intracomm=None):
"""Initialize OpenMC
Parameters
----------
intracomm : mpi4py.MPI.Intracomm or None
MPI intracommunicator
"""
if intracomm is not None:
# If an mpi4py communicator was passed, convert it to an integer to
# be passed to openmc_init
try:
intracomm = intracomm.py2f()
except AttributeError:
pass
return self._dll.openmc_init(c_int(intracomm))
else:
return self._dll.openmc_init(None)
def load_nuclide(self, name):
"""Load cross section data for a nuclide.
Parameters
----------
name : str
Name of nuclide, e.g. 'U235'
Returns
-------
int
Return status (negative if an error occurs).
"""
return self._dll.openmc_load_nuclide(name.encode())
def material_add_nuclide(self, mat_id, name, density):
"""Add a nuclide to a material.
Parameters
----------
mat_id : int
ID of the material
name : str
Name of nuclide, e.g. 'U235'
density : float
Density in atom/b-cm
Returns
-------
int
Return status (negative if an error occurs).
"""
return self._dll.openmc_material_add_nuclide(
mat_id, name.encode(), density)
def material_get_densities(self, mat_id):
"""Get atom densities in a material.
Parameters
----------
mat_id : int
ID of the material
Returns
-------
numpy.ndarray
Array of densities in atom/b-cm
"""
data = POINTER(c_double)()
n = self._dll.openmc_material_get_densities(mat_id, data)
if data:
return as_array(data, (n,))
else:
return None
def material_set_density(self, mat_id, density):
"""Set density of a material.
Parameters
----------
mat_id : int
ID of the material
density : float
Density in atom/b-cm
Returns
-------
int
Return status (negative if an error occurs).
"""
return self._dll.openmc_material_set_density(mat_id, density)
def plot_geometry(self):
"""Plot geometry"""
return self._dll.openmc_plot_geometry()
def reset(self):
"""Reset tallies"""
return self._dll.openmc_reset()
def run(self):
"""Run simulation"""
return self._dll.openmc_run()
def tally_results(self, tally_id):
"""Get tally results array
Parameters
----------
tally_id : int
ID of tally
Returns
-------
numpy.ndarray
Array that exposes the internal tally results array
"""
data = POINTER(c_double)()
shape = _int3()
self._dll.openmc_tally_results(tally_id, data, shape)
if data:
return as_array(data, tuple(shape[::-1]))
else:
return None
def __getattr__(self, key):
# Fall-back for other functions that may be available from library
try:
return getattr(self._dll, 'openmc_{}'.format(key))
except AttributeError:
raise AttributeError("OpenMC library doesn't have a '{}' function"
.format(key))
@contextmanager
def lib_context(intracomm=None):
"""Provides context manager for calling OpenMC shared library functions.
This function is intended to be used in a 'with' statement and ensures that
OpenMC is properly initialized/finalized. At the completion of the 'with'
block, all memory that was allocated during the block is freed. For
example::
with openmc.lib_context() as lib:
for i in range(n_iters):
lib.reset()
do_stuff()
lib.run()
Parameters
----------
intracomm : mpi4py.MPI.Intracomm or None
MPI intracommunicator
"""
lib.init(comm)
yield lib
lib.finalize()
# Determine shared-library suffix
if sys.platform == 'darwin':
suffix = 'dylib'
else:
suffix = 'so'
# Open shared library
filename = pkg_resources.resource_filename(
__name__, '_libopenmc.{}'.format(suffix))
try:
lib = OpenMCLibrary(filename)
except OSError:
warn("OpenMC shared library is not available from the Python API. This "
"means you will not be able to use openmc.lib to make in-memory "
"calls to OpenMC.")
lib = None

55
openmc/capi/__init__.py Normal file
View file

@ -0,0 +1,55 @@
"""
This module provides bindings to C functions defined by OpenMC shared library.
When the :mod:`openmc` package is imported, the OpenMC shared library is
automatically loaded. Calls to the OpenMC library can then be via functions or
objects in the :mod:`openmc.capi` subpackage, for example:
.. code-block:: python
openmc.capi.init()
openmc.capi.run()
openmc.capi.finalize()
"""
from ctypes import CDLL
import os
import sys
from warnings import warn
import pkg_resources
# Determine shared-library suffix
if sys.platform == 'darwin':
_suffix = 'dylib'
else:
_suffix = 'so'
if os.environ.get('READTHEDOCS', None) != 'True':
# Open shared library
_filename = pkg_resources.resource_filename(
__name__, 'libopenmc.{}'.format(_suffix))
_dll = CDLL(_filename)
else:
# For documentation builds, we don't actually have the shared library
# available. Instead, we create a mock object so that when the modules
# within the openmc.capi package try to configure arguments and return
# values for symbols, no errors occur
try:
from unittest.mock import Mock
except ImportError:
from mock import Mock
_dll = Mock()
from .error import *
from .core import *
from .nuclide import *
from .material import *
from .cell import *
from .filter import *
from .tally import *
from .settings import settings
warn("The Python bindings to OpenMC's C API are still unstable "
"and may change substantially in future releases.", FutureWarning)

128
openmc/capi/cell.py Normal file
View file

@ -0,0 +1,128 @@
from collections import Mapping, Iterable
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER
from weakref import WeakValueDictionary
import numpy as np
from numpy.ctypeslib import as_array
from . import _dll
from .core import _FortranObjectWithID
from .error import _error_handler
from .material import Material
__all__ = ['Cell', 'cells']
# Cell functions
_dll.openmc_cell_get_id.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_cell_get_id.restype = c_int
_dll.openmc_cell_get_id.errcheck = _error_handler
_dll.openmc_cell_get_fill.argtypes = [
c_int32, POINTER(c_int), POINTER(POINTER(c_int32)), POINTER(c_int32)]
_dll.openmc_cell_set_fill.argtypes = [
c_int32, c_int, c_int32, POINTER(c_int32)]
_dll.openmc_cell_set_fill.restype = c_int
_dll.openmc_cell_set_fill.errcheck = _error_handler
_dll.openmc_cell_set_temperature.argtypes = [
c_int32, c_double, POINTER(c_int32)]
_dll.openmc_cell_set_temperature.restype = c_int
_dll.openmc_cell_set_temperature.errcheck = _error_handler
_dll.openmc_get_cell_index.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_get_cell_index.restype = c_int
_dll.openmc_get_cell_index.errcheck = _error_handler
class Cell(_FortranObjectWithID):
"""Cell stored internally.
This class exposes a cell that is stored internally in the OpenMC
library. To obtain a view of a cell with a given ID, use the
:data:`openmc.capi.nuclides` mapping.
Parameters
----------
index : int
Index in the `cells` array.
Attributes
----------
id : int
ID of the cell
"""
__instances = WeakValueDictionary()
def __new__(cls, *args):
if args not in cls.__instances:
instance = super(Cell, self).__new__(cls)
cls.__instances[args] = instance
return cls.__instances[args]
@property
def id(self):
cell_id = c_int32()
_dll.openmc_cell_get_id(self._index, cell_id)
return cell_id.value
@property
def fill(self):
fill_type = c_int()
indices = POINTER(c_int32)()
n = c_int32()
_dll.openmc_cell_get_fill(self._index, fill_type, indices, n)
if fill_type.value == 1:
if n.value > 1:
return [Material(index=i) for i in indices[:n.value]]
else:
return Material(index=indices[0])
else:
raise NotImplementedError
@fill.setter
def fill(self, fill):
if isinstance(fill, Iterable):
n = len(fill)
indices = (c_int*n)(*(m._index for m in fill))
_dll.openmc_cell_set_fill(self._index, 1, 1, indices)
elif isinstance(fill, Material):
materials = [fill]
indices = (c_int*1)(fill._index)
_dll.openmc_cell_set_fill(self._index, 1, 1, indices)
else:
raise NotImplementedError
def set_temperature(self, T, instance=None):
"""Set the temperature of a cell
Parameters
----------
T : float
Temperature in K
instance : int or None
Which instance of the cell
"""
_dll.openmc_cell_set_temperature(self._index, T, instance)
class _CellMapping(Mapping):
def __getitem__(self, key):
index = c_int32()
try:
_dll.openmc_get_cell_index(key, index)
except (AllocationError, InvalidIDError) as e:
# __contains__ expects a KeyError to work correctly
raise KeyError(str(e))
return Cell(index.value)
def __iter__(self):
for i in range(len(self)):
yield Cell(i + 1).id
def __len__(self):
return c_int32.in_dll(_dll, 'n_cells').value
def __repr__(self):
return repr(dict(self))
cells = _CellMapping()

186
openmc/capi/core.py Normal file
View file

@ -0,0 +1,186 @@
from contextlib import contextmanager
from ctypes import CDLL, c_int, c_int32, c_double, POINTER
from warnings import warn
from . import _dll
from .error import _error_handler
_dll.openmc_calculate_volumes.restype = None
_dll.openmc_finalize.restype = None
_dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32),
POINTER(c_int32)]
_dll.openmc_find.restype = c_int
_dll.openmc_find.errcheck = _error_handler
_dll.openmc_hard_reset.restype = None
_dll.openmc_init.argtypes = [POINTER(c_int)]
_dll.openmc_init.restype = None
_dll.openmc_get_keff.argtypes = [POINTER(c_double*2)]
_dll.openmc_get_keff.restype = c_int
_dll.openmc_get_keff.errcheck = _error_handler
_dll.openmc_plot_geometry.restype = None
_dll.openmc_run.restype = None
_dll.openmc_reset.restype = None
def calculate_volumes():
"""Run stochastic volume calculation"""
_dll.openmc_calculate_volumes()
def finalize():
"""Finalize simulation and free memory"""
_dll.openmc_finalize()
def find_cell(xyz):
"""Find the cell at a given point
Parameters
----------
xyz : iterable of float
Cartesian coordinates of position
Returns
-------
int
ID of the cell.
int
If the cell at the given point is repeated in the geometry, this
indicates which instance it is, i.e., 0 would be the first instance.
"""
uid = c_int32()
instance = c_int32()
_dll.openmc_find((c_double*3)(*xyz), 1, uid, instance)
return uid.value, instance.value
def find_material(xyz):
"""Find the material at a given point
Parameters
----------
xyz : iterable of float
Cartesian coordinates of position
Returns
-------
int or None
ID of the material or None is no material is found
"""
uid = c_int32()
instance = c_int32()
_dll.openmc_find((c_double*3)(*xyz), 2, uid, instance)
return uid.value if uid != 0 else None
def hard_reset():
"""Reset tallies, timers, and pseudo-random number generator state."""
_dll.openmc_hard_reset()
def init(intracomm=None):
"""Initialize OpenMC
Parameters
----------
intracomm : mpi4py.MPI.Intracomm or None
MPI intracommunicator
"""
if intracomm is not None:
# If an mpi4py communicator was passed, convert it to an integer to
# be passed to openmc_init
try:
intracomm = intracomm.py2f()
except AttributeError:
pass
_dll.openmc_init(c_int(intracomm))
else:
_dll.openmc_init(None)
def keff():
"""Return the calculated k-eigenvalue and its standard deviation.
Returns
-------
tuple
Mean k-eigenvalue and standard deviation of the mean
"""
k = (c_double*2)()
_dll.openmc_get_keff(k)
return tuple(k)
def plot_geometry():
"""Plot geometry"""
_dll.openmc_plot_geometry()
def reset():
"""Reset tallies and timers."""
_dll.openmc_reset()
def run():
"""Run simulation"""
_dll.openmc_run()
@contextmanager
def run_in_memory(intracomm=None):
"""Provides context manager for calling OpenMC shared library functions.
This function is intended to be used in a 'with' statement and ensures that
OpenMC is properly initialized/finalized. At the completion of the 'with'
block, all memory that was allocated during the block is freed. For
example::
with openmc.capi.run_in_memory():
for i in range(n_iters):
openmc.capi.reset()
do_stuff()
openmc.capi.run()
Parameters
----------
intracomm : mpi4py.MPI.Intracomm or None
MPI intracommunicator
"""
init(intracomm)
try:
yield
finally:
finalize()
class _DLLGlobal(object):
"""Data descriptor that exposes global variables from libopenmc."""
def __init__(self, ctype, name):
self.ctype = ctype
self.name = name
def __get__(self, instance, owner):
return self.ctype.in_dll(_dll, self.name).value
def __set__(self, instance, value):
self.ctype.in_dll(_dll, self.name).value = value
class _FortranObject(object):
def __repr__(self):
return "{}[{}]".format(type(self).__name__, self._index)
class _FortranObjectWithID(_FortranObject):
def __init__(self, uid=None, new=True, index=None):
# Creating the object has already been handled by __new__. In the
# initializer, all we do is make sure that the object returned has an ID
# assigned. If the array index of the object is out of bounds, an
# OutOfBoundsError will be raised here by virtue of referencing self.id
self.id

73
openmc/capi/error.py Normal file
View file

@ -0,0 +1,73 @@
from ctypes import c_int, c_char
from . import _dll
class Error(Exception):
"""Root exception class for OpenMC."""
class GeometryError(Error):
"""Geometry-related error"""
class InvalidIDError(Error):
"""Use of an ID that is invalid."""
class AllocationError(Error):
"""Error related to memory allocation."""
class OutOfBoundsError(Error):
"""Index in array out of bounds."""
class DataError(Error):
"""Error relating to nuclear data."""
class PhysicsError(Error):
"""Error relating to performing physics."""
class InvalidArgumentError(Error):
"""Argument passed was invalid."""
class InvalidTypeError(Error):
"""Tried to perform an operation on the wrong type."""
def _error_handler(err, func, args):
"""Raise exception according to error code."""
# Get error code corresponding to global constant.
def errcode(s):
return c_int.in_dll(_dll, s).value
# Get error message set by OpenMC library
errmsg = (c_char*256).in_dll(_dll, 'openmc_err_msg')
msg = errmsg.value.decode()
# Raise exception type corresponding to error code
if err == errcode('e_allocate'):
raise AllocationError(msg)
elif err == errcode('e_out_of_bounds'):
raise OutOfBoundsError(msg)
elif err == errcode('e_invalid_argument'):
raise InvalidArgumentError(msg)
elif err == errcode('e_invalid_type'):
raise InvalidTypeError(msg)
if err == errcode('e_invalid_id'):
raise InvalidIDError(msg)
elif err == errcode('e_geometry'):
raise GeometryError(msg)
elif err == errcode('e_data'):
raise DataError(msg)
elif err == errcode('e_physics'):
raise PhysicsError(msg)
elif err == errcode('e_warning'):
warn(msg)
elif err < 0:
raise Exception("Unknown error encountered (code {}).".format(err))

256
openmc/capi/filter.py Normal file
View file

@ -0,0 +1,256 @@
from collections import Mapping
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER, \
create_string_buffer
from weakref import WeakValueDictionary
import numpy as np
from numpy.ctypeslib import as_array
from . import _dll
from .core import _FortranObjectWithID
from .error import _error_handler, AllocationError, InvalidIDError
from .material import Material
__all__ = ['Filter', 'AzimuthalFilter', 'CellFilter',
'CellbornFilter', 'CellfromFilter', 'DistribcellFilter',
'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter',
'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter',
'MuFilter', 'PolarFilter', 'SurfaceFilter',
'UniverseFilter', 'filters']
# Tally functions
_dll.openmc_energy_filter_get_bins.argtypes = [
c_int32, POINTER(POINTER(c_double)), POINTER(c_int32)]
_dll.openmc_energy_filter_get_bins.restype = c_int
_dll.openmc_energy_filter_get_bins.errcheck = _error_handler
_dll.openmc_energy_filter_set_bins.argtypes = [c_int32, c_int32, POINTER(c_double)]
_dll.openmc_energy_filter_set_bins.restype = c_int
_dll.openmc_energy_filter_set_bins.errcheck = _error_handler
_dll.openmc_extend_filters.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)]
_dll.openmc_extend_filters.restype = c_int
_dll.openmc_extend_filters.errcheck = _error_handler
_dll.openmc_filter_get_id.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_filter_get_id.restype = c_int
_dll.openmc_filter_get_id.errcheck = _error_handler
_dll.openmc_filter_get_type.argtypes = [c_int32, c_char_p]
_dll.openmc_filter_get_type.restype = c_int
_dll.openmc_filter_get_type.errcheck = _error_handler
_dll.openmc_filter_set_id.argtypes = [c_int32, c_int32]
_dll.openmc_filter_set_id.restype = c_int
_dll.openmc_filter_set_id.errcheck = _error_handler
_dll.openmc_filter_set_type.argtypes = [c_int32, c_char_p]
_dll.openmc_filter_set_type.restype = c_int
_dll.openmc_filter_set_type.errcheck = _error_handler
_dll.openmc_get_filter_index.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_get_filter_index.restype = c_int
_dll.openmc_get_filter_index.errcheck = _error_handler
_dll.openmc_material_filter_get_bins.argtypes = [
c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)]
_dll.openmc_material_filter_get_bins.restype = c_int
_dll.openmc_material_filter_get_bins.errcheck = _error_handler
_dll.openmc_material_filter_set_bins.argtypes = [c_int32, c_int32, POINTER(c_int32)]
_dll.openmc_material_filter_set_bins.restype = c_int
_dll.openmc_material_filter_set_bins.errcheck = _error_handler
_dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32]
_dll.openmc_mesh_filter_set_mesh.restype = c_int
_dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler
class Filter(_FortranObjectWithID):
__instances = WeakValueDictionary()
def __new__(cls, obj=None, uid=None, new=True, index=None):
mapping = filters
if index is None:
if new:
# Determine ID to assign
if uid is None:
try:
uid = max(mapping) + 1
except ValueError:
uid = 1
else:
if uid in mapping:
raise AllocationError('A filter with ID={} has already '
'been allocated.'.format(uid))
# Resize internal array
index = c_int32()
_dll.openmc_extend_filters(1, index, None)
# Set the filter type -- note that the filter_type attribute
# only exists on subclasses!
_dll.openmc_filter_set_type(index, cls.filter_type.encode())
index = index.value
else:
index = mapping[uid]._index
if index not in cls.__instances:
instance = super(Filter, cls).__new__(cls)
instance._index = index
if uid is not None:
instance.id = uid
cls.__instances[index] = instance
return cls.__instances[index]
@property
def id(self):
filter_id = c_int32()
_dll.openmc_filter_get_id(self._index, filter_id)
return filter_id.value
@id.setter
def id(self, filter_id):
_dll.openmc_filter_set_id(self._index, filter_id)
class EnergyFilter(Filter):
filter_type = 'energy'
def __init__(self, bins=None, uid=None, new=True, index=None):
super(EnergyFilter, self).__init__(uid, new, index)
if bins is not None:
self.bins = bins
@property
def bins(self):
energies = POINTER(c_double)()
n = c_int32()
_dll.openmc_energy_filter_get_bins(self._index, energies, n)
return as_array(energies, (n.value,))
@bins.setter
def bins(self, bins):
# Get numpy array as a double*
energies = np.asarray(bins)
energies_p = energies.ctypes.data_as(POINTER(c_double))
_dll.openmc_energy_filter_set_bins(
self._index, len(energies), energies_p)
class EnergyoutFilter(Filter):
filter_type = 'energyout'
class AzimuthalFilter(Filter):
filter_type = 'azimuthal'
class CellFilter(Filter):
filter_type = 'cell'
class CellbornFilter(Filter):
filter_type = 'cellborn'
class CellfromFilter(Filter):
filter_type = 'cellfrom'
class DelayedGroupFilter(Filter):
filter_type = 'delayedgroup'
class DistribcellFilter(Filter):
filter_type = 'distribcell'
class EnergyFunctionFilter(Filter):
filter_type = 'energyfunction'
class MaterialFilter(Filter):
filter_type = 'material'
def __init__(self, bins=None, uid=None, new=True, index=None):
super(MaterialFilter, self).__init__(uid, new, index)
if bins is not None:
self.bins = bins
@property
def bins(self):
materials = POINTER(c_int32)()
n = c_int32()
_dll.openmc_material_filter_get_bins(self._index, materials, n)
return [Material(index=materials[i]) for i in range(n.value)]
@bins.setter
def bins(self, materials):
# Get material indices as int32_t[]
n = len(materials)
bins = (c_int32*n)(*(m._index for m in materials))
_dll.openmc_material_filter_set_bins(self._index, n, bins)
class MeshFilter(Filter):
filter_type = 'mesh'
class MuFilter(Filter):
filter_type = 'mu'
class PolarFilter(Filter):
filter_type = 'polar'
class SurfaceFilter(Filter):
filter_type = 'surface'
class UniverseFilter(Filter):
filter_type = 'universe'
_FILTER_TYPE_MAP = {
'azimuthal': AzimuthalFilter,
'cell': CellFilter,
'cellborn': CellbornFilter,
'cellfrom': CellfromFilter,
'delayedgroup': DelayedGroupFilter,
'distribcell': DistribcellFilter,
'energy': EnergyFilter,
'energyout': EnergyoutFilter,
'energyfunction': EnergyFunctionFilter,
'material': MaterialFilter,
'mesh': MeshFilter,
'mu': MuFilter,
'polar': PolarFilter,
'surface': SurfaceFilter,
'universe': UniverseFilter,
}
def _get_filter(index):
filter_type = create_string_buffer(20)
_dll.openmc_filter_get_type(index, filter_type)
filter_type = filter_type.value.decode()
return _FILTER_TYPE_MAP[filter_type](index=index)
class _FilterMapping(Mapping):
def __getitem__(self, key):
index = c_int32()
try:
_dll.openmc_get_filter_index(key, index)
except (AllocationError, InvalidIDError) as e:
# __contains__ expects a KeyError to work correctly
raise KeyError(str(e))
return _get_filter(index.value)
def __iter__(self):
for i in range(len(self)):
yield _get_filter(i + 1).id
def __len__(self):
return c_int32.in_dll(_dll, 'n_filters').value
def __repr__(self):
return repr(dict(self))
filters = _FilterMapping()

214
openmc/capi/material.py Normal file
View file

@ -0,0 +1,214 @@
from collections import Mapping
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER
from weakref import WeakValueDictionary
import numpy as np
from numpy.ctypeslib import as_array
from . import _dll, Nuclide
from .core import _FortranObjectWithID
from .error import _error_handler, AllocationError, InvalidIDError
__all__ = ['Material', 'materials']
# Material functions
_dll.openmc_extend_materials.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)]
_dll.openmc_extend_materials.restype = c_int
_dll.openmc_extend_materials.errcheck = _error_handler
_dll.openmc_get_material_index.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_get_material_index.restype = c_int
_dll.openmc_get_material_index.errcheck = _error_handler
_dll.openmc_material_add_nuclide.argtypes = [
c_int32, c_char_p, c_double]
_dll.openmc_material_add_nuclide.restype = c_int
_dll.openmc_material_add_nuclide.errcheck = _error_handler
_dll.openmc_material_get_id.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_material_get_id.restype = c_int
_dll.openmc_material_get_id.errcheck = _error_handler
_dll.openmc_material_get_densities.argtypes = [
c_int32, POINTER(POINTER(c_int)), POINTER(POINTER(c_double)),
POINTER(c_int)]
_dll.openmc_material_get_densities.restype = c_int
_dll.openmc_material_get_densities.errcheck = _error_handler
_dll.openmc_material_set_density.argtypes = [c_int32, c_double]
_dll.openmc_material_set_density.restype = c_int
_dll.openmc_material_set_density.errcheck = _error_handler
_dll.openmc_material_set_densities.argtypes = [
c_int32, c_int, POINTER(c_char_p), POINTER(c_double)]
_dll.openmc_material_set_densities.restype = c_int
_dll.openmc_material_set_densities.errcheck = _error_handler
_dll.openmc_material_set_id.argtypes = [c_int32, c_int32]
_dll.openmc_material_set_id.restype = c_int
_dll.openmc_material_set_id.errcheck = _error_handler
class Material(_FortranObjectWithID):
"""Material stored internally.
This class exposes a material that is stored internally in the OpenMC
library. To obtain a view of a material with a given ID, use the
:data:`openmc.capi.materials` mapping.
Parameters
----------
uid : int or None
Unique ID of the tally
new : bool
When `index` is None, this argument controls whether a new object is
created or a view to an existing object is returned.
index : int or None
Index in the `materials` array.
Attributes
----------
id : int
ID of the material
nuclides : list of str
List of nuclides in the material
densities : numpy.ndarray
Array of densities in atom/b-cm
"""
__instances = WeakValueDictionary()
def __new__(cls, uid=None, new=True, index=None):
mapping = materials
if index is None:
if new:
# Determine ID to assign
if uid is None:
try:
uid = max(mapping) + 1
except ValueError:
uid = 1
else:
if uid in mapping:
raise AllocationError('A material with ID={} has already '
'been allocated.'.format(uid))
index = c_int32()
_dll.openmc_extend_materials(1, index, None)
index = index.value
else:
index = mapping[uid]._index
if index not in cls.__instances:
instance = super(Material, cls).__new__(cls)
instance._index = index
if uid is not None:
instance.id = uid
cls.__instances[index] = instance
return cls.__instances[index]
@property
def id(self):
mat_id = c_int32()
_dll.openmc_material_get_id(self._index, mat_id)
return mat_id.value
@id.setter
def id(self, mat_id):
_dll.openmc_material_set_id(self._index, mat_id)
@property
def nuclides(self):
return self._get_densities()[0]
return nuclides
@property
def densities(self):
return self._get_densities()[1]
def _get_densities(self):
"""Get atom densities in a material.
Returns
-------
list of string
List of nuclide names
numpy.ndarray
Array of densities in atom/b-cm
"""
# Allocate memory for arguments that are written to
nuclides = POINTER(c_int)()
densities = POINTER(c_double)()
n = c_int()
# Get nuclide names and densities
_dll.openmc_material_get_densities(self._index, nuclides, densities, n)
# Convert to appropriate types and return
nuclide_list = [Nuclide(nuclides[i]).name for i in range(n.value)]
density_array = as_array(densities, (n.value,))
return nuclide_list, density_array
def add_nuclide(self, name, density):
"""Add a nuclide to a material.
Parameters
----------
name : str
Name of nuclide, e.g. 'U235'
density : float
Density in atom/b-cm
"""
_dll.openmc_material_add_nuclide(self._index, name.encode(), density)
def set_density(self, density):
"""Set density of a material.
Parameters
----------
density : float
Density in atom/b-cm
"""
_dll.openmc_material_set_density(self._index, density)
def set_densities(self, nuclides, densities):
"""Set the densities of a list of nuclides in a material
Parameters
----------
nuclides : iterable of str
Nuclide names
densities : iterable of float
Corresponding densities in atom/b-cm
"""
# Convert strings to an array of char*
nucs = (c_char_p * len(nuclides))()
nucs[:] = [x.encode() for x in nuclides]
# Get numpy array as a double*
d = np.asarray(densities)
dp = d.ctypes.data_as(POINTER(c_double))
_dll.openmc_material_set_densities(self._index, len(nuclides), nucs, dp)
class _MaterialMapping(Mapping):
def __getitem__(self, key):
index = c_int32()
try:
_dll.openmc_get_material_index(key, index)
except (AllocationError, InvalidIDError) as e:
# __contains__ expects a KeyError to work correctly
raise KeyError(str(e))
return Material(index=index.value)
def __iter__(self):
for i in range(len(self)):
yield Material(index=i + 1).id
def __len__(self):
return c_int32.in_dll(_dll, 'n_materials').value
def __repr__(self):
return repr(dict(self))
materials = _MaterialMapping()

101
openmc/capi/nuclide.py Normal file
View file

@ -0,0 +1,101 @@
from collections import Mapping
from ctypes import c_int, c_char_p, POINTER
from weakref import WeakValueDictionary
import numpy as np
from numpy.ctypeslib import as_array
from . import _dll
from .core import _FortranObject
from .error import _error_handler, DataError, AllocationError
__all__ = ['Nuclide', 'nuclides', 'load_nuclide']
# Nuclide functions
_dll.openmc_get_nuclide_index.argtypes = [c_char_p, POINTER(c_int)]
_dll.openmc_get_nuclide_index.restype = c_int
_dll.openmc_get_nuclide_index.errcheck = _error_handler
_dll.openmc_load_nuclide.argtypes = [c_char_p]
_dll.openmc_load_nuclide.restype = c_int
_dll.openmc_load_nuclide.errcheck = _error_handler
_dll.openmc_nuclide_name.argtypes = [c_int, POINTER(c_char_p)]
_dll.openmc_nuclide_name.restype = c_int
_dll.openmc_nuclide_name.errcheck = _error_handler
def load_nuclide(name):
"""Load cross section data for a nuclide.
Parameters
----------
name : str
Name of the nuclide, e.g. 'U235'
"""
_dll.openmc_load_nuclide(name.encode())
class Nuclide(_FortranObject):
"""Nuclide stored internally.
This class exposes a nuclide that is stored internally in the OpenMC
solver. To obtain a view of a nuclide with a given name, use the
:data:`openmc.capi.nuclides` mapping.
Parameters
----------
index : int
Index in the `nuclides` array.
Attributes
----------
name : str
Name of the nuclide, e.g. 'U235'
"""
__instances = WeakValueDictionary()
def __new__(cls, *args):
if args not in cls.__instances:
instance = super(Nuclide, cls).__new__(cls)
cls.__instances[args] = instance
return cls.__instances[args]
def __init__(self, index):
self._index = index
@property
def name(self):
name = c_char_p()
_dll.openmc_nuclide_name(self._index, name)
# Find blank in name
i = 0
while name.value[i:i+1] != b' ':
i += 1
return name.value[:i].decode()
class _NuclideMapping(Mapping):
"""Provide mapping from nuclide name to index in nuclides array."""
def __getitem__(self, key):
index = c_int()
try:
_dll.openmc_get_nuclide_index(key.encode(), index)
except (DataError, AllocationError) as e:
# __contains__ expects a KeyError to work correctly
raise KeyError(str(e))
return Nuclide(index.value)
def __iter__(self):
for i in range(len(self)):
yield Nuclide(i + 1).name
def __len__(self):
return c_int.in_dll(_dll, 'n_nuclides').value
def __repr__(self):
return repr(dict(self))
nuclides = _NuclideMapping()

53
openmc/capi/settings.py Normal file
View file

@ -0,0 +1,53 @@
from ctypes import c_int, c_int32, c_int64, c_double, c_char_p, POINTER
from . import _dll
from .core import _DLLGlobal
from .error import _error_handler
_RUN_MODES = {1: 'fixed source',
2: 'eigenvalue',
3: 'plot',
4: 'particle restart',
5: 'volume'}
_dll.openmc_set_seed.argtypes = [c_int64]
_dll.openmc_set_seed.restype = c_int
_dll.openmc_set_seed.errcheck = _error_handler
class _Settings(object):
# Attributes that are accessed through a descriptor
batches = _DLLGlobal(c_int32, 'n_batches')
generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch')
inactive = _DLLGlobal(c_int32, 'n_inactive')
particles = _DLLGlobal(c_int64, 'n_particles')
verbosity = _DLLGlobal(c_int, 'verbosity')
@property
def run_mode(self):
i = c_int.in_dll(_dll, 'run_mode').value
try:
return _RUN_MODES[i]
except KeyError:
return None
@run_mode.setter
def run_mode(self, mode):
current_idx = c_int.in_dll(_dll, 'run_mode')
for idx, mode_value in _RUN_MODES.items():
if mode_value == mode:
current_idx.value = idx
break
else:
raise ValueError('Invalid run mode: {}'.format(mode))
@property
def seed(self):
return c_int64.in_dll(_dll, 'seed').value
@seed.setter
def seed(self, seed):
_dll.openmc_set_seed(seed)
settings = _Settings()

209
openmc/capi/tally.py Normal file
View file

@ -0,0 +1,209 @@
from collections import Mapping
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER
from weakref import WeakValueDictionary
from numpy.ctypeslib import as_array
from . import _dll, Nuclide
from .core import _FortranObjectWithID
from .error import _error_handler, AllocationError, InvalidIDError
from .filter import _get_filter
__all__ = ['Tally', 'tallies']
# Tally functions
_dll.openmc_get_tally_index.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_get_tally_index.restype = c_int
_dll.openmc_get_tally_index.errcheck = _error_handler
_dll.openmc_extend_tallies.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)]
_dll.openmc_extend_tallies.restype = c_int
_dll.openmc_extend_tallies.errcheck = _error_handler
_dll.openmc_tally_get_id.argtypes = [c_int32, POINTER(c_int32)]
_dll.openmc_tally_get_id.restype = c_int
_dll.openmc_tally_get_id.errcheck = _error_handler
_dll.openmc_tally_get_filters.argtypes = [
c_int32, POINTER(POINTER(c_int32)), POINTER(c_int)]
_dll.openmc_tally_get_filters.restype = c_int
_dll.openmc_tally_get_filters.errcheck = _error_handler
_dll.openmc_tally_get_nuclides.argtypes = [
c_int32, POINTER(POINTER(c_int)), POINTER(c_int)]
_dll.openmc_tally_get_nuclides.restype = c_int
_dll.openmc_tally_get_nuclides.errcheck = _error_handler
_dll.openmc_tally_results.argtypes = [
c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)]
_dll.openmc_tally_results.restype = c_int
_dll.openmc_tally_results.errcheck = _error_handler
_dll.openmc_tally_set_filters.argtypes = [c_int32, c_int, POINTER(c_int32)]
_dll.openmc_tally_set_filters.restype = c_int
_dll.openmc_tally_set_filters.errcheck = _error_handler
_dll.openmc_tally_set_id.argtypes = [c_int32, c_int32]
_dll.openmc_tally_set_id.restype = c_int
_dll.openmc_tally_set_id.errcheck = _error_handler
_dll.openmc_tally_set_nuclides.argtypes = [c_int32, c_int, POINTER(c_char_p)]
_dll.openmc_tally_set_nuclides.restype = c_int
_dll.openmc_tally_set_nuclides.errcheck = _error_handler
_dll.openmc_tally_set_scores.argtypes = [c_int32, c_int, POINTER(c_char_p)]
_dll.openmc_tally_set_scores.restype = c_int
_dll.openmc_tally_set_scores.errcheck = _error_handler
_dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p]
_dll.openmc_tally_set_type.restype = c_int
_dll.openmc_tally_set_type.errcheck = _error_handler
class Tally(_FortranObjectWithID):
"""Tally stored internally.
This class exposes a tally that is stored internally in the OpenMC
library. To obtain a view of a tally with a given ID, use the
:data:`openmc.capi.tallies` mapping.
Parameters
----------
uid : int or None
Unique ID of the tally
new : bool
When `index` is None, this argument controls whether a new object is
created or a view of an existing object is returned.
index : int or None
Index in the `tallies` array.
Attributes
----------
id : int
ID of the tally
filters : list
List of tally filters
nuclides : list of str
List of nuclides to score results for
results : numpy.ndarray
Array of tally results
"""
__instances = WeakValueDictionary()
def __new__(cls, uid=None, new=True, index=None):
mapping = tallies
if index is None:
if new:
# Determine ID to assign
if uid is None:
try:
uid = max(mapping) + 1
except ValueError:
uid = 1
else:
if uid in mapping:
raise AllocationError('A tally with ID={} has already '
'been allocated.'.format(uid))
index = c_int32()
_dll.openmc_extend_tallies(1, index, None)
_dll.openmc_tally_set_type(index, b'generic')
index = index.value
else:
index = mapping[uid]._index
if index not in cls.__instances:
instance = super(Tally, cls).__new__(cls)
instance._index = index
if uid is not None:
instance.id = uid
cls.__instances[index] = instance
return cls.__instances[index]
@property
def id(self):
tally_id = c_int32()
_dll.openmc_tally_get_id(self._index, tally_id)
return tally_id.value
@id.setter
def id(self, tally_id):
_dll.openmc_tally_set_id(self._index, tally_id)
@property
def filters(self):
filt_idx = POINTER(c_int32)()
n = c_int()
_dll.openmc_tally_get_filters(self._index, filt_idx, n)
return [_get_filter(filt_idx[i]) for i in range(n.value)]
@property
def nuclides(self):
nucs = POINTER(c_int)()
n = c_int()
_dll.openmc_tally_get_nuclides(self._index, nucs, n)
return [Nuclide(nucs[i]).name if nucs[i] > 0 else 'total'
for i in range(n.value)]
@property
def results(self):
data = POINTER(c_double)()
shape = (c_int*3)()
_dll.openmc_tally_results(self._index, data, shape)
return as_array(data, tuple(shape[::-1]))
@filters.setter
def filters(self, filters):
# Get filter indices as int32_t[]
n = len(filters)
indices = (c_int32*n)(*(f._index for f in filters))
_dll.openmc_tally_set_filters(self._index, n, indices)
@nuclides.setter
def nuclides(self, nuclides):
nucs = (c_char_p * len(nuclides))()
nucs[:] = [x.encode() for x in nuclides]
_dll.openmc_tally_set_nuclides(self._index, len(nuclides), nucs)
@property
def scores(self):
pass
@scores.setter
def scores(self, scores):
scores_ = (c_char_p * len(scores))()
scores_[:] = [x.encode() for x in scores]
_dll.openmc_tally_set_scores(self._index, len(scores), scores_)
@classmethod
def new(cls, tally_id=None):
# Determine ID to assign
if tally_id is None:
try:
tally_id = max(tallies) + 1
except ValueError:
tally_id = 1
index = c_int32()
_dll.openmc_extend_tallies(1, index, None)
_dll.openmc_tally_set_type(index, b'generic')
tally = cls(index.value)
tally.id = tally_id
return tally
class _TallyMapping(Mapping):
def __getitem__(self, key):
index = c_int32()
try:
_dll.openmc_get_tally_index(key, index)
except (AllocationError, InvalidIDError) as e:
# __contains__ expects a KeyError to work correctly
raise KeyError(str(e))
return Tally(index=index.value)
def __iter__(self):
for i in range(len(self)):
yield Tally(index=i + 1).id
def __len__(self):
return c_int32.in_dll(_dll, 'n_tallies').value
def __repr__(self):
return repr(dict(self))
tallies = _TallyMapping()

View file

@ -867,12 +867,13 @@ class IncidentNeutron(EqualityMixin):
data.fission_energy = FissionEnergyRelease.from_endf(ev, data)
# Add 0K elastic scattering cross section
pendf = Evaluation(pendf_file)
file_obj = StringIO(pendf.section[3, 2])
get_head_record(file_obj)
params, xs = get_tab1_record(file_obj)
data.energy['0K'] = xs.x
data[2].xs['0K'] = xs
if '0K' not in data.energy:
pendf = Evaluation(pendf_file)
file_obj = StringIO(pendf.section[3, 2])
get_head_record(file_obj)
params, xs = get_tab1_record(file_obj)
data.energy['0K'] = xs.x
data[2].xs['0K'] = xs
finally:
# Get rid of temporary files

View file

@ -50,48 +50,45 @@ _THERMAL_DATA = {
75: ThermalTuple('ouo2', [8016, 8017, 8018], 1),
}
_PENDF_TEMPLATE = """
_TEMPLATE_RECONR = """
reconr / %%%%%%%%%%%%%%%%%%% Reconstruct XS for neutrons %%%%%%%%%%%%%%%%%%%%%%%
20 22
{nendf} {npendf}
'{library} PENDF for {zsymam}'/
{mat} 2/
0.001 0.0 0.003/ err tempr errmax
{error}/ err
'{library}: {zsymam}'/
'Processed by NJOY'/
0/
stop
"""
_ACE_TEMPLATE = """
reconr / %%%%%%%%%%%%%%%%%%% Reconstruct XS for neutrons %%%%%%%%%%%%%%%%%%%%%%%
20 21
'{library} PENDF for {zsymam}'/
{mat} 2/
0.001 0.0 0.003/ err tempr errmax
'{library}: {zsymam}'/
'Processed by NJOY'/
0/
_TEMPLATE_BROADR = """
broadr / %%%%%%%%%%%%%%%%%%%%%%% Doppler broaden XS %%%%%%%%%%%%%%%%%%%%%%%%%%%%
20 21 22
{nendf} {npendf} {nbroadr}
{mat} {num_temp} 0 0 0. /
0.001 1.0e6 0.003 /
{error}/ errthn
{temps}
0/
"""
_TEMPLATE_HEATR = """
heatr / %%%%%%%%%%%%%%%%%%%%%%%%% Add heating kerma %%%%%%%%%%%%%%%%%%%%%%%%%%%%
20 22 23 /
{nendf} {nheatr_in} {nheatr} /
{mat} 3 /
302 318 402 /
"""
_TEMPLATE_PURR = """
purr / %%%%%%%%%%%%%%%%%%%%%%%% Add probability tables %%%%%%%%%%%%%%%%%%%%%%%%%
20 23 24
{nendf} {npurr_in} {npurr} /
{mat} {num_temp} 1 20 64 /
{temps}
1.e10
0/
"""
_ACE_TEMPLATE_ACER = """acer /
20 24 0 {nace} {ndir}
_TEMPLATE_ACER = """
acer / %%%%%%%%%%%%%%%%%%%%%%%% Write out in ACE format %%%%%%%%%%%%%%%%%%%%%%%%
{nendf} {nacer_in} 0 {nace} {ndir}
1 0 1 .{ext} /
'{library}: {zsymam} at {temperature}'/
{mat} {temperature}
@ -99,35 +96,22 @@ _ACE_TEMPLATE_ACER = """acer /
/
"""
_ACE_THERMAL_TEMPLATE = """
reconr / %%%%%%%%%%%%%%%%%%% Reconstruct XS for neutrons %%%%%%%%%%%%%%%%%%%%%%%
20 22
'{library} PENDF for {zsymam}'/
{mat} 2/
0.001 0. 0.001/ err tempr errmax
'{library}: PENDF for {zsymam}'/
'Processed by NJOY'/
0/
broadr / %%%%%%%%%%%%%%%%%%%%%%% Doppler broaden XS %%%%%%%%%%%%%%%%%%%%%%%%%%%%
20 22 23
{mat} {num_temp} 0 0 0./
0.001 2.0e+6 0.001/ errthn thnmax errmax
{temps}
0/
_THERMAL_TEMPLATE_THERMR = """
thermr / %%%%%%%%%%%%%%%% Add thermal scattering data (free gas) %%%%%%%%%%%%%%%
0 23 62
0 {nthermr1_in} {nthermr1}
0 {mat} 12 {num_temp} 1 0 {iform} 1 221 1/
{temps}
0.001 {energy_max}
{error} {energy_max}
thermr / %%%%%%%%%%%%%%%% Add thermal scattering data (bound) %%%%%%%%%%%%%%%%%%
60 62 27
{nthermal_endf} {nthermr2_in} {nthermr2}
{mat_thermal} {mat} 16 {num_temp} {inelastic} {elastic} {iform} {natom} 222 1/
{temps}
0.001 {energy_max}
{error} {energy_max}
"""
_ACE_THERMAL_TEMPLATE_ACER = """acer /
20 27 0 {nace} {ndir}
_THERMAL_TEMPLATE_ACER = """
acer / %%%%%%%%%%%%%%%%%%%%%%%% Write out in ACE format %%%%%%%%%%%%%%%%%%%%%%%%
{nendf} {nthermal_acer_in} 0 {nace} {ndir}
2 0 1 .{ext}/
'{library}: {zsymam_thermal} processed by NJOY'/
{mat} {temperature} '{data.name}' /
@ -136,7 +120,8 @@ _ACE_THERMAL_TEMPLATE_ACER = """acer /
"""
def run(commands, tapein, tapeout, stdout=False, njoy_exec='njoy'):
def run(commands, tapein, tapeout, input_filename=None, stdout=False,
njoy_exec='njoy'):
"""Run NJOY with given commands
Parameters
@ -147,6 +132,8 @@ def run(commands, tapein, tapeout, stdout=False, njoy_exec='njoy'):
Dictionary mapping tape numbers to paths for any input files
tapeout : dict
Dictionary mapping tape numbers to paths for any output files
input_filename : str, optional
File name to write out NJOY input commands
stdout : bool, optional
Whether to display output when running NJOY
njoy_exec : str, optional
@ -159,6 +146,10 @@ def run(commands, tapein, tapeout, stdout=False, njoy_exec='njoy'):
"""
if input_filename is not None:
with open(input_filename, 'w') as f:
f.write(commands)
# Create temporary directory -- it would be preferable to use
# TemporaryDirectory(), but it is only available in Python 3.2
tmpdir = tempfile.mkdtemp()
@ -195,7 +186,7 @@ def run(commands, tapein, tapeout, stdout=False, njoy_exec='njoy'):
return njoy.returncode
def make_pendf(filename, pendf='pendf', stdout=False):
def make_pendf(filename, pendf='pendf', error=0.001, stdout=False):
"""Generate ACE file from an ENDF file
Parameters
@ -204,6 +195,8 @@ def make_pendf(filename, pendf='pendf', stdout=False):
Path to ENDF file
pendf : str, optional
Path of pointwise ENDF file to write
error : float, optional
Fractional error tolerance for NJOY processing
stdout : bool
Whether to display NJOY standard output
@ -213,21 +206,14 @@ def make_pendf(filename, pendf='pendf', stdout=False):
Return code of NJOY process
"""
ev = endf.Evaluation(filename)
mat = ev.material
zsymam = ev.target['zsymam']
# Determine name of library
library = '{}-{}.{}'.format(*ev.info['library'])
commands = _PENDF_TEMPLATE.format(**locals())
tapein = {20: filename}
tapeout = {22: pendf}
return run(commands, tapein, tapeout, stdout)
return make_ace(filename, pendf=pendf, error=error, broadr=False,
heatr=False, purr=False, acer=False, stdout=stdout)
def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir',
pendf=None, **kwargs):
def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir', pendf=None,
error=0.001, broadr=True, heatr=True, purr=True, acer=True,
**kwargs):
"""Generate incident neutron ACE file from an ENDF file
Parameters
@ -243,6 +229,16 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir',
Path of xsdir file to write
pendf : str, optional
Path of pendf file to write. If omitted, the pendf file is not saved.
error : float, optional
Fractional error tolerance for NJOY processing
broadr : bool, optional
Indicating whether to Doppler broaden XS when running NJOY
heatr : bool, optional
Indicating whether to add heating kerma when running NJOY
purr : bool, optional
Indicating whether to add probability table when running NJOY
acer : bool, optional
Indicating whether to generate ACE file when running NJOY
**kwargs
Keyword arguments passed to :func:`openmc.data.njoy.run`
@ -264,26 +260,59 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir',
num_temp = len(temperatures)
temps = ' '.join(str(i) for i in temperatures)
commands = _ACE_TEMPLATE.format(**locals())
tapein = {20: filename}
# Create njoy commands by modules
commands = ""
nendf, npendf = 20, 21
tapein = {nendf: filename}
tapeout = {}
if pendf is not None:
tapeout[21] = pendf
fname = '{}_{:.1f}'
for i, temperature in enumerate(temperatures):
# Extend input with an ACER run for each temperature
nace = 25 + 2*i
ndir = 25 + 2*i + 1
ext = '{:02}'.format(i + 1)
commands += _ACE_TEMPLATE_ACER.format(**locals())
tapeout[npendf] = pendf
# Indicate tapes to save for each ACER run
tapeout[nace] = fname.format(ace, temperature)
tapeout[ndir] = fname.format(xsdir, temperature)
# reconr
commands += _TEMPLATE_RECONR
nlast = npendf
# broadr
if broadr:
nbroadr = nlast + 1
commands += _TEMPLATE_BROADR
nlast = nbroadr
# heatr
if heatr:
nheatr_in = nlast
nheatr = nheatr_in + 1
commands += _TEMPLATE_HEATR
nlast = nheatr
# purr
if purr:
npurr_in = nlast
npurr = npurr_in + 1
commands += _TEMPLATE_PURR
nlast = npurr
commands = commands.format(**locals())
# acer
if acer:
nacer_in = nlast
fname = '{}_{:.1f}'
for i, temperature in enumerate(temperatures):
# Extend input with an ACER run for each temperature
nace = nacer_in + 1 + 2*i
ndir = nace + 1
ext = '{:02}'.format(i + 1)
commands += _TEMPLATE_ACER.format(**locals())
# Indicate tapes to save for each ACER run
tapeout[nace] = fname.format(ace, temperature)
tapeout[ndir] = fname.format(xsdir, temperature)
commands += 'stop\n'
retcode = run(commands, tapein, tapeout, **kwargs)
if retcode == 0:
if acer and retcode == 0:
with open(ace, 'w') as ace_file, open(xsdir, 'w') as xsdir_file:
for temperature in temperatures:
# Get contents of ACE file
@ -311,8 +340,8 @@ def make_ace(filename, temperatures=None, ace='ace', xsdir='xsdir',
return retcode
def make_ace_thermal(filename, filename_thermal, temperatures=None,
ace='ace', xsdir='xsdir', **kwargs):
def make_ace_thermal(filename, filename_thermal, temperatures=None,
ace='ace', xsdir='xsdir', error=0.001, **kwargs):
"""Generate thermal scattering ACE file from ENDF files
Parameters
@ -328,6 +357,8 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None,
Path of ACE file to write
xsdir : str, optional
Path of xsdir file to write
error : float, optional
Fractional error tolerance for NJOY processing
**kwargs
Keyword arguments passed to :func:`openmc.data.njoy.run`
@ -390,16 +421,41 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None,
num_temp = len(temperatures)
temps = ' '.join(str(i) for i in temperatures)
commands = _ACE_THERMAL_TEMPLATE.format(**locals())
tapein = {20: filename, 60: filename_thermal}
# Create njoy commands by modules
commands = ""
nendf, nthermal_endf, npendf = 20, 21, 22
tapein = {nendf: filename, nthermal_endf:filename_thermal}
tapeout = {}
# reconr
commands += _TEMPLATE_RECONR
nlast = npendf
# broadr
nbroadr = nlast + 1
commands += _TEMPLATE_BROADR
nlast = nbroadr
# thermr
nthermr1_in = nlast
nthermr1 = nthermr1_in + 1
nthermr2_in = nthermr1
nthermr2 = nthermr2_in + 1
commands += _THERMAL_TEMPLATE_THERMR
nlast = nthermr2
commands = commands.format(**locals())
# acer
nthermal_acer_in = nlast
fname = '{}_{:.1f}'
for i, temperature in enumerate(temperatures):
# Extend input with an ACER run for each temperature
nace = 28 + 2*i
ndir = 28 + 2*i + 1
nace = nthermal_acer_in + 1 + 2*i
ndir = nace + 1
ext = '{:02}'.format(i + 1)
commands += _ACE_THERMAL_TEMPLATE_ACER.format(**locals())
commands += _THERMAL_TEMPLATE_ACER.format(**locals())
# Indicate tapes to save for each ACER run
tapeout[nace] = fname.format(ace, temperature)

View file

@ -124,6 +124,15 @@ class Element(object):
is a tuple consisting of an openmc.Nuclide instance and the natural
abundance of the isotope.
Notes
-----
When the `enrichment` argument is specified, a correlation from
`ORNL/CSD/TM-244 <https://doi.org/10.2172/5561567>`_ is used to
calculate the weight fractions of U234, U235, U236, and U238. Namely,
the weight fraction of U234 and U236 are taken to be 0.89% and 0.46%,
respectively, of the U235 weight fraction. The remainder of the isotopic
weight is assigned to U238.
"""
# Get the nuclides present in nature
@ -217,9 +226,10 @@ class Element(object):
if enrichment is not None:
# Calculate the mass fractions of isotopes
abundances['U234'] = 0.008 * enrichment
abundances['U234'] = 0.0089 * enrichment
abundances['U235'] = enrichment
abundances['U238'] = 100.0 - 1.008 * enrichment
abundances['U236'] = 0.0046 * enrichment
abundances['U238'] = 100.0 - 1.0135 * enrichment
# Convert the mass fractions to mole fractions
for nuclide in abundances.keys():

View file

@ -17,7 +17,8 @@ from .mixin import IDManagerMixin
_FILTER_TYPES = ['universe', 'material', 'cell', 'cellborn', 'surface',
'mesh', 'energy', 'energyout', 'mu', 'polar', 'azimuthal',
'distribcell', 'delayedgroup', 'energyfunction', 'particle']
'distribcell', 'delayedgroup', 'energyfunction', 'cellfrom',
'particle']
_CURRENT_NAMES = {1: 'x-min out', 2: 'x-min in',
3: 'x-max out', 4: 'x-max in',
@ -620,6 +621,39 @@ class CellFilter(WithIDFilter):
self._smart_set_bins(bins, openmc.Cell)
class CellFromFilter(WithIDFilter):
"""Bins tally on which Cell the neutron came from.
Parameters
----------
bins : openmc.Cell, Integral, or iterable thereof
The Cell(s) to tally. Either openmc.Cell objects or their
Integral ID numbers can be used.
filter_id : int
Unique identifier for the filter
Attributes
----------
bins : Integral or Iterable of Integral
openmc.Cell IDs.
id : int
Unique identifier for the filter
num_bins : Integral
The number of filter bins
stride : Integral
The number of filter, nuclide and score bins within each of this
filter's bins.
"""
@property
def bins(self):
return self._bins
@bins.setter
def bins(self, bins):
self._smart_set_bins(bins, openmc.Cell)
class CellbornFilter(WithIDFilter):
"""Bins tally events based on which Cell the neutron was born in.

View file

@ -606,7 +606,7 @@ class Library(object):
Parameters
----------
domain : Material or Cell or Universe or Integral
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh or Integral
The material, cell, or universe object of interest (or its ID)
mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix', 'delayed-nu-fission', 'delayed-nu-fission matrix', 'chi-delayed', 'beta'}
The type of multi-group cross section object to return
@ -668,7 +668,7 @@ class Library(object):
Returns
-------
Library
openmc.mgxs.Library
A new multi-group cross section library condensed to the group
structure of interest
@ -880,7 +880,7 @@ class Library(object):
Returns
-------
Library
openmc.mgxs.Library
A Library object loaded from the pickle binary file
See also

View file

@ -71,7 +71,7 @@ class MDGXS(MGXS):
Reaction type (e.g., 'chi-delayed', 'beta', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -948,7 +948,7 @@ class ChiDelayed(MDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -1462,7 +1462,7 @@ class DelayedNuFissionXS(MDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -1598,7 +1598,7 @@ class Beta(MDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -1782,7 +1782,7 @@ class DecayRate(MDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -1959,7 +1959,7 @@ class MatrixMDGXS(MDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2551,7 +2551,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization

View file

@ -154,7 +154,7 @@ class MGXS(object):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2018,7 +2018,7 @@ class MatrixMGXS(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2512,7 +2512,7 @@ class TotalXS(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2649,7 +2649,7 @@ class TransportXS(MGXS):
If True, the cross section data will include neutron multiplication
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2858,7 +2858,7 @@ class AbsorptionXS(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -2986,7 +2986,7 @@ class CaptureXS(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -3140,7 +3140,7 @@ class FissionXS(MGXS):
If true, computes cross sections which only includes prompt neutrons
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -3309,7 +3309,7 @@ class KappaFissionXS(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -3441,7 +3441,7 @@ class ScatterXS(MGXS):
If True, the cross section data will include neutron multiplication
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -3658,7 +3658,7 @@ class ScatterMatrixXS(MatrixMGXS):
If True, the cross section data will include neutron multiplication
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -4020,7 +4020,7 @@ class ScatterMatrixXS(MatrixMGXS):
# Override the nuclides for tally arithmetic
correction.nuclides = scatter_p1.nuclides
self._xs_tally -= correction
self._compute_xs()
return self._xs_tally
@ -4749,7 +4749,7 @@ class MultiplicityMatrixXS(MatrixMGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -4915,7 +4915,7 @@ class ScatterProbabilityMatrix(MatrixMGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -5088,7 +5088,7 @@ class NuFissionMatrixXS(MatrixMGXS):
If true, computes cross sections which only includes prompt neutrons
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -5245,7 +5245,7 @@ class Chi(MGXS):
If true, computes cross sections which only includes prompt neutrons
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization
@ -5823,7 +5823,7 @@ class InverseVelocity(MGXS):
Reaction type (e.g., 'total', 'nu-fission', etc.)
by_nuclide : bool
If true, computes cross sections for each nuclide in domain
domain : Material or Cell or Universe or Mesh
domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh
Domain for spatial homogenization
domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'}
Domain type for spatial homogenization

View file

@ -11,12 +11,12 @@ class Nuclide(object):
Parameters
----------
name : str
Name of the nuclide, e.g. U235
Name of the nuclide, e.g. 'U235'
Attributes
----------
name : str
Name of the nuclide, e.g. U235
Name of the nuclide, e.g. 'U235'
scattering : 'data' or 'iso-in-lab' or None
The type of angular scattering distribution to use

View file

@ -968,20 +968,15 @@ class Settings(object):
subelement = ET.SubElement(element, key)
subelement.text = str(value)
def _create_entropy_subelement(self, root):
if self._entropy_mesh is not None:
element = ET.SubElement(root, "entropy")
def _create_entropy_mesh_subelement(self, root):
if self.entropy_mesh is not None:
# See if a <mesh> element already exists -- if not, add it
path = "./mesh[@id='{}']".format(self.entropy_mesh.id)
if root.find(path) is None:
root.append(self.entropy_mesh.to_xml_element())
if self._entropy_mesh.dimension is not None:
subelement = ET.SubElement(element, "dimension")
subelement.text = ' '.join(
str(x) for x in self._entropy_mesh.dimension)
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(
str(x) for x in self._entropy_mesh.lower_left)
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(
str(x) for x in self._entropy_mesh.upper_right)
subelement = ET.SubElement(root, "entropy_mesh")
subelement.text = str(self.entropy_mesh.id)
def _create_trigger_subelement(self, root):
if self._trigger_active is not None:
@ -1038,18 +1033,15 @@ class Settings(object):
element = ET.SubElement(root, "track")
element.text = ' '.join(map(str, self._track))
def _create_ufs_subelement(self, root):
if self._ufs_mesh is not None:
element = ET.SubElement(root, "uniform_fs")
subelement = ET.SubElement(element, "dimension")
subelement.text = ' '.join(str(x) for x in
self._ufs_mesh.dimension)
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(str(x) for x in
self._ufs_mesh.lower_left)
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(str(x) for x in
self._ufs_mesh.upper_right)
def _create_ufs_mesh_subelement(self, root):
if self.ufs_mesh is not None:
# See if a <mesh> element already exists -- if not, add it
path = "./mesh[@id='{}']".format(self.ufs_mesh.id)
if root.find(path) is None:
root.append(self.ufs_mesh.to_xml_element())
subelement = ET.SubElement(root, "ufs_mesh")
subelement.text = str(self.ufs_mesh.id)
def _create_dd_subelement(self, root):
if self._dd_mesh_lower_left is not None and \
@ -1137,7 +1129,7 @@ class Settings(object):
self._create_seed_subelement(root_element)
self._create_survival_biasing_subelement(root_element)
self._create_cutoff_subelement(root_element)
self._create_entropy_subelement(root_element)
self._create_entropy_mesh_subelement(root_element)
self._create_trigger_subelement(root_element)
self._create_no_reduce_subelement(root_element)
self._create_threads_subelement(root_element)
@ -1146,7 +1138,7 @@ class Settings(object):
self._create_temperature_subelements(root_element)
self._create_trace_subelement(root_element)
self._create_track_subelement(root_element)
self._create_ufs_subelement(root_element)
self._create_ufs_mesh_subelement(root_element)
self._create_dd_subelement(root_element)
self._create_resonance_scattering_subelement(root_element)
self._create_volume_calcs_subelement(root_element)

View file

@ -402,14 +402,6 @@ class StatePoint(object):
scores = group['score_bins'].value
n_score_bins = group['n_score_bins'].value
# Compute and set the filter strides
for i in range(n_filters):
tally_filter = tally.filters[i]
tally_filter.stride = n_score_bins * len(nuclide_names)
for j in range(i+1, n_filters):
tally_filter.stride *= tally.filters[j].num_bins
# Read scattering moment order strings (e.g., P3, Y1,2, etc.)
moments = group['moment_orders'].value
@ -423,6 +415,9 @@ class StatePoint(object):
tally.scores.append(score)
# Compute and set the filter strides
tally._update_filter_strides()
# Add Tally to the global dictionary of all Tallies
tally.sparse = self.sparse
self._tallies[tally_id] = tally

View file

@ -4,7 +4,7 @@ from collections import Iterable, MutableSequence
import copy
import re
from functools import partial
import itertools
from itertools import product
from numbers import Integral, Real
import warnings
from xml.etree import ElementTree as ET
@ -502,7 +502,7 @@ class Tally(IDManagerMixin):
Parameters
----------
nuclide : str, Nuclide, CrossNuclide or AggregateNuclide
nuclide : str, openmc.Nuclide, CrossNuclide or AggregateNuclide
Nuclide to add to the tally. The nuclide should be a Nuclide object
when a user is adding nuclides to a Tally for input file generation.
The nuclide is a str when a Tally is created from a StatePoint file
@ -1329,7 +1329,7 @@ class Tally(IDManagerMixin):
if isinstance(self_filter, openmc.MeshFilter):
dimension = self_filter.mesh.dimension
xyz = [range(1, x+1) for x in dimension]
bins = list(itertools.product(*xyz))
bins = list(product(*xyz))
# Create list of 2-tuples for energy boundary bins
elif isinstance(self_filter, (openmc.EnergyFilter,
@ -1364,7 +1364,7 @@ class Tally(IDManagerMixin):
indices *= self_filter.num_bins
# Apply outer product sum between all filter bin indices
filter_indices = list(map(sum, itertools.product(*filter_indices)))
filter_indices = list(map(sum, product(*filter_indices)))
# If user did not specify any specific Filters, use them all
else:
@ -1664,8 +1664,10 @@ class Tally(IDManagerMixin):
new_column.extend(['']*delta_len)
columns[i] = tuple(new_column)
# Create and set a MultiIndex for the DataFrame's columns
df.columns = pd.MultiIndex.from_tuples(columns)
# Create and set a MultiIndex for the DataFrame's columns, but only
# if any column actually is multi-level (e.g., a mesh filter)
if any(len(c) > 1 for c in columns):
df.columns = pd.MultiIndex.from_tuples(columns)
# Modify the df.to_string method so that it prints formatted strings.
# Credit to http://stackoverflow.com/users/3657742/chrisb for this trick
@ -1880,7 +1882,7 @@ class Tally(IDManagerMixin):
new_tally.filters.append(self_filter)
else:
all_filters = [self_copy.filters, other_copy.filters]
for self_filter, other_filter in itertools.product(*all_filters):
for self_filter, other_filter in product(*all_filters):
new_filter = openmc.CrossFilter(self_filter, other_filter,
binary_op)
new_tally.filters.append(new_filter)
@ -1891,20 +1893,31 @@ class Tally(IDManagerMixin):
new_tally.nuclides.append(self_nuclide)
else:
all_nuclides = [self_copy.nuclides, other_copy.nuclides]
for self_nuclide, other_nuclide in itertools.product(*all_nuclides):
new_nuclide = \
openmc.CrossNuclide(self_nuclide, other_nuclide, binary_op)
for self_nuclide, other_nuclide in product(*all_nuclides):
new_nuclide = openmc.CrossNuclide(self_nuclide, other_nuclide,
binary_op)
new_tally.nuclides.append(new_nuclide)
# Define helper function that handles score units appropriately
# depending on the binary operator
def cross_score(score1, score2, binary_op):
if binary_op == '+' or binary_op == '-':
if score1 == score2:
return score1
else:
return openmc.CrossScore(score1, score2, binary_op)
else:
return openmc.CrossScore(score1, score2, binary_op)
# Add scores to the new tally
if score_product == 'entrywise':
for self_score in self_copy.scores:
new_tally.scores.append(self_score)
new_score = cross_score(self_score, self_score, binary_op)
new_tally.scores.append(new_score)
else:
all_scores = [self_copy.scores, other_copy.scores]
for self_score, other_score in itertools.product(*all_scores):
new_score = openmc.CrossScore(self_score, other_score,
binary_op)
for self_score, other_score in product(*all_scores):
new_score = cross_score(self_score, other_score, binary_op)
new_tally.scores.append(new_score)
# Update the new tally's filter strides
@ -2142,7 +2155,7 @@ class Tally(IDManagerMixin):
std_dev = {}
# Store the data from the misaligned structure
for i, (bin1, bin2) in enumerate(itertools.product(filter1_bins, filter2_bins)):
for i, (bin1, bin2) in enumerate(product(filter1_bins, filter2_bins)):
filter_bins = [(bin1,), (bin2,)]
if self.mean is not None:
@ -2163,7 +2176,7 @@ class Tally(IDManagerMixin):
self._update_filter_strides()
# Realign the data
for i, (bin1, bin2) in enumerate(itertools.product(filter1_bins, filter2_bins)):
for i, (bin1, bin2) in enumerate(product(filter1_bins, filter2_bins)):
filter_bins = [(bin1,), (bin2,)]
indices = self.get_filter_indices(filters, filter_bins)
@ -3503,7 +3516,7 @@ class Tallies(cv.CheckedList):
if f not in already_written:
root_element.append(f.to_xml_element())
already_written[f] = f.id
else:
elif f.id != already_written[f]:
# Set the IDs of identical filters with different
# user-defined IDs to the same value
f.id = already_written[f]

View file

@ -2,8 +2,7 @@
OpenMC Monte Carlo Particle Transport Code
==========================================
.. image:: https://travis-ci.org/mit-crpg/openmc.svg?branch=develop
:target: https://travis-ci.org/mit-crpg/openmc
|licensebadge| |travisbadge|
The OpenMC project aims to provide a fully-featured Monte Carlo particle
transport code based on modern methods. It is a constructive solid geometry,
@ -55,3 +54,11 @@ OpenMC is distributed under the MIT/X license_.
.. _Troubleshooting section: http://openmc.readthedocs.io/en/stable/usersguide/troubleshoot.html
.. _Issues: https://github.com/mit-crpg/openmc/issues
.. _license: http://openmc.readthedocs.io/en/stable/license.html
.. |licensebadge| image:: https://img.shields.io/github/license/mit-crpg/openmc.svg
:target: http://openmc.readthedocs.io/en/latest/license.html
:alt: License
.. |travisbadge| image:: https://travis-ci.org/mit-crpg/openmc.svg?branch=develop
:target: https://travis-ci.org/mit-crpg/openmc
:alt: Travis CI build status (Linux)

View file

@ -247,6 +247,11 @@ def update_materials(root):
if 'name' in nuclide.attrib:
nucname = nuclide.attrib['name']
nucname = nucname.replace('-', '')
# If a nuclide name is in the ZAID notation (e.g., a number),
# convert it to the proper nuclide name.
if nucname.strip().isnumeric():
nucname = \
openmc.data.neutron._get_metadata(int(nucname))[0]
nucname = nucname.replace('Nat', '0')
if nucname.endswith('m'):
nucname = nucname[:-1] + '_m1'

View file

@ -3,13 +3,8 @@
import glob
import sys
import numpy as np
try:
from setuptools import setup
have_setuptools = True
except ImportError:
from distutils.core import setup
have_setuptools = False
from setuptools import setup, find_packages
try:
from Cython.Build import cythonize
have_cython = True
@ -28,46 +23,53 @@ else:
with open('openmc/__init__.py', 'r') as f:
version = f.readlines()[-1].split()[-1].strip("'")
kwargs = {'name': 'openmc',
'version': version,
'packages': ['openmc', 'openmc.data', 'openmc.mgxs', 'openmc.model',
'openmc.stats'],
'scripts': glob.glob('scripts/openmc-*'),
kwargs = {
'name': 'openmc',
'version': version,
'packages': find_packages(),
'scripts': glob.glob('scripts/openmc-*'),
# Data files and librarries
'package_data': {
'openmc': ['_libopenmc.{}'.format(suffix)],
'openmc.data': ['mass.mas12', '*.h5']
},
# Data files and librarries
'package_data': {
'openmc.capi': ['libopenmc.{}'.format(suffix)],
'openmc.data': ['mass.mas12', '*.h5']
},
# Metadata
'author': 'Will Boyd',
'author_email': 'wbinventor@gmail.com',
'description': 'OpenMC Python API',
'url': 'https://github.com/mit-crpg/openmc',
'classifiers': [
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Programming Language :: Python',
'Topic :: Scientific/Engineering'
]}
# Metadata
'author': 'The OpenMC Development Team',
'author_email': 'openmc-dev@googlegroups.com',
'description': 'OpenMC',
'url': 'https://github.com/mit-crpg/openmc',
'classifiers': [
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
'Intended Audience :: Science/Research',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Topic :: Scientific/Engineering'
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
],
if have_setuptools:
kwargs.update({
# Required dependencies
'install_requires': ['six', 'numpy>=1.9', 'h5py', 'scipy', 'pandas>=0.17.0'],
# Required dependencies
'install_requires': [
'six', 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib',
'pandas', 'lxml', 'uncertainties'
],
# Optional dependencies
'extras_require': {
'decay': ['uncertainties'],
'plot': ['matplotlib', 'ipython'],
'vtk': ['vtk', 'silomesh'],
'validate': ['lxml']
},
})
# Optional dependencies
'extras_require': {
'test': ['pytest', 'pytest-cov'],
'vtk': ['vtk', 'silomesh'],
},
}
# If Cython is present, add resonance reconstruction capability
if have_cython:

View file

@ -2,82 +2,177 @@ module openmc_api
use, intrinsic :: ISO_C_BINDING
use hdf5, only: HID_T
use hdf5, only: HID_T, h5tclose_f, h5close_f
use constants, only: K_BOLTZMANN
use eigenvalue, only: k_sum
use finalize, only: openmc_finalize
use eigenvalue, only: k_sum, openmc_get_keff
use error
use geometry, only: find_cell
use global
use geometry_header
use hdf5_interface
use message_passing, only: master
use material_header
use mesh_header
use message_passing
use nuclide_header
use initialize, only: openmc_init
use input_xml, only: assign_0K_elastic_scattering, check_data_version
use particle_header, only: Particle
use plot, only: openmc_plot_geometry
use random_lcg, only: seed, openmc_set_seed
use settings
use simulation_header
use tally_header
use tally_filter_header
use tally_filter
use tally, only: openmc_tally_set_type
use simulation, only: openmc_run
use string, only: to_f_string
use timer_header
use volume_calc, only: openmc_calculate_volumes
implicit none
private
public :: openmc_calculate_volumes
public :: openmc_cell_get_id
public :: openmc_cell_get_fill
public :: openmc_cell_set_fill
public :: openmc_cell_set_temperature
public :: openmc_energy_filter_get_bins
public :: openmc_energy_filter_set_bins
public :: openmc_extend_filters
public :: openmc_extend_materials
public :: openmc_extend_tallies
public :: openmc_filter_get_id
public :: openmc_filter_get_type
public :: openmc_filter_set_id
public :: openmc_filter_set_type
public :: openmc_finalize
public :: openmc_find
public :: openmc_get_cell_index
public :: openmc_get_keff
public :: openmc_get_filter_index
public :: openmc_get_filter_next_id
public :: openmc_get_material_index
public :: openmc_get_nuclide_index
public :: openmc_get_tally_index
public :: openmc_hard_reset
public :: openmc_init
public :: openmc_load_nuclide
public :: openmc_material_add_nuclide
public :: openmc_material_get_id
public :: openmc_material_get_densities
public :: openmc_material_set_density
public :: openmc_material_set_densities
public :: openmc_material_set_id
public :: openmc_material_filter_get_bins
public :: openmc_material_filter_set_bins
public :: openmc_mesh_filter_set_mesh
public :: openmc_nuclide_name
public :: openmc_plot_geometry
public :: openmc_reset
public :: openmc_run
public :: openmc_tally_get_id
public :: openmc_tally_get_filters
public :: openmc_tally_get_nuclides
public :: openmc_tally_results
public :: openmc_tally_set_filters
public :: openmc_tally_set_id
public :: openmc_tally_set_nuclides
public :: openmc_tally_set_scores
public :: openmc_tally_set_type
contains
!===============================================================================
! OPENMC_CELL_SET_TEMPERATURE sets the temperature of a cell
! OPENMC_FINALIZE frees up memory by deallocating arrays and resetting global
! variables
!===============================================================================
function openmc_cell_set_temperature(id, T, instance) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: id ! id of cell
real(C_DOUBLE), value, intent(in) :: T
integer(C_INT32_T), optional, intent(in) :: instance
integer(C_INT) :: err
subroutine openmc_finalize() bind(C)
integer :: i, n
integer :: err
err = -1
if (allocated(cells)) then
if (cell_dict % has_key(id)) then
i = cell_dict % get_key(id)
associate (c => cells(i))
if (allocated(c % sqrtkT)) then
n = size(c % sqrtkT)
if (present(instance) .and. n > 1) then
if (instance >= 0 .and. instance < n) then
c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * T)
err = 0
end if
else
c % sqrtkT(:) = sqrt(K_BOLTZMANN * T)
err = 0
end if
end if
end associate
end if
end if
end function openmc_cell_set_temperature
! Clear results
call openmc_reset()
! Reset global variables
assume_separate = .false.
check_overlaps = .false.
confidence_intervals = .false.
create_fission_neutrons = .true.
energy_cutoff = ZERO
energy_max_neutron = INFINITY
energy_min_neutron = ZERO
entropy_on = .false.
gen_per_batch = 1
keff = ONE
legendre_to_tabular = .true.
legendre_to_tabular_points = 33
n_batch_interval = 1
n_particles = 0
n_source_points = 0
n_state_points = 0
n_tallies = 0
output_summary = .true.
output_tallies = .true.
particle_restart_run = .false.
pred_batches = .false.
reduce_tallies = .true.
res_scat_on = .false.
res_scat_method = RES_SCAT_ARES
res_scat_energy_min = 0.01_8
res_scat_energy_max = 1000.0_8
restart_run = .false.
root_universe = -1
run_CE = .true.
run_mode = NONE
satisfy_triggers = .false.
seed = 1_8
source_latest = .false.
source_separate = .false.
source_write = .true.
survival_biasing = .false.
temperature_default = 293.6_8
temperature_method = TEMPERATURE_NEAREST
temperature_multipole = .false.
temperature_range = [ZERO, ZERO]
temperature_tolerance = 10.0_8
total_gen = 0
trigger_on = .false.
ufs = .false.
urr_ptables_on = .true.
verbosity = 7
weight_cutoff = 0.25_8
weight_survive = ONE
write_all_tracks = .false.
write_initial_source = .false.
! Deallocate arrays
call free_memory()
! Release compound datatypes
call h5tclose_f(hdf5_bank_t, err)
! Close FORTRAN interface.
call h5close_f(err)
#ifdef MPI
! Free all MPI types
call MPI_TYPE_FREE(MPI_BANK, err)
#endif
end subroutine openmc_finalize
!===============================================================================
! OPENMC_FIND determines the ID or a cell or material at a given point in space
!===============================================================================
subroutine openmc_find(xyz, rtype, id, instance) bind(C)
function openmc_find(xyz, rtype, id, instance) result(err) bind(C)
real(C_DOUBLE), intent(in) :: xyz(3) ! Cartesian point
integer(C_INT), intent(in), value :: rtype ! 1 for cell, 2 for material
integer(C_INT32_T), intent(out) :: id
integer(C_INT32_T), intent(out) :: instance
integer(C_INT) :: err
logical :: found
type(Particle) :: p
@ -89,6 +184,8 @@ contains
id = -1
instance = -1
err = E_UNASSIGNED
if (found) then
if (rtype == 1) then
id = cells(p % coord(p % n_coord) % cell) % id
@ -100,202 +197,53 @@ contains
end if
end if
instance = p % cell_instance - 1
end if
end subroutine openmc_find
!===============================================================================
! OPENMC_LOAD_NUCLIDE loads a nuclide from the cross section library
!===============================================================================
function openmc_load_nuclide(name) result(err) bind(C)
character(kind=C_CHAR) :: name(*)
integer(C_INT) :: err
integer :: n
integer(HID_T) :: file_id
integer(HID_T) :: group_id
character(:), allocatable :: name_
real(8) :: minmax(2) = [ZERO, INFINITY]
type(VectorReal) :: temperature
type(Nuclide), allocatable :: new_nuclides(:)
! Copy array of C_CHARs to normal Fortran string
name_ = to_f_string(name)
err = -1
if (.not. nuclide_dict % has_key(to_lower(name_))) then
if (library_dict % has_key(to_lower(name_))) then
! allocate extra space in nuclides array
n = n_nuclides_total
allocate(new_nuclides(n + 1))
new_nuclides(1:n) = nuclides(:)
call move_alloc(FROM=new_nuclides, TO=nuclides)
n = n + 1
i_library = library_dict % get_key(to_lower(name_))
! Open file and make sure version is sufficient
file_id = file_open(libraries(i_library) % path, 'r')
call check_data_version(file_id)
! Read nuclide data from HDF5
group_id = open_group(file_id, name_)
call nuclides(n) % from_hdf5(group_id, temperature, &
temperature_method, temperature_tolerance, minmax, &
master)
call close_group(group_id)
call file_close(file_id)
! Add entry to nuclide dictionary
call nuclide_dict % add_key(to_lower(name_), n)
n_nuclides_total = n
! Assign resonant scattering data
if (res_scat_on) call assign_0K_elastic_scattering(nuclides(n))
! Initialize nuclide grid
call nuclides(n) % init_grid(energy_min_neutron, &
energy_max_neutron, n_log_bins)
err = 0
else
err = -2
end if
err = 0
else
err = E_GEOMETRY
call set_errmsg("Could not find cell/material at position (" // &
trim(to_str(xyz(1))) // "," // trim(to_str(xyz(2))) // "," // &
trim(to_str(xyz(3))) // ").")
end if
end function openmc_load_nuclide
end function openmc_find
!===============================================================================
! OPENMC_MATERIAL_ADD_NUCLIDE
! OPENMC_HARD_RESET reset tallies and timers as well as the pseudorandom
! generator state
!===============================================================================
function openmc_material_add_nuclide(id, name, density) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: id
character(kind=C_CHAR) :: name(*)
real(C_DOUBLE), value, intent(in) :: density
integer(C_INT) :: err
subroutine openmc_hard_reset() bind(C)
integer :: err
integer :: i, j, k, n
integer :: err2
real(8) :: awr
integer, allocatable :: new_nuclide(:)
real(8), allocatable :: new_density(:)
character(:), allocatable :: name_
! Reset all tallies and timers
call openmc_reset()
name_ = to_f_string(name)
! Reset total generations and keff guess
keff = ONE
total_gen = 0
err = -1
if (allocated(materials)) then
if (material_dict % has_key(id)) then
i = material_dict % get_key(id)
associate (m => materials(i))
! Check if nuclide is already in material
do j = 1, size(m % nuclide)
k = m % nuclide(j)
if (nuclides(k) % name == name_) then
awr = nuclides(k) % awr
m % density = m % density + density - m % atom_density(j)
m % density_gpcc = m % density_gpcc + (density - &
m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO
m % atom_density(j) = density
err = 0
end if
end do
! If nuclide wasn't found, extend nuclide/density arrays
if (err /= 0) then
! If nuclide hasn't been loaded, load it now
err2 = openmc_load_nuclide(name)
if (err2 /= -2) then
! Extend arrays
n = size(m % nuclide)
allocate(new_nuclide(n + 1))
new_nuclide(1:n) = m % nuclide
call move_alloc(FROM=new_nuclide, TO=m % nuclide)
allocate(new_density(n + 1))
new_density(1:n) = m % atom_density
call move_alloc(FROM=new_density, TO=m % atom_density)
! Append new nuclide/density
k = nuclide_dict % get_key(to_lower(name_))
m % nuclide(n + 1) = k
m % atom_density(n + 1) = density
m % density = m % density + density
m % density_gpcc = m % density_gpcc + &
density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO
m % n_nuclides = n + 1
err = 0
end if
end if
end associate
end if
end if
end function openmc_material_add_nuclide
! Reset the random number generator state
err = openmc_set_seed(1_8)
end subroutine openmc_hard_reset
!===============================================================================
! OPENMC_MATERIAL_GET_DENSITIES returns an array of nuclide densities in a
! material
!===============================================================================
function openmc_material_get_densities(id, ptr) result(n) bind(C)
integer(C_INT32_T), intent(in), value :: id
type(C_PTR), intent(out) :: ptr
integer(C_INT) :: n
ptr = C_NULL_PTR
n = 0
if (allocated(materials)) then
if (material_dict % has_key(id)) then
i = material_dict % get_key(id)
associate (m => materials(i))
if (allocated(m % atom_density)) then
ptr = C_LOC(m % atom_density(1))
n = size(m % atom_density)
end if
end associate
end if
end if
end function openmc_material_get_densities
!===============================================================================
! OPENMC_MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm
!===============================================================================
function openmc_material_set_density(id, density) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: id
real(C_DOUBLE), value, intent(in) :: density
integer(C_INT) :: err
integer :: i
err = -1
if (allocated(materials)) then
if (material_dict % has_key(id)) then
i = material_dict % get_key(id)
associate (m => materials(i))
err = m % set_density(density, nuclides)
end associate
end if
end if
end function openmc_material_set_density
!===============================================================================
! OPENMC_RESET resets all tallies
! OPENMC_RESET resets tallies and timers
!===============================================================================
subroutine openmc_reset() bind(C)
integer :: i
do i = 1, size(tallies)
tallies(i) % n_realizations = 0
if (allocated(tallies(i) % results)) then
tallies(i) % results(:, :, :) = ZERO
end if
end do
if (allocated(tallies)) then
do i = 1, size(tallies)
associate (t => tallies(i) % obj)
t % active = .false.
t % n_realizations = 0
if (allocated(t % results)) then
t % results(:, :, :) = ZERO
end if
end associate
end do
end if
! Reset global tallies
n_realizations = 0
@ -307,10 +255,6 @@ contains
k_abs_tra = ZERO
k_sum(:) = ZERO
! Turn off tally flags
tallies_on = .false.
active_batches = .false.
! Clear active tally lists
call active_analog_tallies % clear()
call active_tracklength_tallies % clear()
@ -318,50 +262,61 @@ contains
call active_collision_tallies % clear()
call active_tallies % clear()
! Reset timers
call time_total % reset()
call time_total % reset()
call time_initialize % reset()
call time_read_xs % reset()
call time_unionize % reset()
call time_bank % reset()
call time_bank_sample % reset()
call time_bank_sendrecv % reset()
call time_tallies % reset()
call time_inactive % reset()
call time_active % reset()
call time_transport % reset()
call time_finalize % reset()
end subroutine openmc_reset
!===============================================================================
! OPENMC_TALLY_RESULTS returns a pointer to a tally results array along with its
! shape. This allows a user to obtain in-memory tally results from Python
! directly.
! FREE_MEMORY deallocates and clears all global allocatable arrays in the
! program
!===============================================================================
subroutine openmc_tally_results(id, ptr, shape_) bind(C)
integer(C_INT32_T), intent(in), value :: id
type(C_PTR), intent(out) :: ptr
integer(C_INT), intent(out) :: shape_(3)
subroutine free_memory()
integer :: i
use cmfd_header
use mgxs_header
use plot_header
use sab_header
use settings
use source_header
use surface_header
use tally_derivative_header
use trigger_header
use volume_header
ptr = C_NULL_PTR
if (allocated(tallies)) then
if (tally_dict % has_key(id)) then
i = tally_dict % get_key(id)
if (allocated(tallies(i) % results)) then
ptr = C_LOC(tallies(i) % results(1,1,1))
shape_(:) = shape(tallies(i) % results)
end if
end if
end if
end subroutine openmc_tally_results
call free_memory_geometry()
call free_memory_surfaces()
call free_memory_material()
call free_memory_plot()
call free_memory_volume()
call free_memory_simulation()
call free_memory_nuclide()
call free_memory_settings()
call free_memory_mgxs()
call free_memory_sab()
call free_memory_source()
call free_memory_mesh()
call free_memory_tally()
call free_memory_tally_filter()
call free_memory_tally_derivative()
call free_memory_bank()
function to_f_string(c_string) result(f_string)
character(kind=C_CHAR), intent(in) :: c_string(*)
character(:), allocatable :: f_string
! Deallocate CMFD
call deallocate_cmfd(cmfd)
integer :: i, n
! Determine length of original string
n = 0
do while (c_string(n + 1) /= C_NULL_CHAR)
n = n + 1
end do
! Copy C string character by character
allocate(character(len=n) :: f_string)
do i = 1, n
f_string(i:i) = c_string(i)
end do
end function to_f_string
end subroutine free_memory
end module openmc_api

View file

@ -19,4 +19,34 @@ module bank_header
integer(C_INT) :: particle ! particle type (neutron, photon, etc.)
end type Bank
! Source and fission bank
type(Bank), allocatable, target :: source_bank(:)
type(Bank), allocatable, target :: fission_bank(:)
#ifdef _OPENMP
type(Bank), allocatable, target :: master_fission_bank(:)
#endif
integer(8) :: n_bank ! # of sites in fission bank
!$omp threadprivate(fission_bank, n_bank)
contains
!===============================================================================
! FREE_MEMORY_BANK deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_bank()
! Deallocate fission and source bank and entropy
!$omp parallel
if (allocated(fission_bank)) deallocate(fission_bank)
!$omp end parallel
#ifdef _OPENMP
if (allocated(master_fission_bank)) deallocate(master_fission_bank)
#endif
if (allocated(source_bank)) deallocate(source_bank)
end subroutine free_memory_bank
end module bank_header

View file

@ -5,8 +5,10 @@ module cmfd_data
! parameters for CMFD calculation.
!==============================================================================
use cmfd_header, only: allocate_cmfd, cmfd, cmfd_coremap, &
cmfd_downscatter, cmfd_tallies, dhat_reset
use constants
use tally_filter, only: MeshFilter
use tally_filter_mesh, only: MeshFilter
implicit none
private
@ -20,9 +22,7 @@ contains
subroutine set_up_cmfd()
use cmfd_header, only: allocate_cmfd
use constants, only: CMFD_NOACCEL
use global, only: cmfd, cmfd_coremap, cmfd_downscatter
! Check for core map and set it up
if ((cmfd_coremap) .and. (cmfd%mat_dim == CMFD_NOACCEL)) call set_coremap()
@ -56,12 +56,9 @@ contains
IN_BACK, IN_FRONT, IN_BOTTOM, IN_TOP, CMFD_NOACCEL, &
ZERO, ONE, TINY_BIT
use error, only: fatal_error
use global, only: cmfd, n_cmfd_tallies, cmfd_tallies, meshes, &
filters, filter_matches
use mesh, only: mesh_indices_to_bin
use mesh_header, only: RegularMesh
use mesh_header, only: RegularMesh, meshes
use string, only: to_str
use tally_header, only: TallyObject
use tally_filter_header, only: filters, filter_matches
integer :: nx ! number of mesh cells in x direction
integer :: ny ! number of mesh cells in y direction
@ -76,7 +73,6 @@ contains
integer :: ital ! tally object index
integer :: ijk(3) ! indices for mesh cell
integer :: score_index ! index to pull from tally object
integer :: i_mesh ! index in meshes array
integer :: i_filt ! index in filters array
integer :: i_filter_mesh ! index for mesh filter
integer :: i_filter_ein ! index for incoming energy filter
@ -85,7 +81,6 @@ contains
integer :: stride_surf ! stride for surface filter
logical :: energy_filters! energy filters present
real(8) :: flux ! temp variable for flux
type(TallyObject), pointer :: t ! pointer for tally object
type(RegularMesh), pointer :: m ! pointer for mesh object
! Extract spatial and energy indices from object
@ -99,13 +94,14 @@ contains
cmfd % openmc_src = ZERO
! Associate tallies and mesh
t => cmfd_tallies(1)
i_filt = t % filter(t % find_filter(FILTER_MESH))
associate (t => cmfd_tallies(1) % obj)
i_filt = t % filter(t % find_filter(FILTER_MESH))
end associate
select type(filt => filters(i_filt) % obj)
type is (MeshFilter)
i_mesh = filt % mesh
m => meshes(filt % mesh)
end select
m => meshes(i_mesh)
! Set mesh widths
cmfd % hxyz(1,:,:,:) = m % width(1) ! set x width
@ -115,16 +111,15 @@ contains
cmfd % keff_bal = ZERO
! Begin loop around tallies
TAL: do ital = 1, n_cmfd_tallies
TAL: do ital = 1, size(cmfd_tallies)
! Associate tallies and mesh
t => cmfd_tallies(ital)
associate (t => cmfd_tallies(ital) % obj)
i_filt = t % filter(t % find_filter(FILTER_MESH))
select type(filt => filters(i_filt) % obj)
type is (MeshFilter)
i_mesh = filt % mesh
m => meshes(filt % mesh)
end select
m => meshes(i_mesh)
! Check for energy filters
energy_filters = (t % find_filter(FILTER_ENERGYIN) > 0)
@ -166,7 +161,7 @@ contains
! Get bin number for mesh indices
filter_matches(i_filter_mesh) % bins % data(1) = &
mesh_indices_to_bin(m,ijk)
m % get_bin_from_indices(ijk)
! Apply energy in filter
if (energy_filters) then
@ -217,7 +212,7 @@ contains
! Get bin number for mesh indices
filter_matches(i_filter_mesh) % bins % data(1) = &
mesh_indices_to_bin(m,ijk)
m % get_bin_from_indices(ijk)
if (energy_filters) then
! Apply energy in filter
@ -266,7 +261,7 @@ contains
! Get the bin for this mesh cell
filter_matches(i_filter_mesh) % bins % data(1) = &
mesh_indices_to_bin(m, (/ i, j, k /))
m % get_bin_from_indices([ i, j, k ])
score_index = 1
do l = 1, size(t % filter)
@ -321,13 +316,13 @@ contains
end do ZLOOP
end associate
end do TAL
! Normalize openmc source distribution
cmfd % openmc_src = cmfd % openmc_src/sum(cmfd % openmc_src)*cmfd%norm
! Nullify all pointers
if (associated(t)) nullify(t)
if (associated(m)) nullify(m)
end subroutine compute_xs
@ -339,7 +334,6 @@ contains
subroutine set_coremap()
use constants, only: CMFD_NOACCEL
use global, only: cmfd
integer :: counter=1 ! counter for unique fuel assemblies
integer :: nx ! number of mesh cells in x direction
@ -400,7 +394,7 @@ contains
subroutine neutron_balance()
use constants, only: ONE, ZERO, CMFD_NOACCEL, CMFD_NORES
use global, only: cmfd, keff, current_batch
use simulation_header, only: keff, current_batch
integer :: nx ! number of mesh cells in x direction
integer :: ny ! number of mesh cells in y direction
@ -509,7 +503,6 @@ contains
subroutine compute_dtilde()
use constants, only: CMFD_NOACCEL, ZERO_FLUX, TINY_BIT
use global, only: cmfd, cmfd_coremap
integer :: nx ! maximum number of cells in x direction
integer :: ny ! maximum number of cells in y direction
@ -651,7 +644,6 @@ contains
subroutine compute_dhat()
use constants, only: CMFD_NOACCEL, ZERO
use global, only: cmfd, cmfd_coremap, dhat_reset
use output, only: write_message
use string, only: to_str
@ -801,7 +793,6 @@ contains
function get_reflector_albedo(l, g, i, j, k)
use constants, only: ONE
use global, only: cmfd
real(8) :: get_reflector_albedo ! reflector albedo
integer, intent(in) :: i ! iteration counter for x
@ -840,7 +831,6 @@ contains
subroutine compute_effective_downscatter()
use constants, only: ZERO, CMFD_NOACCEL
use global, only: cmfd
integer :: nx ! number of mesh cells in x direction
integer :: ny ! number of mesh cells in y direction

View file

@ -5,7 +5,9 @@ module cmfd_execute
! cross section generation, diffusion calculation, and source re-weighting
!==============================================================================
use global
use cmfd_header
use settings
use simulation_header
implicit none
private
@ -63,10 +65,6 @@ contains
subroutine cmfd_init_batch()
use global, only: cmfd_begin, cmfd_on, &
cmfd_reset, cmfd_run, &
current_batch
! Check to activate CMFD diffusion and possible feedback
! this guarantees that when cmfd begins at least one batch of tallies are
! accumulated
@ -91,7 +89,6 @@ contains
subroutine calc_fission_source()
use constants, only: CMFD_NOACCEL, ZERO, TWO
use global, only: cmfd, cmfd_coremap, entropy_on, current_batch
use message_passing
use string, only: to_str
@ -108,6 +105,9 @@ contains
real(8) :: hxyz(3) ! cell dimensions of current ijk cell
real(8) :: vol ! volume of cell
real(8),allocatable :: source(:,:,:,:) ! tmp source array for entropy
#ifdef MPI
integer :: mpi_err ! MPI error code
#endif
! Get maximum of spatial and group indices
nx = cmfd % indices(1)
@ -211,11 +211,11 @@ contains
subroutine cmfd_reweight(new_weights)
use algorithm, only: binary_search
use bank_header, only: source_bank
use constants, only: ZERO, ONE
use error, only: warning, fatal_error
use global, only: meshes, source_bank, work, n_user_meshes, cmfd
use mesh_header, only: RegularMesh
use mesh, only: count_bank_sites, get_mesh_indices
use mesh, only: count_bank_sites
use message_passing
use string, only: to_str
@ -226,15 +226,17 @@ contains
integer :: nz ! maximum number of cells in z direction
integer :: ng ! maximum number of energy groups
integer :: i ! iteration counter
integer :: g ! index for group
integer :: ijk(3) ! spatial bin location
integer :: e_bin ! energy bin of source particle
integer :: mesh_bin ! mesh bin of soruce particle
integer :: n_groups ! number of energy groups
real(8) :: norm ! normalization factor
logical :: outside ! any source sites outside mesh
logical :: in_mesh ! source site is inside mesh
type(RegularMesh), pointer :: m ! point to mesh
! Associate pointer
m => meshes(n_user_meshes + 1)
#ifdef MPI
integer :: mpi_err
#endif
! Get maximum of spatial and group indices
nx = cmfd % indices(1)
@ -244,7 +246,7 @@ contains
! allocate arrays in cmfd object (can take out later extend to multigroup)
if (.not.allocated(cmfd%sourcecounts)) then
allocate(cmfd%sourcecounts(ng,nx,ny,nz))
allocate(cmfd%sourcecounts(ng, nx*ny*nz))
cmfd % sourcecounts = 0
end if
if (.not.allocated(cmfd % weightfactors)) then
@ -259,9 +261,8 @@ contains
cmfd%weightfactors = ONE
! Count bank sites in mesh and reverse due to egrid structure
call count_bank_sites(m, source_bank, cmfd%sourcecounts, cmfd % egrid, &
sites_outside=outside, size_bank=work)
cmfd % sourcecounts = cmfd%sourcecounts(ng:1:-1,:,:,:)
call count_bank_sites(cmfd_mesh, source_bank, cmfd%sourcecounts, &
cmfd % egrid, sites_outside=outside, size_bank=work)
! Check for sites outside of the mesh
if (master .and. outside) then
@ -270,10 +271,21 @@ contains
! Have master compute weight factors (watch for 0s)
if (master) then
where(cmfd % cmfd_src > ZERO .and. cmfd % sourcecounts > ZERO)
cmfd % weightfactors = cmfd % cmfd_src/sum(cmfd % cmfd_src)* &
sum(cmfd % sourcecounts) / cmfd % sourcecounts
end where
! Calculate normalization factor
norm = sum(cmfd % sourcecounts) / sum(cmfd % cmfd_src)
do mesh_bin = 1, nx*ny*nz
call cmfd_mesh % get_indices_from_bin(mesh_bin, ijk)
do g = 1, ng
if (cmfd % sourcecounts(ng - g + 1, mesh_bin) > ZERO) then
if (cmfd % cmfd_src(g,ijk(1),ijk(2),ijk(3)) > ZERO) then
cmfd % weightfactors(g,ijk(1),ijk(2),ijk(3)) = &
cmfd % cmfd_src(g,ijk(1),ijk(2),ijk(3)) * norm &
/ cmfd % sourcecounts(ng - g + 1, mesh_bin)
end if
end if
end do
end do
end if
if (.not. cmfd_feedback) return
@ -289,7 +301,7 @@ contains
do i = 1, int(work,4)
! Determine spatial bin
call get_mesh_indices(m, source_bank(i) % xyz, ijk, in_mesh)
call cmfd_mesh % get_indices(source_bank(i) % xyz, ijk, in_mesh)
! Determine energy bin
n_groups = size(cmfd % egrid) - 1
@ -313,8 +325,7 @@ contains
! Reweight particle
source_bank(i) % wgt = source_bank(i) % wgt * &
cmfd % weightfactors(e_bin, ijk(1), ijk(2), ijk(3))
cmfd % weightfactors(e_bin, ijk(1), ijk(2), ijk(3))
end do
end subroutine cmfd_reweight
@ -325,8 +336,6 @@ contains
function get_matrix_idx(g, i, j, k, ng, nx, ny) result (matidx)
use global, only: cmfd, cmfd_coremap
integer :: matidx ! the index location in matrix
integer, intent(in) :: i ! current x index
integer, intent(in) :: j ! current y index
@ -357,7 +366,6 @@ contains
subroutine cmfd_tally_reset()
use global, only: cmfd_tallies
use output, only: write_message
integer :: i ! loop counter
@ -367,8 +375,8 @@ contains
! Reset CMFD tallies
do i = 1, size(cmfd_tallies)
cmfd_tallies(i) % n_realizations = 0
cmfd_tallies(i) % results(:,:,:) = ZERO
cmfd_tallies(i) % obj % n_realizations = 0
cmfd_tallies(i) % obj % results(:,:,:) = ZERO
end do
end subroutine cmfd_tally_reset

View file

@ -1,6 +1,10 @@
module cmfd_header
use constants, only: CMFD_NOACCEL, ZERO, ONE
use mesh_header, only: RegularMesh
use set_header, only: SetInt
use tally_header, only: TallyContainer
use timer_header, only: Timer
implicit none
private
@ -49,7 +53,7 @@ module cmfd_header
real(8), allocatable :: openmc_src(:,:,:,:)
! Source sites in each mesh box
real(8), allocatable :: sourcecounts(:,:,:,:)
real(8), allocatable :: sourcecounts(:,:)
! Weight adjustment factors
real(8), allocatable :: weightfactors(:,:,:,:)
@ -88,6 +92,67 @@ module cmfd_header
end type cmfd_type
! Main object
type(cmfd_type), public :: cmfd
type(RegularMesh), public, pointer :: cmfd_mesh => null()
! Pointers for different tallies
type(TallyContainer), public, pointer :: cmfd_tallies(:) => null()
! Timing objects
type(Timer), public :: time_cmfd ! timer for whole cmfd calculation
type(Timer), public :: time_cmfdbuild ! timer for matrix build
type(Timer), public :: time_cmfdsolve ! timer for solver
! Flag for active core map
logical, public :: cmfd_coremap = .false.
! Flag to reset dhats to zero
logical, public :: dhat_reset = .false.
! Flag to activate neutronic feedback via source weights
logical, public :: cmfd_feedback = .false.
! Adjoint method type
character(len=10), public :: cmfd_adjoint_type = 'physical'
! Number of incomplete ilu factorization levels
integer, public :: cmfd_ilu_levels = 1
! Batch to begin cmfd
integer, public :: cmfd_begin = 1
! Tally reset list
integer, public :: n_cmfd_resets
type(SetInt), public :: cmfd_reset
! Compute effective downscatter cross section
logical, public :: cmfd_downscatter = .false.
! Convergence monitoring
logical, public :: cmfd_power_monitor = .false.
! Cmfd output
logical, public :: cmfd_write_matrices = .false.
! Run an adjoint calculation (last batch only)
logical, public :: cmfd_run_adjoint = .false.
! CMFD run logicals
logical, public :: cmfd_on = .false.
! CMFD display info
character(len=25), public :: cmfd_display = 'balance'
! Estimate of spectral radius of CMFD matrices and tolerances
real(8), public :: cmfd_spectral = ZERO
real(8), public :: cmfd_shift = 1.e6
real(8), public :: cmfd_ktol = 1.e-8_8
real(8), public :: cmfd_stol = 1.e-8_8
real(8), public :: cmfd_atoli = 1.e-10_8
real(8), public :: cmfd_rtoli = 1.e-5_8
contains
!==============================================================================
@ -133,7 +198,7 @@ contains
if (.not. allocated(this % openmc_src)) allocate(this % openmc_src(ng,nx,ny,nz))
! Allocate source weight modification vars
if (.not. allocated(this % sourcecounts)) allocate(this % sourcecounts(ng,nx,ny,nz))
if (.not. allocated(this % sourcecounts)) allocate(this % sourcecounts(ng,nx*ny*nz))
if (.not. allocated(this % weightfactors)) allocate(this % weightfactors(ng,nx,ny,nz))
! Allocate batchwise parameters

View file

@ -1,6 +1,13 @@
module cmfd_input
use global
use, intrinsic :: ISO_C_BINDING
use cmfd_header
use mesh_header, only: mesh_dict
use mgxs_header, only: energy_bins
use tally
use tally_header
use timer_header
implicit none
private
@ -14,21 +21,9 @@ contains
subroutine configure_cmfd()
use cmfd_header, only: allocate_cmfd
use message_passing, only: master
integer :: color ! color group of processor
! Read in cmfd input file
call read_cmfd_xml()
! Assign color
if (master) then
color = 1
else
color = 2
end if
! Initialize timers
call time_cmfd % reset()
call time_cmfdbuild % reset()
@ -47,7 +42,6 @@ contains
use constants, only: ZERO, ONE
use error, only: fatal_error, warning
use global
use output, only: write_message
use string, only: to_lower
use xml_interface
@ -241,20 +235,19 @@ contains
! There are 3 tally types:
! 1: Only an energy in filter-> flux,total,p1 scatter
! 2: Energy in and energy out filter-> nu-scatter,nu-fission
! 3: Surface current
! 3: Mesh current
!===============================================================================
subroutine create_cmfd_tally(root)
use constants, only: MAX_LINE_LEN
use error, only: fatal_error, warning
use mesh_header, only: RegularMesh
use mesh_header, only: RegularMesh, openmc_extend_meshes
use string
use tally, only: setup_active_cmfdtallies
use tally_header, only: TallyObject
use tally, only: openmc_tally_set_type
use tally_header, only: openmc_extend_tallies
use tally_filter_header
use tally_filter
use tally_initialize, only: add_tallies
use xml_interface
type(XMLNode), intent(in) :: root ! XML root element
@ -262,25 +255,28 @@ contains
logical :: energy_filters
integer :: i ! loop counter
integer :: n ! size of arrays in mesh specification
integer :: ng ! number of energy groups (default 1)
integer(C_INT32_T) :: ng ! number of energy groups (default 1)
integer :: n_filter ! number of filters
integer :: i_filt ! index in filters array
integer :: i_start, i_end
integer :: i_filt_start, i_filt_end
integer(C_INT32_T), allocatable :: filter_indices(:)
integer(C_INT) :: err
integer :: i_filt ! index in filters array
integer :: filt_id
integer :: iarray3(3) ! temp integer array
real(8) :: rarray3(3) ! temp double array
type(TallyObject), pointer :: t
real(C_DOUBLE), allocatable :: energies(:)
type(RegularMesh), pointer :: m
type(XMLNode) :: node_mesh
! Set global variables if they are 0 (this can happen if there is no tally
! file)
if (n_meshes == 0) n_meshes = n_user_meshes + n_cmfd_meshes
err = openmc_extend_meshes(1, i_start)
! Allocate mesh
if (.not. allocated(meshes)) allocate(meshes(n_meshes))
m => meshes(n_user_meshes+1)
cmfd_mesh => meshes(i_start)
m => meshes(i_start)
! Set mesh id
m % id = n_user_meshes + 1
m % id = i_start
! Set mesh type to rectangular
m % type = LATTICE_RECT
@ -374,71 +370,54 @@ contains
m % volume_frac = ONE/real(product(m % dimension),8)
! Add mesh to dictionary
call mesh_dict % add_key(m % id, n_user_meshes + 1)
call mesh_dict % set(m % id, i_start)
! Determine number of filters
energy_filters = check_for_node(node_mesh, "energy")
n_cmfd_filters = merge(5, 3, energy_filters)
n = merge(5, 3, energy_filters)
! Extend filters array so we can add CMFD filters
call add_filters(n_cmfd_filters)
err = openmc_extend_filters(n, i_filt_start, i_filt_end)
! Set up mesh filter
i_filt = n_user_filters + 1
allocate(MeshFilter :: filters(i_filt) % obj)
select type (filt => filters(i_filt) % obj)
type is (MeshFilter)
filt % id = i_filt
filt % n_bins = product(m % dimension)
filt % mesh = n_user_meshes + 1
! Add filter to dictionary
call filter_dict % add_key(filt % id, i_filt)
end select
i_filt = i_filt_start
err = openmc_filter_set_type(i_filt, C_CHAR_'mesh' // C_NULL_CHAR)
call openmc_get_filter_next_id(filt_id)
err = openmc_filter_set_id(i_filt, filt_id)
err = openmc_mesh_filter_set_mesh(i_filt, i_start)
if (energy_filters) then
! Read and set incoming energy mesh filter
i_filt = i_filt + 1
allocate(EnergyFilter :: filters(i_filt) % obj)
select type (filt => filters(i_filt) % obj)
type is (EnergyFilter)
filt % id = i_filt
ng = node_word_count(node_mesh, "energy")
filt % n_bins = ng - 1
allocate(filt % bins(ng))
call get_node_array(node_mesh, "energy", filt % bins)
! Add filter to dictionary
call filter_dict % add_key(filt % id, i_filt)
end select
err = openmc_filter_set_type(i_filt, C_CHAR_'energy' // C_NULL_CHAR)
call openmc_get_filter_next_id(filt_id)
err = openmc_filter_set_id(i_filt, filt_id)
! Get energies and set bins
ng = node_word_count(node_mesh, "energy")
allocate(energies(ng))
call get_node_array(node_mesh, "energy", energies)
err = openmc_energy_filter_set_bins(i_filt, ng, energies)
! Read and set outgoing energy mesh filter
i_filt = i_filt + 1
allocate(EnergyoutFilter :: filters(i_filt) % obj)
select type (filt => filters(i_filt) % obj)
type is (EnergyoutFilter)
filt % id = i_filt
ng = node_word_count(node_mesh, "energy")
filt % n_bins = ng - 1
allocate(filt % bins(ng))
call get_node_array(node_mesh, "energy", filt % bins)
! Add filter to dictionary
call filter_dict % add_key(filt % id, i_filt)
end select
err = openmc_filter_set_type(i_filt, C_CHAR_'energyout' // C_NULL_CHAR)
call openmc_get_filter_next_id(filt_id)
err = openmc_filter_set_id(i_filt, filt_id)
err = openmc_energy_filter_set_bins(i_filt, ng, energies)
end if
! Duplicate the mesh filter for the surface current tally since other
! Duplicate the mesh filter for the mesh current tally since other
! tallies use this filter and we need to change the dimension
i_filt = i_filt + 1
allocate(MeshFilter :: filters(i_filt) % obj)
select type (filt => filters(i_filt) % obj)
type is (MeshFilter)
filt % id = i_filt
! We need to increase the dimension by one since we also need
! currents coming into and out of the boundary mesh cells.
filt % n_bins = product(m % dimension + 1)
filt % mesh = n_user_meshes + 1
! Add filter to dictionary
call filter_dict % add_key(filt % id, i_filt)
end select
err = openmc_filter_set_type(i_filt, C_CHAR_'mesh' // C_NULL_CHAR)
call openmc_get_filter_next_id(filt_id)
err = openmc_filter_set_id(i_filt, filt_id)
err = openmc_mesh_filter_set_mesh(i_filt, i_start)
! We need to increase the dimension by one since we also need
! currents coming into and out of the boundary mesh cells.
filters(i_filt) % obj % n_bins = product(m % dimension + 1)
! Set up surface filter
i_filt = i_filt + 1
@ -458,32 +437,41 @@ contains
end if
filt % current = .true.
! Add filter to dictionary
call filter_dict % add_key(filt % id, i_filt)
call filter_dict % set(filt % id, i_filt)
end select
! Initialize filters
do i = i_filt_start, i_filt_end
select type (filt => filters(i) % obj)
type is (SurfaceFilter)
! Don't do anything
class default
call filt % initialize()
end select
end do
! Allocate tallies
call add_tallies("cmfd", n_cmfd_tallies)
err = openmc_extend_tallies(3, i_start, i_end)
cmfd_tallies => tallies(i_start:i_end)
! Begin loop around tallies
do i = 1, n_cmfd_tallies
do i = 1, size(cmfd_tallies)
! Allocate tally
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR)
! Point t to tally variable
t => cmfd_tallies(i)
associate (t => cmfd_tallies(i) % obj)
! Set reset property
if (check_for_node(root, "reset")) then
call get_node_value(root, "reset", t % reset)
end if
! Set the mesh filter index in the tally find_filter array
n_filter = 1
t % find_filter(FILTER_MESH) = n_filter
! Set the incoming energy mesh filter index in the tally find_filter
! array
n_filter = 1
if (energy_filters) then
n_filter = n_filter + 1
t % find_filter(FILTER_ENERGYIN) = n_filter
end if
! Set number of nucilde bins
@ -492,7 +480,7 @@ contains
t % n_nuclide_bins = 1
! Record tally id which is equivalent to loop number
t % id = i_cmfd_tallies + i
t % id = i_start + i - 1
if (i == 1) then
@ -506,11 +494,13 @@ contains
t % type = TALLY_VOLUME
! Allocate and set filters
allocate(t % filter(n_filter))
t % filter(1) = n_user_filters + 1
allocate(filter_indices(n_filter))
filter_indices(1) = i_filt_start
if (energy_filters) then
t % filter(2) = n_user_filters + 2
filter_indices(2) = i_filt_start + 1
end if
err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices)
deallocate(filter_indices)
! Allocate scoring bins
allocate(t % score_bins(3))
@ -542,16 +532,17 @@ contains
! array
if (energy_filters) then
n_filter = n_filter + 1
t % find_filter(FILTER_ENERGYOUT) = n_filter
end if
! Allocate and set indices in filters array
allocate(t % filter(n_filter))
t % filter(1) = n_user_filters + 1
allocate(filter_indices(n_filter))
filter_indices(1) = i_filt_start
if (energy_filters) then
t % filter(2) = n_user_filters + 2
t % filter(3) = n_user_filters + 3
filter_indices(2) = i_filt_start + 1
filter_indices(3) = i_filt_start + 2
end if
err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices)
deallocate(filter_indices)
! Allocate macro reactions
allocate(t % score_bins(2))
@ -576,15 +567,16 @@ contains
! Set the surface filter index in the tally find_filter array
n_filter = n_filter + 1
t % find_filter(FILTER_SURFACE) = n_filter
! Allocate and set filters
allocate(t % filter(n_filter))
t % filter(1) = n_user_filters + n_cmfd_filters - 1
t % filter(n_filter) = n_user_filters + n_cmfd_filters
allocate(filter_indices(n_filter))
filter_indices(1) = i_filt_end - 1
filter_indices(n_filter) = i_filt_end
if (energy_filters) then
t % filter(2) = n_user_filters + 2
filter_indices(2) = i_filt_start + 1
end if
err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices)
deallocate(filter_indices)
! Allocate macro reactions
allocate(t % score_bins(1))
@ -597,14 +589,14 @@ contains
! Set macro bins
t % score_bins(1) = SCORE_CURRENT
t % type = TALLY_SURFACE_CURRENT
t % type = TALLY_MESH_CURRENT
end if
end do
! Make CMFD tallies active from the start
t % active = .true.
! Put cmfd tallies into active tally array and turn tallies on
call setup_active_cmfdtallies()
tallies_on = .true.
end associate
end do
end subroutine create_cmfd_tally

View file

@ -1,7 +1,7 @@
module cmfd_loss_operator
use constants, only: CMFD_NOACCEL, ZERO
use global, only: cmfd, cmfd_coremap
use cmfd_header, only: cmfd, cmfd_coremap
use matrix_header, only: Matrix
implicit none

View file

@ -1,7 +1,7 @@
module cmfd_prod_operator
use constants, only: CMFD_NOACCEL
use global, only: cmfd, cmfd_coremap
use cmfd_header, only: cmfd, cmfd_coremap
use matrix_header, only: Matrix
implicit none

View file

@ -53,7 +53,7 @@ contains
subroutine cmfd_solver_execute(adjoint)
use global, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve
use cmfd_header, only: cmfd_adjoint_type, time_cmfdbuild, time_cmfdsolve
logical, optional, intent(in) :: adjoint ! adjoint calc
@ -100,8 +100,8 @@ contains
subroutine init_data(adjoint)
use constants, only: ONE, ZERO
use global, only: cmfd_shift, keff, cmfd_ktol, cmfd_stol, &
cmfd_write_matrices
use cmfd_header, only: cmfd_shift, cmfd_ktol, cmfd_stol, cmfd_write_matrices
use simulation_header, only: keff
logical, intent(in) :: adjoint
@ -167,7 +167,7 @@ contains
subroutine compute_adjoint()
use error, only: fatal_error
use global, only: cmfd_write_matrices
use cmfd_header, only: cmfd_write_matrices
! Transpose matrices
loss = loss % transpose()
@ -190,7 +190,7 @@ contains
use constants, only: ONE
use error, only: fatal_error
use global, only: cmfd, cmfd_atoli, cmfd_rtoli
use cmfd_header, only: cmfd, cmfd_atoli, cmfd_rtoli
integer :: i ! iteration counter
integer :: innerits ! # of inner iterations
@ -304,7 +304,7 @@ contains
use, intrinsic :: ISO_FORTRAN_ENV
use constants, only: ONE, ZERO
use global, only: cmfd_power_monitor
use cmfd_header, only: cmfd_power_monitor
use message_passing, only: master
integer, intent(in) :: iter ! outer iteration number
@ -346,7 +346,7 @@ contains
use constants, only: ONE, ZERO
use error, only: fatal_error
use global, only: cmfd, cmfd_spectral
use cmfd_header, only: cmfd, cmfd_spectral
type(Matrix), intent(inout) :: A ! coefficient matrix
type(Vector), intent(inout) :: b ! right hand side vector
@ -452,7 +452,7 @@ contains
use constants, only: ONE, ZERO
use error, only: fatal_error
use global, only: cmfd, cmfd_spectral
use cmfd_header, only: cmfd, cmfd_spectral
type(Matrix), intent(inout) :: A ! coefficient matrix
type(Vector), intent(inout) :: b ! right hand side vector
@ -597,7 +597,7 @@ contains
use constants, only: ONE, ZERO
use error, only: fatal_error
use global, only: cmfd, cmfd_spectral
use cmfd_header, only: cmfd, cmfd_spectral
type(Matrix), intent(inout) :: A ! coefficient matrix
type(Vector), intent(inout) :: b ! right hand side vector
@ -693,7 +693,8 @@ contains
subroutine extract_results()
use global, only: cmfd, cmfd_write_matrices, current_batch
use cmfd_header, only: cmfd, cmfd_write_matrices
use simulation_header, only: current_batch
character(len=25) :: filename ! name of file to write data
integer :: n ! problem size
@ -750,7 +751,7 @@ contains
subroutine matrix_to_indices(irow, g, i, j, k, ng, nx, ny, nz)
use global, only: cmfd, cmfd_coremap
use cmfd_header, only: cmfd, cmfd_coremap
integer, intent(out) :: i ! iteration counter for x
integer, intent(out) :: j ! iteration counter for y

View file

@ -302,7 +302,8 @@ module constants
! Tally type
integer, parameter :: &
TALLY_VOLUME = 1, &
TALLY_SURFACE_CURRENT = 2
TALLY_MESH_CURRENT = 2, &
TALLY_SURFACE = 3
! Tally estimator types
integer, parameter :: &
@ -332,7 +333,7 @@ module constants
SCORE_FISSION = -10, & ! fission rate
SCORE_NU_FISSION = -11, & ! neutron production rate
SCORE_KAPPA_FISSION = -12, & ! fission energy production rate
SCORE_CURRENT = -13, & ! partial current
SCORE_CURRENT = -13, & ! current
SCORE_FLUX_YN = -14, & ! angular moment of flux
SCORE_TOTAL_YN = -15, & ! angular moment of total reaction rate
SCORE_SCATTER_YN = -16, & ! angular flux-weighted scattering moment (0:N)
@ -366,7 +367,7 @@ module constants
integer, parameter :: NO_BIN_FOUND = -1
! Tally filter and map types
integer, parameter :: N_FILTER_TYPES = 15
integer, parameter :: N_FILTER_TYPES = 16
integer, parameter :: &
FILTER_UNIVERSE = 1, &
FILTER_MATERIAL = 2, &
@ -382,7 +383,8 @@ module constants
FILTER_AZIMUTHAL = 12, &
FILTER_DELAYEDGROUP = 13, &
FILTER_ENERGYFUNCTION = 14, &
FILTER_PARTICLE = 15
FILTER_CELLFROM = 15, &
FILTER_PARTICLE = 16
! Mesh types
integer, parameter :: &

View file

@ -3,17 +3,19 @@ module cross_section
use algorithm, only: binary_search
use constants
use error, only: fatal_error
use global
use list_header, only: ListElemInt
use material_header, only: Material
use material_header, only: Material, materials
use math, only: faddeeva, w_derivative, broaden_wmp_polynomials
use multipole_header, only: FORM_RM, FORM_MLBW, MP_EA, RM_RT, RM_RA, RM_RF, &
MLBW_RT, MLBW_RX, MLBW_RA, MLBW_RF, FIT_T, FIT_A,&
FIT_F, MultipoleArray
use nuclide_header
use particle_header, only: Particle
use photon_header, only: micro_photon_xs, elements
use random_lcg, only: prn, future_prn, prn_set_stream
use sab_header, only: SAlphaBeta
use sab_header, only: SAlphaBeta, sab_tables
use settings
use simulation_header
implicit none

View file

@ -7,438 +7,535 @@ module dict_header
! pairs. This data structure is used to provide lookup features, e.g. cells and
! surfaces by name.
!
! The original version was roughly based on capabilities in the 'flibs' open
! source package. However, it was rewritten from scratch so that it could be
! used stand-alone without relying on the implementation of lists. As with
! lists, it was considered writing a single dictionary used unlimited
! polymorphism, but again compiler support is spotty and doesn't always prevent
! duplication of code.
! The implementation is based on Algorithm D from Knuth Vol. 3 Sec. 6.4 (open
! addressing with double hashing). Hash table sizes M are chosen such that M
! and M - 2 are twin primes, which helps reduce clustering. The sequence of
! twin primes used for the table sizes comes from
! https://github.com/anholt/hash_table/blob/master/hash_table.c. These values
! were selected so that the table would grow by approximately a factor of two
! each time the maximum load factor is exceeded. An upper limit is placed on
! the load factor to prevent exponential performance degradation as the number
! of entries approaches the number of buckets.
!===============================================================================
implicit none
integer, parameter, private :: HASH_SIZE = 4993
integer, parameter, private :: HASH_MULTIPLIER = 31
integer, parameter, private :: DICT_NULL = -huge(0)
integer, parameter :: DICT_KEY_LENGTH = 255
integer, parameter :: EMPTY = -huge(0)
integer, parameter, private :: DELETED = -huge(0) + 1
integer, parameter, private :: KEY_CHAR_LENGTH = 255
integer, parameter, private :: TABLE_SIZES(30) = &
[5, 7, 13, 19, 43, 73, 151, 283, 571, 1153, 2269, 4519, 9013, 18043, &
36109, 72091, 144409, 288361, 576883, 1153459, 2307163, 4613893, &
9227641, 18455029, 36911011, 73819861, 147639589, 295279081, &
590559793, 1181116273]
real(8), parameter, private :: MAX_LOAD_FACTOR = 0.65
!===============================================================================
! ELEMKEYVALUE* contains (key,value) pairs and a pointer to the next (key,value)
! pair
! DICTENTRY* contains (key,value) pairs.
!===============================================================================
type ElemKeyValueCI
type(ElemKeyValueCI), pointer :: next => null()
character(len=DICT_KEY_LENGTH) :: key
integer :: value
end type ElemKeyValueCI
type DictEntryII
integer :: key = EMPTY
integer :: value = EMPTY
end type DictEntryII
type ElemKeyValueII
type(ElemKeyValueII), pointer :: next => null()
integer :: key
integer :: value
end type ElemKeyValueII
type DictEntryCI
character(len=KEY_CHAR_LENGTH) :: key
integer :: value = EMPTY
end type DictEntryCI
!===============================================================================
! HASHLIST* types contain a single pointer to a linked list of (key,value)
! pairs. This type is necesssary so that the Dict types can be dynamically
! allocated.
! BUCKET* contains an allocatable DictEntry object for storing a (key,value)
! pair and the hash value for fast comparisons. Integer (key,value) pairs are
! stored directly in the hash table since their memory requirement is small.
!===============================================================================
type, private :: HashListCI
type(ElemKeyValueCI), pointer :: list => null()
end type HashListCI
type, private :: HashListII
type(ElemKeyValueII), pointer :: list => null()
end type HashListII
type, private :: BucketCI
type(DictEntryCI), allocatable :: entry
integer :: hash = EMPTY
end type BucketCI
!===============================================================================
! DICT* is a dictionary of (key,value) pairs with convenience methods as
! type-bound procedures. DictCharInt has character(*) keys and integer values,
! and DictIntInt has integer keys and values.
! type-bound procedures. DictIntInt has integer keys and values, and
! DictCharInt has character(*) keys and integer values.
!===============================================================================
type, public :: DictCharInt
private
type(HashListCI), pointer :: table(:) => null()
contains
procedure :: add_key => dict_add_key_ci
procedure :: get_key => dict_get_key_ci
procedure :: has_key => dict_has_key_ci
procedure :: keys => dict_keys_ci
procedure :: clear => dict_clear_ci
procedure, private :: get_elem => dict_get_elem_ci
end type DictCharInt
type, public :: DictIntInt
private
type(HashListII), pointer :: table(:) => null()
integer, private :: entries = 0
integer, private :: capacity = 0
type(DictEntryII), allocatable, private :: table(:)
contains
procedure :: add_key => dict_add_key_ii
procedure :: get_key => dict_get_key_ii
procedure :: has_key => dict_has_key_ii
procedure :: keys => dict_keys_ii
procedure :: clear => dict_clear_ii
procedure, private :: get_elem => dict_get_elem_ii
procedure :: set => set_ii
procedure :: get => get_ii
procedure :: has => has_ii
procedure :: remove => remove_ii
procedure :: next_entry => next_entry_ii
procedure :: clear => clear_ii
procedure :: size => size_ii
procedure, private :: get_entry => get_entry_ii
procedure, private :: resize => resize_ii
end type DictIntInt
type, public :: DictCharInt
integer, private :: entries = 0
integer, private :: capacity = 0
type(BucketCI), allocatable, private :: table(:)
contains
procedure :: set => set_ci
procedure :: get => get_ci
procedure :: has => has_ci
procedure :: remove => remove_ci
procedure :: next_entry => next_entry_ci
procedure :: clear => clear_ci
procedure :: size => size_ci
procedure, private :: get_entry => get_entry_ci
procedure, private :: resize => resize_ci
end type DictCharInt
contains
!===============================================================================
! DICT_ADD_KEY adds a (key,value) entry to a dictionary. If the key is already
! in the dictionary, the value is replaced by the new specified value.
! GET_ENTRY returns the index of the (key,value) pair in the table for a given
! key. This method is private.
!===============================================================================
subroutine dict_add_key_ci(this, key, value)
function get_entry_ii(this, key) result(i)
class(DictCharInt) :: this
character(*), intent(in) :: key
integer, intent(in) :: value
class(DictIntInt) :: this
integer, intent(in) :: key
integer :: i
integer :: hash
type(ElemKeyValueCI), pointer :: elem => null()
type(ElemKeyValueCI), pointer :: new_elem => null()
integer :: c
elem => this % get_elem(key)
if (associated(elem)) then
elem % value = value
else
! Get hash
hash = dict_hash_key_ci(key)
! Create new element
allocate(new_elem)
new_elem % key = key
new_elem % value = value
! Add element to front of list
new_elem % next => this % table(hash) % list
this % table(hash) % list => new_elem
if (.not. allocated(this % table)) then
allocate(this % table(TABLE_SIZES(1)))
this % capacity = TABLE_SIZES(1)
end if
end subroutine dict_add_key_ci
hash = hash_ii(key)
i = 1 + mod(hash, this % capacity)
c = 2 + mod(hash, this % capacity - 2)
subroutine dict_add_key_ii(this, key, value)
do
if (this % table(i) % key == key .or. &
this % table(i) % key == EMPTY) exit
class(DictIntInt) :: this
i = 1 + mod(i + c - 1, this % capacity)
end do
end function get_entry_ii
function get_entry_ci(this, key) result(i)
class(DictCharInt) :: this
character(*), intent(in) :: key
integer :: i
integer :: hash
integer :: c
if (.not. allocated(this % table)) then
allocate(this % table(TABLE_SIZES(1)))
this % capacity = TABLE_SIZES(1)
end if
hash = hash_ci(key)
i = 1 + mod(hash, this % capacity)
c = 2 + mod(hash, this % capacity - 2)
do
if (this % table(i) % hash == hash) then
if (allocated(this % table(i) % entry)) then
if (this % table(i) % entry % key == key) exit
end if
end if
if (this % table(i) % hash == EMPTY) exit
i = 1 + mod(i + c - 1, this % capacity)
end do
end function get_entry_ci
!===============================================================================
! RESIZE allocates a new hash table to accomodate the number of entries and
! reinserts all of the entries into the new table. This method is private.
!===============================================================================
subroutine resize_ii(this)
class(DictIntInt) :: this
type(DictEntryII), allocatable :: table(:)
integer :: new_size
integer :: i
do i = 1, size(TABLE_SIZES)
if (TABLE_SIZES(i) > this % capacity) exit
end do
new_size = TABLE_SIZES(i)
call move_alloc(this % table, table)
allocate(this % table(new_size))
this % capacity = new_size
this % entries = 0
! Rehash each entry into the new table
do i = 1, size(table)
if (table(i) % key /= EMPTY .and. table(i) % key /= DELETED) then
call this % set(table(i) % key, table(i) % value)
end if
end do
deallocate(table)
end subroutine resize_ii
subroutine resize_ci(this)
class(DictCharInt) :: this
type(BucketCI), allocatable :: table(:)
integer :: new_size
integer :: i
do i = 1, size(TABLE_SIZES)
if (TABLE_SIZES(i) > this % capacity) exit
end do
new_size = TABLE_SIZES(i)
call move_alloc(this % table, table)
allocate(this % table(new_size))
this % capacity = new_size
this % entries = 0
! Rehash each entry into the new table
do i = 1, size(table)
if (table(i) % hash /= EMPTY .and. table(i) % hash /= DELETED) then
call this % set(table(i) % entry % key, table(i) % entry % value)
end if
end do
deallocate(table)
end subroutine resize_ci
!===============================================================================
! SET adds a (key,value) entry to a dictionary. If the key is already in the
! dictionary, the value is replaced by the new specified value.
!===============================================================================
subroutine set_ii(this, key, value)
class(DictIntInt) :: this
integer, intent(in) :: key
integer, intent(in) :: value
integer :: hash
type(ElemKeyValueII), pointer :: elem => null()
type(ElemKeyValueII), pointer :: new_elem => null()
integer :: i
integer :: c
elem => this % get_elem(key)
if (associated(elem)) then
elem % value = value
else
! Get hash
hash = dict_hash_key_ii(key)
! Create new element
allocate(new_elem)
new_elem % key = key
new_elem % value = value
! Add element to front of list
new_elem % next => this % table(hash) % list
this % table(hash) % list => new_elem
if (.not. allocated(this % table)) then
allocate(this % table(TABLE_SIZES(1)))
this % capacity = TABLE_SIZES(1)
else if (real(this % entries + 1, 8) / this % capacity > MAX_LOAD_FACTOR) then
call this % resize()
end if
end subroutine dict_add_key_ii
hash = hash_ii(key)
i = 1 + mod(hash, this % capacity)
c = 2 + mod(hash, this % capacity - 2)
!===============================================================================
! DICT_GET_ELEM returns a pointer to the (key,value) pair for a given key. This
! method is private.
!===============================================================================
do
if (this % table(i) % key == EMPTY .or. &
this % table(i) % key == DELETED) then
this % table(i) % key = key
this % table(i) % value = value
this % entries = this % entries + 1
exit
else if (this % table(i) % key == key) then
this % table(i) % value = value
exit
end if
function dict_get_elem_ci(this, key) result(elem)
class(DictCharInt) :: this
character(*), intent(in) :: key
type(ElemKeyValueCI), pointer :: elem
integer :: hash
! Check for dictionary not being allocated
if (.not. associated(this % table)) then
allocate(this % table(HASH_SIZE))
end if
hash = dict_hash_key_ci(key)
elem => this % table(hash) % list
do while (associated(elem))
if (elem % key == key) exit
elem => elem % next
i = 1 + mod(i + c - 1, this % capacity)
end do
end function dict_get_elem_ci
end subroutine set_ii
function dict_get_elem_ii(this, key) result(elem)
subroutine set_ci(this, key, value)
class(DictIntInt) :: this
integer, intent(in) :: key
type(ElemKeyValueII), pointer :: elem
integer :: hash
! Check for dictionary not being allocated
if (.not. associated(this % table)) then
allocate(this % table(HASH_SIZE))
end if
hash = dict_hash_key_ii(key)
elem => this % table(hash) % list
do while (associated(elem))
if (elem % key == key) exit
elem => elem % next
end do
end function dict_get_elem_ii
!===============================================================================
! DICT_GET_KEY returns the value matching a given key. If the dictionary does
! not contain the key, the value DICT_NULL is returned.
!===============================================================================
function dict_get_key_ci(this, key) result(value)
class(DictCharInt) :: this
class(DictCharInt) :: this
character(*), intent(in) :: key
integer :: value
integer, intent(in) :: value
type(ElemKeyValueCI), pointer :: elem
integer :: hash
integer :: i
integer :: c
elem => this % get_elem(key)
if (associated(elem)) then
value = elem % value
else
value = DICT_NULL
if (.not. allocated(this % table)) then
allocate(this % table(TABLE_SIZES(1)))
this % capacity = TABLE_SIZES(1)
else if (real(this % entries + 1, 8) / this % capacity > MAX_LOAD_FACTOR) then
call this % resize()
end if
end function dict_get_key_ci
hash = hash_ci(key)
i = 1 + mod(hash, this % capacity)
c = 2 + mod(hash, this % capacity - 2)
function dict_get_key_ii(this, key) result(value)
do
if (this % table(i) % hash == EMPTY .or. &
this % table(i) % hash == DELETED) then
if (.not. allocated(this % table(i) % entry)) then
allocate(this % table(i) % entry)
end if
this % table(i) % hash = hash
this % table(i) % entry % key = key
this % table(i) % entry % value = value
this % entries = this % entries + 1
exit
else if (this % table(i) % hash == hash .and. &
this % table(i) % entry % key == key) then
this % table(i) % entry % value = value
exit
end if
i = 1 + mod(i + c - 1, this % capacity)
end do
end subroutine set_ci
!===============================================================================
! GET returns the value matching a given key. If the dictionary does not contain
! the key, the value EMPTY is returned.
!===============================================================================
function get_ii(this, key) result(value)
class(DictIntInt) :: this
integer, intent(in) :: key
integer :: value
type(ElemKeyValueII), pointer :: elem
integer :: i
elem => this % get_elem(key)
i = this % get_entry(key)
value = this % table(i) % value
if (associated(elem)) then
value = elem % value
end function get_ii
function get_ci(this, key) result(value)
class(DictCharInt) :: this
character(*), intent(in) :: key
integer :: value
integer :: i
i = this % get_entry(key)
if (allocated(this % table(i) % entry)) then
value = this % table(i) % entry % value
else
value = DICT_NULL
value = EMPTY
end if
end function dict_get_key_ii
end function get_ci
!===============================================================================
! DICT_HAS_KEY determines whether a dictionary has a (key,value) pair with a
! given key.
! HAS determines whether a dictionary has a (key,value) pair with a given key.
!===============================================================================
function dict_has_key_ci(this, key) result(has)
class(DictCharInt) :: this
character(*), intent(in) :: key
logical :: has
type(ElemKeyValueCI), pointer :: elem
elem => this % get_elem(key)
has = associated(elem)
end function dict_has_key_ci
function dict_has_key_ii(this, key) result(has)
function has_ii(this, key) result(has)
class(DictIntInt) :: this
integer, intent(in) :: key
logical :: has
type(ElemKeyValueII), pointer :: elem
integer :: i
elem => this % get_elem(key)
has = associated(elem)
i = this % get_entry(key)
has = (this % table(i) % key /= EMPTY)
end function dict_has_key_ii
end function has_ii
!===============================================================================
! DICT_HASH_KEY returns the hash value for a given key
!===============================================================================
function dict_hash_key_ci(key) result(val)
function has_ci(this, key) result(has)
class(DictCharInt) :: this
character(*), intent(in) :: key
integer :: val
logical :: has
integer :: i
val = 0
i = this % get_entry(key)
has = (this % table(i) % hash /= EMPTY)
do i = 1, len_trim(key)
val = HASH_MULTIPLIER * val + ichar(key(i:i))
end do
end function has_ci
! Added the absolute val on val-1 since the sum in the do loop is
! susceptible to integer overflow
val = 1 + mod(abs(val-1), HASH_SIZE)
!===============================================================================
! REMOVE deletes a (key,value) entry from a dictionary.
!===============================================================================
end function dict_hash_key_ci
function dict_hash_key_ii(key) result(val)
subroutine remove_ii(this, key)
class(DictIntInt) :: this
integer, intent(in) :: key
integer :: val
val = 0
! Added the absolute val on val-1 since the sum in the do loop is
! susceptible to integer overflow
val = 1 + mod(abs(key-1), HASH_SIZE)
end function dict_hash_key_ii
!===============================================================================
! DICT_KEYS returns a pointer to a linked list of all (key,value) pairs
!===============================================================================
function dict_keys_ci(this) result(keys)
class(DictCharInt) :: this
type(ElemKeyValueCI), pointer :: keys
integer :: i
type(ElemKeyValueCI), pointer :: current => null()
type(ElemKeyValueCI), pointer :: elem => null()
keys => null()
do i = 1, size(this % table)
! Get pointer to start of bucket i
elem => this % table(i) % list
do while (associated(elem))
! Allocate (key,value) pair
if (.not. associated(keys)) then
allocate(keys)
current => keys
else
allocate(current % next)
current => current % next
end if
! Copy (key,value) pair
current % key = elem % key
current % value = elem % value
! Move to next element in bucket i
elem => elem % next
end do
end do
end function dict_keys_ci
function dict_keys_ii(this) result(keys)
class(DictIntInt) :: this
type(ElemKeyValueII), pointer :: keys
integer :: i
type(ElemKeyValueII), pointer :: current => null()
type(ElemKeyValueII), pointer :: elem => null()
keys => null()
do i = 1, size(this % table)
! Get pointer to start of bucket i
elem => this % table(i) % list
do while (associated(elem))
! Allocate (key,value) pair
if (.not. associated(keys)) then
allocate(keys)
current => keys
else
allocate(current % next)
current => current % next
end if
! Copy (key,value) pair
current % key = elem % key
current % value = elem % value
! Move to next element in bucket i
elem => elem % next
end do
end do
end function dict_keys_ii
!===============================================================================
! DICT_CLEAR Deletes and deallocates the dictionary item
!===============================================================================
subroutine dict_clear_ci(this)
class(DictCharInt) :: this
integer :: i
type(ElemKeyValueCI), pointer :: current
type(ElemKeyValueCI), pointer :: next
if (associated(this % table)) then
do i = 1, size(this % table)
current => this % table(i) % list
do while (associated(current))
if (associated(current % next)) then
next => current % next
else
nullify(next)
end if
deallocate(current)
current => next
end do
if (associated(this % table(i) % list)) &
nullify(this % table(i) % list)
end do
deallocate(this % table)
i = this % get_entry(key)
if (this % table(i) % key /= EMPTY) then
this % table(i) % key = DELETED
this % table(i) % value = EMPTY
this % entries = this % entries - 1
end if
end subroutine dict_clear_ci
end subroutine remove_ii
subroutine dict_clear_ii(this)
subroutine remove_ci(this, key)
class(DictCharInt) :: this
character(*), intent(in) :: key
integer :: i
i = this % get_entry(key)
if (this % table(i) % hash /= EMPTY) then
this % table(i) % hash = DELETED
if (allocated(this % table(i) % entry)) then
deallocate(this % table(i) % entry)
end if
this % entries = this % entries - 1
end if
end subroutine remove_ci
!===============================================================================
! NEXT_ENTRY finds the next (key,value) pair. The value of current_entry is
! updated with the (key,value) pair, and the value of i is updated with the
! index of the entry in the table. Passing in i = 0 will locate the first entry
! in the dictionary. If there are no more entries, i will be set to 0.
!===============================================================================
subroutine next_entry_ii(this, current_entry, i)
class(DictIntInt) :: this
type(DictEntryII), intent(inout) :: current_entry
integer, intent(inout) :: i
if (.not. allocated(this % table)) return
do
i = i + 1
if (i > size(this % table)) then
i = 0
exit
else if (this % table(i) % key /= EMPTY .and. &
this % table(i) % key /= DELETED) then
current_entry = this % table(i)
exit
end if
end do
end subroutine next_entry_ii
subroutine next_entry_ci(this, current_entry, i)
class(DictCharInt) :: this
type(DictEntryCI), intent(inout) :: current_entry
integer, intent(inout) :: i
if (.not. allocated(this % table)) return
do
i = i + 1
if (i > size(this % table)) then
i = 0
exit
else if (this % table(i) % hash /= EMPTY .and. &
this % table(i) % hash /= DELETED) then
current_entry = this % table(i) % entry
exit
end if
end do
end subroutine next_entry_ci
!===============================================================================
! CLEAR deletes and deallocates the dictionary item
!===============================================================================
subroutine clear_ii(this)
class(DictIntInt) :: this
if (allocated(this % table)) deallocate(this % table)
this % entries = 0
this % capacity = 0
end subroutine clear_ii
subroutine clear_ci(this)
class(DictCharInt) :: this
if (allocated(this % table)) deallocate(this % table)
this % entries = 0
this % capacity = 0
end subroutine clear_ci
!===============================================================================
! SIZE returns the number of entries in the dictionary
!===============================================================================
pure function size_ii(this) result(size)
class(DictIntInt), intent(in) :: this
integer :: size
size = this % entries
end function size_ii
pure function size_ci(this) result(size)
class(DictCharInt), intent(in) :: this
integer :: size
size = this % entries
end function size_ci
!===============================================================================
! HASH returns the hash value for a given key
!===============================================================================
pure function hash_ii(key) result(hash)
integer, intent(in) :: key
integer :: hash
hash = abs(key - 1)
end function hash_ii
pure function hash_ci(key) result(hash)
character(*), intent(in) :: key
integer :: hash
integer :: i
type(ElemKeyValueII), pointer :: current
type(ElemKeyValueII), pointer :: next
if (associated(this % table)) then
do i = 1, size(this % table)
current => this % table(i) % list
do while (associated(current))
if (associated(current % next)) then
next => current % next
else
nullify(next)
end if
deallocate(current)
current => next
end do
if (associated(this % table(i) % list)) &
nullify(this % table(i) % list)
end do
deallocate(this % table)
end if
hash = 0
end subroutine dict_clear_ii
do i = 1, len_trim(key)
hash = 31 * hash + ichar(key(i:i))
end do
hash = abs(hash - 1)
end function hash_ci
end module dict_header

View file

@ -1,9 +1,11 @@
module distribution_multivariate
use constants, only: ONE, TWO, PI
use distribution_univariate, only: Distribution
use distribution_univariate
use error, only: fatal_error
use random_lcg, only: prn
use math, only: rotate_angle
use xml_interface
implicit none
@ -58,10 +60,17 @@ module distribution_multivariate
type, abstract :: SpatialDistribution
contains
procedure(spatial_distribution_from_xml_), deferred :: from_xml
procedure(spatial_distribution_sample_), deferred :: sample
end type SpatialDistribution
abstract interface
subroutine spatial_distribution_from_xml_(this, node)
import SpatialDistribution, XMLNode
class(SpatialDistribution), intent(inout) :: this
type(XMLNode), intent(in) :: node
end subroutine spatial_distribution_from_xml_
function spatial_distribution_sample_(this) result(xyz)
import SpatialDistribution
class(SpatialDistribution), intent(in) :: this
@ -74,6 +83,7 @@ module distribution_multivariate
class(Distribution), allocatable :: y
class(Distribution), allocatable :: z
contains
procedure :: from_xml => cartesian_independent_from_xml
procedure :: sample => cartesian_independent_sample
end type CartesianIndependent
@ -82,12 +92,14 @@ module distribution_multivariate
real(8) :: upper_right(3)
logical :: only_fissionable = .false.
contains
procedure :: from_xml => spatial_box_from_xml
procedure :: sample => spatial_box_sample
end type SpatialBox
type, extends(SpatialDistribution) :: SpatialPoint
real(8) :: xyz(3)
contains
procedure :: from_xml => spatial_point_from_xml
procedure :: sample => spatial_point_sample
end type SpatialPoint
@ -132,6 +144,55 @@ contains
uvw(:) = this % reference_uvw
end function monodirectional_sample
subroutine cartesian_independent_from_xml(this, node)
class(CartesianIndependent), intent(inout) :: this
type(XMLNode), intent(in) :: node
type(XMLNode) :: node_dist
! Read distribution for x coordinate
if (check_for_node(node, "x")) then
node_dist = node % child("x")
call distribution_from_xml(this % x, node_dist)
else
allocate(Discrete :: this % x)
select type (dist => this % x)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
! Read distribution for y coordinate
if (check_for_node(node, "y")) then
node_dist = node % child("y")
call distribution_from_xml(this % y, node_dist)
else
allocate(Discrete :: this % y)
select type (dist => this % y)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
if (check_for_node(node, "z")) then
node_dist = node % child("z")
call distribution_from_xml(this % z, node_dist)
else
allocate(Discrete :: this % z)
select type (dist => this % z)
type is (Discrete)
allocate(dist % x(1), dist % p(1))
dist % x(1) = ZERO
dist % p(1) = ONE
end select
end if
end subroutine cartesian_independent_from_xml
function cartesian_independent_sample(this) result(xyz)
class(CartesianIndependent), intent(in) :: this
real(8) :: xyz(3)
@ -141,6 +202,26 @@ contains
xyz(3) = this % z % sample()
end function cartesian_independent_sample
subroutine spatial_box_from_xml(this, node)
class(SpatialBox), intent(inout) :: this
type(XMLNode), intent(in) :: node
real(8), allocatable :: temp_real(:)
! Make sure correct number of parameters are given
if (node_word_count(node, "parameters") /= 6) then
call fatal_error('Box/fission spatial source must have &
&six parameters specified.')
end if
! Read lower-right/upper-left coordinates
allocate(temp_real(6))
call get_node_array(node, "parameters", temp_real)
this % lower_left(:) = temp_real(1:3)
this % upper_right(:) = temp_real(4:6)
deallocate(temp_real)
end subroutine spatial_box_from_xml
function spatial_box_sample(this) result(xyz)
class(SpatialBox), intent(in) :: this
real(8) :: xyz(3)
@ -152,6 +233,20 @@ contains
xyz(:) = this % lower_left + r*(this % upper_right - this % lower_left)
end function spatial_box_sample
subroutine spatial_point_from_xml(this, node)
class(SpatialPoint), intent(inout) :: this
type(XMLNode), intent(in) :: node
! Make sure correct number of parameters are given
if (node_word_count(node, "parameters") /= 3) then
call fatal_error('Point spatial source must have &
&three parameters specified.')
end if
! Read location of point source
call get_node_array(node, "parameters", this % xyz)
end subroutine spatial_point_from_xml
function spatial_point_sample(this) result(xyz)
class(SpatialPoint), intent(in) :: this
real(8) :: xyz(3)

View file

@ -1,16 +1,20 @@
module eigenvalue
use, intrinsic :: ISO_C_BINDING
use algorithm, only: binary_search
use constants, only: ZERO
use error, only: fatal_error, warning
use global
use math, only: t_percentile
use mesh, only: count_bank_sites
use mesh_header, only: RegularMesh
use mesh_header, only: RegularMesh, meshes
use message_passing
use random_lcg, only: prn, set_particle_seed, advance_prn_seed
use settings
use simulation_header
use string, only: to_str
use tally_header
use timer_header
implicit none
@ -39,6 +43,7 @@ contains
& temp_sites(:) ! local array of extra sites on each node
#ifdef MPI
integer :: mpi_err ! MPI error code
integer(8) :: n ! number of sites to send/recv
integer :: neighbor ! processor to send/recv data from
#ifdef MPIF08
@ -297,69 +302,34 @@ contains
subroutine shannon_entropy()
integer :: ent_idx ! entropy index
integer :: i, j, k ! index for bank sites
integer :: n ! # of boxes in each dimension
integer :: i ! index for mesh elements
logical :: sites_outside ! were there sites outside entropy box?
type(RegularMesh), pointer :: m
! Get pointer to entropy mesh
m => entropy_mesh
! On the first pass through this subroutine, we need to determine how big
! the entropy mesh should be in each direction and then allocate a
! three-dimensional array to store the fraction of source sites in each mesh
! box
if (.not. allocated(entropy_p)) then
if (.not. allocated(m % dimension)) then
! If the user did not specify how many mesh cells are to be used in
! each direction, we automatically determine an appropriate number of
! cells
n = ceiling((n_particles/20)**(ONE/THREE))
! copy dimensions
m % n_dimension = 3
allocate(m % dimension(3))
m % dimension = n
! determine width
m % width = (m % upper_right - m % lower_left) / m % dimension
associate (m => meshes(index_entropy_mesh))
! count number of fission sites over mesh
call count_bank_sites(m, fission_bank, entropy_p, &
size_bank=n_bank, sites_outside=sites_outside)
! display warning message if there were sites outside entropy box
if (sites_outside) then
if (master) call warning("Fission source site(s) outside of entropy box.")
end if
! allocate p
allocate(entropy_p(1, m % dimension(1), m % dimension(2), &
m % dimension(3)))
end if
! sum values to obtain shannon entropy
if (master) then
! Normalize to total weight of bank sites
entropy_p = entropy_p / sum(entropy_p)
! count number of fission sites over mesh
call count_bank_sites(m, fission_bank, entropy_p, &
size_bank=n_bank, sites_outside=sites_outside)
! display warning message if there were sites outside entropy box
if (sites_outside) then
if (master) call warning("Fission source site(s) outside of entropy box.")
end if
! sum values to obtain shannon entropy
if (master) then
! Normalize to total weight of bank sites
entropy_p = entropy_p / sum(entropy_p)
ent_idx = current_gen + gen_per_batch*(current_batch - 1)
entropy(ent_idx) = ZERO
do i = 1, m % dimension(1)
do j = 1, m % dimension(2)
do k = 1, m % dimension(3)
if (entropy_p(1,i,j,k) > ZERO) then
entropy(ent_idx) = entropy(ent_idx) - &
entropy_p(1,i,j,k) * log(entropy_p(1,i,j,k))/log(TWO)
end if
end do
ent_idx = current_gen + gen_per_batch*(current_batch - 1)
entropy(ent_idx) = ZERO
do i = 1, size(entropy_p, 2)
if (entropy_p(1,i) > ZERO) then
entropy(ent_idx) = entropy(ent_idx) - &
entropy_p(1,i) * log(entropy_p(1,i))/log(TWO)
end if
end do
end do
end if
end if
end associate
end subroutine shannon_entropy
!===============================================================================
@ -371,6 +341,9 @@ contains
subroutine calculate_generation_keff()
integer :: i ! overall generation
#ifdef MPI
integer :: mpi_err ! MPI error code
#endif
! Get keff for this generation by subtracting off the starting value
keff_generation = global_tallies(RESULT_VALUE, K_TRACKLENGTH) - keff_generation
@ -439,8 +412,8 @@ contains
end subroutine calculate_average_keff
!===============================================================================
! CALCULATE_COMBINED_KEFF calculates a minimum variance estimate of k-effective
! based on a linear combination of the collision, absorption, and tracklength
! OPENMC_GET_KEFF calculates a minimum variance estimate of k-effective based on
! a linear combination of the collision, absorption, and tracklength
! estimates. The theory behind this can be found in M. Halperin, "Almost
! linearly-optimum combination of unbiased estimates," J. Am. Stat. Assoc., 56,
! 36-43 (1961), doi:10.1080/01621459.1961.10482088. The implementation here
@ -448,7 +421,9 @@ contains
! of keff confidence intervals in MCNP," Nucl. Technol., 111, 169-182 (1995).
!===============================================================================
subroutine calculate_combined_keff()
function openmc_get_keff(k_combined) result(err) bind(C)
real(C_DOUBLE), intent(out) :: k_combined(2)
integer(C_INT) :: err
integer :: l ! loop index
integer :: i, j, k ! indices referring to collision, absorption, or track
@ -459,9 +434,14 @@ contains
real(8) :: g ! sum of weighting factors
real(8) :: S(3) ! sums used for variance calculation
k_combined = ZERO
! Make sure we have at least four realizations. Notice that at the end,
! there is a N-3 term in a denominator.
if (n_realizations <= 3) return
if (n_realizations <= 3) then
err = -1
return
end if
! Initialize variables
n = real(n_realizations, 8)
@ -523,7 +503,6 @@ contains
! Initialize variables
g = ZERO
S = ZERO
k_combined = ZERO
do l = 1, 3
! Permutations of estimates
@ -590,8 +569,9 @@ contains
k_combined(2) = sqrt(k_combined(2))
end if
err = 0
end subroutine calculate_combined_keff
end function openmc_get_keff
!===============================================================================
! COUNT_SOURCE_FOR_UFS determines the source fraction in each UFS mesh cell and
@ -606,18 +586,21 @@ contains
logical :: sites_outside ! were there sites outside the ufs mesh?
#ifdef MPI
integer :: n ! total number of ufs mesh cells
integer :: mpi_err ! MPI error code
#endif
associate (m => meshes(index_ufs_mesh))
if (current_batch == 1 .and. current_gen == 1) then
! On the first generation, just assume that the source is already evenly
! distributed so that effectively the production of fission sites is not
! biased
source_frac = ufs_mesh % volume_frac
source_frac = m % volume_frac
else
! count number of source sites in each ufs mesh cell
call count_bank_sites(ufs_mesh, source_bank, source_frac, &
call count_bank_sites(m, source_bank, source_frac, &
sites_outside=sites_outside, size_bank=work)
! Check for sites outside of the mesh
@ -627,7 +610,7 @@ contains
#ifdef MPI
! Send source fraction to all processors
n = product(ufs_mesh % dimension)
n = product(m % dimension)
call MPI_BCAST(source_frac, n, MPI_REAL8, 0, mpi_intracomm, mpi_err)
#endif
@ -641,6 +624,8 @@ contains
source_bank % wgt = source_bank % wgt * n_particles / total
end if
end associate
end subroutine count_source_for_ufs
#ifdef _OPENMP

View file

@ -1,14 +1,57 @@
module error
use, intrinsic :: ISO_C_BINDING
use, intrinsic :: ISO_FORTRAN_ENV
use constants
use constants
use message_passing
implicit none
private
public :: fatal_error
public :: warning
! Error codes
integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1
integer(C_INT), public, bind(C) :: E_ALLOCATE = -2
integer(C_INT), public, bind(C) :: E_OUT_OF_BOUNDS = -3
integer(C_INT), public, bind(C) :: E_INVALID_SIZE = -4
integer(C_INT), public, bind(C) :: E_INVALID_ARGUMENT = -5
integer(C_INT), public, bind(C) :: E_INVALID_TYPE = -6
integer(C_INT), public, bind(C) :: E_INVALID_ID = -7
integer(C_INT), public, bind(C) :: E_GEOMETRY = -8
integer(C_INT), public, bind(C) :: E_DATA = -9
integer(C_INT), public, bind(C) :: E_PHYSICS = -10
! Warning codes
integer(C_INT), public, bind(C) :: E_WARNING = 1
! Error message
character(kind=C_CHAR), public, bind(C) :: openmc_err_msg(256)
public :: set_errmsg
contains
!===============================================================================
! SET_ERRMSG sets the 'openmc_err_msg' module variable that is exposed via the C
! API
!===============================================================================
subroutine set_errmsg(f_string)
character(*), intent(in) :: f_string
integer :: i, n
! Copy Fortran string to null-terminated C char array
n = len_trim(f_string)
do i = 1, n
openmc_err_msg(i) = f_string(i:i)
end do
openmc_err_msg(n + 1) = C_NULL_CHAR
end subroutine set_errmsg
!===============================================================================
! WARNING issues a warning to the user in the log file and the standard output
! stream.

View file

@ -1,40 +0,0 @@
module finalize
use, intrinsic :: ISO_C_BINDING
use hdf5, only: h5tclose_f, h5close_f
use global
use hdf5_interface, only: hdf5_bank_t
use message_passing
implicit none
contains
!===============================================================================
! FINALIZE_RUN does all post-simulation tasks such as calculating tally
! statistics and writing out tallies
!===============================================================================
subroutine openmc_finalize() bind(C)
integer :: hdf5_err
! Deallocate arrays
call free_memory()
! Release compound datatypes
call h5tclose_f(hdf5_bank_t, hdf5_err)
! Close FORTRAN interface.
call h5close_f(hdf5_err)
#ifdef MPI
! Free all MPI types
call MPI_TYPE_FREE(MPI_BANK, mpi_err)
#endif
end subroutine openmc_finalize
end module finalize

View file

@ -2,16 +2,17 @@ module geometry
use constants
use error, only: fatal_error, warning
use geometry_header, only: Cell, Universe, Lattice, &
&RectLattice, HexLattice
use global
use geometry_header
use output, only: write_message
use particle_header, only: LocalCoord, Particle
use particle_restart_write, only: write_particle_restart
use simulation_header
use settings
use surface_header
use stl_vector, only: VectorInt
use string, only: to_str
use tally, only: score_surface_current
use tally_header
implicit none
@ -384,9 +385,8 @@ contains
! the geometry, is reflected, or crosses into a new lattice or cell
!===============================================================================
subroutine cross_surface(p, last_cell)
subroutine cross_surface(p)
type(Particle), intent(inout) :: p
integer, intent(in) :: last_cell ! last cell particle was in
real(8) :: u ! x-component of direction
real(8) :: v ! y-component of direction
@ -425,9 +425,7 @@ contains
end if
! Score to global leakage tally
if (tallies_on) then
global_tally_leakage = global_tally_leakage + p % wgt
end if
global_tally_leakage = global_tally_leakage + p % wgt
! Display message
if (verbosity >= 10 .or. trace) then
@ -469,7 +467,7 @@ contains
p%coord(1)%uvw(:) = [u, v, w] / norm
! Reassign particle's cell and surface
p % coord(1) % cell = last_cell
p % coord(1) % cell = p % last_cell(p % last_n_coord)
p % surface = -p % surface
! If a reflective surface is coincident with a lattice or universe
@ -1251,8 +1249,8 @@ contains
class(Lattice), pointer :: lat ! pointer to current lattice
! Don't research places already checked
if (found(universe_dict % get_key(univ % id), map)) then
count = counts(universe_dict % get_key(univ % id), map)
if (found(universe_dict % get(univ % id), map)) then
count = counts(universe_dict % get(univ % id), map)
return
end if
@ -1260,8 +1258,8 @@ contains
! Count = 1, then quit
if (univ % id == univ_id) then
count = 1
counts(universe_dict % get_key(univ % id), map) = 1
found(universe_dict % get_key(univ % id), map) = .true.
counts(universe_dict % get(univ % id), map) = 1
found(universe_dict % get(univ % id), map) = .true.
return
end if
@ -1357,8 +1355,8 @@ contains
end if
end do
counts(universe_dict % get_key(univ % id), map) = count
found(universe_dict % get_key(univ % id), map) = .true.
counts(universe_dict % get(univ % id), map) = count
found(universe_dict % get(univ % id), map) = .true.
end function count_target

View file

@ -1,10 +1,14 @@
module geometry_header
use, intrinsic :: ISO_C_BINDING
use algorithm, only: find
use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, &
MATERIAL_VOID, NONE
use dict_header, only: DictCharInt, DictIntInt
use material_header, only: Material
use material_header, only: Material, materials, material_dict, n_materials
use nuclide_header
use sab_header
use stl_vector, only: VectorReal
use string, only: to_lower
@ -155,6 +159,19 @@ module geometry_header
! array index of the root universe
integer :: root_universe = -1
integer(C_INT32_T), bind(C) :: n_cells ! # of cells
integer(C_INT32_T), bind(C) :: n_universes ! # of universes
integer(C_INT32_T), bind(C) :: n_lattices ! # of lattices
type(Cell), allocatable, target :: cells(:)
type(Universe), allocatable, target :: universes(:)
type(LatticeContainer), allocatable, target :: lattices(:)
! Dictionaries which map user IDs to indices in the global arrays
type(DictIntInt) :: cell_dict
type(DictIntInt) :: universe_dict
type(DictIntInt) :: lattice_dict
contains
!===============================================================================
@ -330,16 +347,8 @@ contains
! temperatures to read (which may be different if interpolation is used)
!===============================================================================
subroutine get_temperatures(cells, materials, material_dict, nuclide_dict, &
n_nucs, nuc_temps, sab_dict, n_sabs, sab_temps)
type(Cell), allocatable, intent(in) :: cells(:)
type(Material), allocatable, intent(in) :: materials(:)
type(DictIntInt), intent(in) :: material_dict
type(DictCharInt), intent(in) :: nuclide_dict
integer, intent(in) :: n_nucs
subroutine get_temperatures(nuc_temps, sab_temps)
type(VectorReal), allocatable, intent(out) :: nuc_temps(:)
type(DictCharInt), optional, intent(in) :: sab_dict
integer, optional, intent(in) :: n_sabs
type(VectorReal), optional, allocatable, intent(out) :: sab_temps(:)
integer :: i, j, k
@ -348,8 +357,8 @@ contains
integer :: i_material
real(8) :: temperature ! temperature in Kelvin
allocate(nuc_temps(n_nucs))
if (present(n_sabs) .and. present(sab_temps)) allocate(sab_temps(n_sabs))
allocate(nuc_temps(n_nuclides))
if (present(sab_temps)) allocate(sab_temps(n_sab_tables))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
@ -364,11 +373,12 @@ contains
temperature = cells(i) % sqrtkT(1)**2 / K_BOLTZMANN
end if
i_material = material_dict % get_key(cells(i) % material(j))
i_material = cells(i) % material(j)
associate (mat => materials(i_material))
NUC_NAMES_LOOP: do k = 1, size(mat % names)
! Get index in nuc_temps array
i_nuclide = nuclide_dict % get_key(to_lower(mat % names(k)))
i_nuclide = nuclide_dict % get(to_lower(mat % names(k)))
! Add temperature if it hasn't already been added
if (find(nuc_temps(i_nuclide), temperature) == -1) then
@ -376,11 +386,10 @@ contains
end if
end do NUC_NAMES_LOOP
if (present(sab_temps) .and. present(sab_dict) .and. &
mat % n_sab > 0) then
if (present(sab_temps) .and. mat % n_sab > 0) then
SAB_NAMES_LOOP: do k = 1, size(mat % sab_names)
! Get index in nuc_temps array
i_sab = sab_dict % get_key(to_lower(mat % sab_names(k)))
i_sab = sab_dict % get(to_lower(mat % sab_names(k)))
! Add temperature if it hasn't already been added
if (find(sab_temps(i_sab), temperature) == -1) then
@ -394,4 +403,239 @@ contains
end subroutine get_temperatures
!===============================================================================
! FREE_MEMORY_GEOMETRY deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_geometry()
n_cells = 0
n_universes = 0
n_lattices = 0
if (allocated(cells)) deallocate(cells)
if (allocated(universes)) deallocate(universes)
if (allocated(lattices)) deallocate(lattices)
call cell_dict % clear()
call universe_dict % clear()
call lattice_dict % clear()
end subroutine free_memory_geometry
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_get_cell_index(id, index) result(err) bind(C)
! Return the index in the cells array of a cell with a given ID
integer(C_INT32_T), value :: id
integer(C_INT32_T), intent(out) :: index
integer(C_INT) :: err
if (allocated(cells)) then
if (cell_dict % has(id)) then
index = cell_dict % get(id)
err = 0
else
err = E_INVALID_ID
call set_errmsg("No cell exists with ID=" // trim(to_str(id)) // ".")
end if
else
err = E_ALLOCATE
call set_errmsg("Memory has not been allocated for cells.")
end if
end function openmc_get_cell_index
function openmc_cell_get_fill(index, type, indices, n) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), intent(out) :: type
integer(C_INT32_T), intent(out) :: n
type(C_PTR), intent(out) :: indices
integer(C_INT) :: err
err = 0
if (index >= 1 .and. index <= size(cells)) then
associate (c => cells(index))
type = c % type
select case (type)
case (FILL_MATERIAL)
n = size(c % material)
indices = C_LOC(c % material(1))
case (FILL_UNIVERSE, FILL_LATTICE)
n = 1
indices = C_LOC(c % fill)
end select
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_get_fill
function openmc_cell_get_id(index, id) result(err) bind(C)
! Return the ID of a cell
integer(C_INT32_T), value :: index
integer(C_INT32_T), intent(out) :: id
integer(C_INT) :: err
if (index >= 1 .and. index <= size(cells)) then
id = cells(index) % id
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_get_id
function openmc_cell_set_fill(index, type, n, indices) result(err) bind(C)
! Set the fill for a fill
integer(C_INT32_T), value, intent(in) :: index ! index in cells
integer(C_INT), value, intent(in) :: type
integer(c_INT32_T), value, intent(in) :: n
integer(C_INT32_T), intent(in) :: indices(n)
integer(C_INT) :: err
integer :: i, j
err = 0
if (index >= 1 .and. index <= size(cells)) then
associate (c => cells(index))
select case (type)
case (FILL_MATERIAL)
if (allocated(c % material)) deallocate(c % material)
allocate(c % material(n))
c % type = FILL_MATERIAL
do i = 1, n
j = indices(i)
if (j == 0) then
c % material(i) = MATERIAL_VOID
else
if (j >= 1 .and. j <= n_materials) then
c % material(i) = j
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index " // trim(to_str(j)) // " in the &
&materials array is out of bounds.")
end if
end if
end do
case (FILL_UNIVERSE)
c % type = FILL_UNIVERSE
case (FILL_LATTICE)
c % type = FILL_LATTICE
end select
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_set_fill
function openmc_cell_set_temperature(index, T, instance) result(err) bind(C)
! Set the temperature of a cell
integer(C_INT32_T), value, intent(in) :: index ! index in cells
real(C_DOUBLE), value, intent(in) :: T ! temperature
integer(C_INT32_T), optional, intent(in) :: instance ! cell instance
integer(C_INT) :: err ! error code
integer :: j ! looping variable
integer :: n ! number of cell instances
integer :: material_index ! material index in materials array
integer :: num_nuclides ! num nuclides in material
integer :: nuclide_index ! index of nuclide in nuclides array
real(8) :: min_temp ! min common-denominator avail temp
real(8) :: max_temp ! max common-denominator avail temp
real(8) :: temp ! actual temp we'll assign
logical :: outside_low ! lower than available data
logical :: outside_high ! higher than available data
outside_low = .false.
outside_high = .false.
err = E_UNASSIGNED
if (index >= 1 .and. index <= size(cells)) then
! error if the cell is filled with another universe
if (cells(index) % fill /= NONE) then
err = E_GEOMETRY
call set_errmsg("Cannot set temperature on a cell filled &
&with a universe.")
else
! find which material is associated with this cell (material_index
! is the index into the materials array)
if (present(instance)) then
material_index = cells(index) % material(instance)
else
material_index = cells(index) % material(1)
end if
! number of nuclides associated with this material
num_nuclides = size(materials(material_index) % nuclide)
min_temp = ZERO
max_temp = INFINITY
do j = 1, num_nuclides
nuclide_index = materials(material_index) % nuclide(j)
min_temp = max(min_temp, minval(nuclides(nuclide_index) % kTs))
max_temp = min(max_temp, maxval(nuclides(nuclide_index) % kTs))
end do
! adjust the temperature to be within bounds if necessary
if (K_BOLTZMANN * T < min_temp) then
outside_low = .true.
temp = min_temp / K_BOLTZMANN
else if (K_BOLTZMANN * T > max_temp) then
outside_high = .true.
temp = max_temp / K_BOLTZMANN
else
temp = T
end if
associate (c => cells(index))
if (allocated(c % sqrtkT)) then
n = size(c % sqrtkT)
if (present(instance) .and. n > 1) then
if (instance >= 0 .and. instance < n) then
c % sqrtkT(instance + 1) = sqrt(K_BOLTZMANN * temp)
err = 0
end if
else
c % sqrtkT(:) = sqrt(K_BOLTZMANN * temp)
err = 0
end if
end if
end associate
! Assign error codes for outside of temperature bounds provided the
! temperature was changed correctly. This needs to be done after
! changing the temperature based on the logical structure above.
if (err == 0) then
if (outside_low) then
err = E_WARNING
call set_errmsg("Nuclear data has not been loaded beyond lower &
&bound of T=" // trim(to_str(T)) // " K.")
else if (outside_high) then
err = E_WARNING
call set_errmsg("Nuclear data has not been loaded beyond upper &
&bound of T=" // trim(to_str(T)) // " K.")
end if
end if
end if
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_set_temperature
end module geometry_header

View file

@ -1,590 +0,0 @@
module global
use, intrinsic :: ISO_C_BINDING
#ifdef MPIF08
use mpi_f08
#endif
use bank_header, only: Bank
use cmfd_header
use constants
use dict_header, only: DictCharInt, DictIntInt
use geometry_header, only: Cell, Universe, Lattice, LatticeContainer
use material_header, only: Material
use mesh_header, only: RegularMesh
use mgxs_header, only: Mgxs, MgxsContainer
use nuclide_header
use photon_header, only: PhotonInteraction, ElementMicroXS
use plot_header, only: ObjectPlot
use sab_header, only: SAlphaBeta
use set_header, only: SetInt
use stl_vector, only: VectorInt
use surface_header, only: SurfaceContainer
use source_header, only: SourceDistribution
use tally_header, only: TallyObject, TallyDerivative
use tally_filter_header, only: TallyFilterContainer, TallyFilterMatch
use trigger_header, only: KTrigger
use timer_header, only: Timer
use volume_header, only: VolumeCalculation
implicit none
! ============================================================================
! GEOMETRY-RELATED VARIABLES
! Main arrays
type(Cell), allocatable, target :: cells(:)
type(Universe), allocatable, target :: universes(:)
type(LatticeContainer), allocatable, target :: lattices(:)
type(SurfaceContainer), allocatable, target :: surfaces(:)
type(Material), allocatable, target :: materials(:)
type(ObjectPlot), allocatable, target :: plots(:)
type(VolumeCalculation), allocatable :: volume_calcs(:)
! Size of main arrays
integer :: n_cells ! # of cells
integer :: n_universes ! # of universes
integer :: n_lattices ! # of lattices
integer :: n_surfaces ! # of surfaces
integer :: n_materials ! # of materials
integer :: n_plots ! # of plots
! These dictionaries provide a fast lookup mechanism -- the key is the
! user-specified identifier and the value is the index in the corresponding
! array
type(DictIntInt) :: cell_dict
type(DictIntInt) :: universe_dict
type(DictIntInt) :: lattice_dict
type(DictIntInt) :: surface_dict
type(DictIntInt) :: material_dict
type(DictIntInt) :: mesh_dict
type(DictIntInt) :: filter_dict
type(DictIntInt) :: tally_dict
type(DictIntInt) :: plot_dict
! Number of lost particles
integer :: n_lost_particles
! ============================================================================
! ENERGY TREATMENT RELATED VARIABLES
logical :: run_CE = .true. ! Run in CE mode?
! ============================================================================
! CROSS SECTION RELATED VARIABLES NEEDED REGARDLESS OF CE OR MG
integer :: n_nuclides_total ! Number of nuclide cross section tables
integer :: n_elements ! Number of photon cross section tables
! Cross section caches
type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide
type(ElementMicroXS), allocatable :: micro_photon_xs(:) ! Cache for each element
type(MaterialMacroXS) :: material_xs ! Cache for current material
! Dictionaries to look up cross sections and listings
type(DictCharInt) :: nuclide_dict
type(DictCharInt) :: element_dict
type(DictCharInt) :: library_dict
! Cross section libraries
type(Library), allocatable :: libraries(:)
! ============================================================================
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
! Cross section arrays
type(Nuclide), allocatable, target :: nuclides(:) ! Nuclide cross-sections
type(PhotonInteraction), allocatable :: elements(:) ! Photon cross sections
type(SAlphaBeta), allocatable, target :: sab_tables(:) ! S(a,b) tables
integer :: n_sab_tables ! Number of S(a,b) thermal scattering tables
! Minimum/maximum energies
real(8) :: energy_min_neutron = ZERO
real(8) :: energy_max_neutron = INFINITY
! Dictionaries to look up cross sections and listings
type(DictCharInt) :: sab_dict
! Unreoslved resonance probablity tables
logical :: urr_ptables_on = .true.
! Default temperature and method for choosing temperatures
integer :: temperature_method = TEMPERATURE_NEAREST
logical :: temperature_multipole = .false.
real(8) :: temperature_tolerance = 10.0_8
real(8) :: temperature_default = 293.6_8
real(8) :: temperature_range(2) = [ZERO, ZERO]
integer :: n_log_bins ! number of bins for logarithmic grid
real(8) :: log_spacing ! spacing on logarithmic grid
logical :: photon_transport = .false.
integer :: electron_treatment = ELECTRON_LED
! ============================================================================
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
! Cross section arrays
type(MgxsContainer), allocatable, target :: nuclides_MG(:)
! Cross section caches
type(MgxsContainer), target, allocatable :: macro_xs(:)
! Number of energy groups
integer :: num_energy_groups
! Number of delayed groups
integer :: num_delayed_groups
! Energy group structure
real(8), allocatable :: energy_bins(:)
! Midpoint of the energy group structure
real(8), allocatable :: energy_bin_avg(:)
! Maximum Data Order
integer :: max_order
! Whether or not to convert Legendres to tabulars
logical :: legendre_to_tabular = .True.
! Number of points to use in the Legendre to tabular conversion
integer :: legendre_to_tabular_points = 33
! ============================================================================
! TALLY-RELATED VARIABLES
type(RegularMesh), allocatable, target :: meshes(:)
type(TallyObject), allocatable, target :: tallies(:)
type(TallyFilterContainer), allocatable, target :: filters(:)
type(TallyFilterMatch), allocatable :: filter_matches(:)
! Pointers for different tallies
type(TallyObject), pointer :: user_tallies(:) => null()
type(TallyObject), pointer :: cmfd_tallies(:) => null()
! Starting index (minus 1) in tallies for each tally group
integer :: i_user_tallies = -1
integer :: i_cmfd_tallies = -1
! Active tally lists
type(VectorInt) :: active_analog_tallies
type(VectorInt) :: active_tracklength_tallies
type(VectorInt) :: active_current_tallies
type(VectorInt) :: active_collision_tallies
type(VectorInt) :: active_tallies
! Global tallies
! 1) collision estimate of k-eff
! 2) absorption estimate of k-eff
! 3) track-length estimate of k-eff
! 4) leakage fraction
real(C_DOUBLE), allocatable, target :: global_tallies(:,:)
! It is possible to protect accumulate operations on global tallies by using
! an atomic update. However, when multiple threads accumulate to the same
! global tally, it can cause a higher cache miss rate due to
! invalidation. Thus, we use threadprivate variables to accumulate global
! tallies and then reduce at the end of a generation.
real(8) :: global_tally_collision = ZERO
real(8) :: global_tally_absorption = ZERO
real(8) :: global_tally_tracklength = ZERO
real(8) :: global_tally_leakage = ZERO
!$omp threadprivate(global_tally_collision, global_tally_absorption, &
!$omp& global_tally_tracklength, global_tally_leakage)
integer :: n_meshes = 0 ! # of structured meshes
integer :: n_user_meshes = 0 ! # of structured user meshes
integer :: n_filters = 0 ! # of filters
integer :: n_user_filters = 0 ! # of user filters
integer :: n_tallies = 0 ! # of tallies
integer :: n_user_tallies = 0 ! # of user tallies
! Tally derivatives
type(TallyDerivative), allocatable :: tally_derivs(:)
!$omp threadprivate(tally_derivs)
! Normalization for statistics
integer :: n_realizations = 0 ! # of independent realizations
real(8) :: total_weight ! total starting particle weight in realization
! Flag for turning tallies on
logical :: tallies_on = .false.
logical :: active_batches = .false.
! Assume all tallies are spatially distinct
logical :: assume_separate = .false.
! Use confidence intervals for results instead of standard deviations
logical :: confidence_intervals = .false.
! ============================================================================
! EIGENVALUE SIMULATION VARIABLES
integer(8) :: n_particles = 0 ! # of particles per generation
integer :: n_batches ! # of batches
integer :: n_inactive ! # of inactive batches
integer :: n_active ! # of active batches
integer :: gen_per_batch = 1 ! # of generations per batch
integer :: current_batch = 0 ! current batch
integer :: current_gen = 0 ! current generation within a batch
integer :: total_gen = 0 ! total number of generations simulated
! ============================================================================
! TALLY PRECISION TRIGGER VARIABLES
integer :: n_max_batches ! max # of batches
integer :: n_batch_interval = 1 ! batch interval for triggers
logical :: pred_batches = .false. ! predict batches for triggers
logical :: trigger_on = .false. ! flag for turning triggers on/off
type(KTrigger) :: keff_trigger ! trigger for k-effective
logical :: satisfy_triggers = .false. ! whether triggers are satisfied
! External source
type(SourceDistribution), allocatable :: external_source(:)
! Source and fission bank
type(Bank), allocatable, target :: source_bank(:)
type(Bank), allocatable, target :: fission_bank(:)
#ifdef _OPENMP
type(Bank), allocatable, target :: master_fission_bank(:)
#endif
integer(8) :: n_bank ! # of sites in fission bank
integer(8) :: work ! number of particles per processor
integer(8), allocatable :: work_index(:) ! starting index in source bank for each process
integer(8) :: current_work ! index in source bank of current history simulated
! Temporary k-effective values
real(8), allocatable :: k_generation(:) ! single-generation estimates of k
real(8) :: keff = ONE ! average k over active batches
real(8) :: keff_std ! standard deviation of average k
real(8) :: k_col_abs = ZERO ! sum over batches of k_collision * k_absorption
real(8) :: k_col_tra = ZERO ! sum over batches of k_collision * k_tracklength
real(8) :: k_abs_tra = ZERO ! sum over batches of k_absorption * k_tracklength
real(8) :: k_combined(2) ! combined best estimate of k-effective
! Shannon entropy
logical :: entropy_on = .false.
real(8), allocatable :: entropy(:) ! shannon entropy at each generation
real(8), allocatable :: entropy_p(:,:,:,:) ! % of source sites in each cell
type(RegularMesh), pointer :: entropy_mesh
! Uniform fission source weighting
logical :: ufs = .false.
type(RegularMesh), pointer :: ufs_mesh => null()
real(8), allocatable :: source_frac(:,:,:,:)
! Write source at end of simulation
logical :: source_separate = .false.
logical :: source_write = .true.
logical :: source_latest = .false.
! ============================================================================
! PARALLEL PROCESSING VARIABLES
#ifdef _OPENMP
integer :: n_threads = NONE ! number of OpenMP threads
integer :: thread_id ! ID of a given thread
#endif
! No reduction at end of batch
logical :: reduce_tallies = .true.
! ============================================================================
! TIMING VARIABLES
type(Timer) :: time_total ! timer for total run
type(Timer) :: time_initialize ! timer for initialization
type(Timer) :: time_read_xs ! timer for reading cross sections
type(Timer) :: time_unionize ! timer for material xs-energy grid union
type(Timer) :: time_bank ! timer for fission bank synchronization
type(Timer) :: time_bank_sample ! timer for fission bank sampling
type(Timer) :: time_bank_sendrecv ! timer for fission bank SEND/RECV
type(Timer) :: time_tallies ! timer for accumulate tallies
type(Timer) :: time_inactive ! timer for inactive batches
type(Timer) :: time_active ! timer for active batches
type(Timer) :: time_transport ! timer for transport only
type(Timer) :: time_finalize ! timer for finalization
! ===========================================================================
! VARIANCE REDUCTION VARIABLES
logical :: survival_biasing = .false.
real(8) :: weight_cutoff = 0.25_8
real(8) :: energy_cutoff(4) = [ZERO, 1000.0_8, ZERO, ZERO]
real(8) :: weight_survive = ONE
! ============================================================================
! MISCELLANEOUS VARIABLES
! Mode to run in (fixed source, eigenvalue, plotting, etc)
integer :: run_mode = NONE
! Restart run
logical :: restart_run = .false.
integer :: restart_batch
character(MAX_FILE_LEN) :: path_input ! Path to input file
character(MAX_FILE_LEN) :: path_cross_sections = '' ! Path to cross_sections.xml
character(MAX_FILE_LEN) :: path_multipole ! Path to wmp library
character(MAX_FILE_LEN) :: path_source = '' ! Path to binary source
character(MAX_FILE_LEN) :: path_state_point ! Path to binary state point
character(MAX_FILE_LEN) :: path_source_point ! Path to binary source point
character(MAX_FILE_LEN) :: path_particle_restart ! Path to particle restart
character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory
! The verbosity controls how much information will be printed to the
! screen and in logs
integer :: verbosity = 7
! Flag for enabling cell overlap checking during transport
logical :: check_overlaps = .false.
integer(8), allocatable :: overlap_check_cnt(:)
! Trace for single particle
logical :: trace
integer :: trace_batch
integer :: trace_gen
integer(8) :: trace_particle
! Particle tracks
logical :: write_all_tracks = .false.
integer, allocatable :: track_identifiers(:,:)
! Particle restart run
logical :: particle_restart_run = .false.
! Number of distribcell maps
integer :: n_maps
! Write out initial source
logical :: write_initial_source = .false.
! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE
logical :: create_fission_neutrons = .true.
! ============================================================================
! CMFD VARIABLES
! Main object
type(cmfd_type) :: cmfd
! Is CMFD active
logical :: cmfd_run = .false.
! Timing objects
type(Timer) :: time_cmfd ! timer for whole cmfd calculation
type(Timer) :: time_cmfdbuild ! timer for matrix build
type(Timer) :: time_cmfdsolve ! timer for solver
! Flag for active core map
logical :: cmfd_coremap = .false.
! Flag to reset dhats to zero
logical :: dhat_reset = .false.
! Flag to activate neutronic feedback via source weights
logical :: cmfd_feedback = .false.
! User-defined tally information
integer :: n_cmfd_meshes = 1 ! # of structured meshes
integer :: n_cmfd_filters = 0 ! # of filters
integer :: n_cmfd_tallies = 3 ! # of user-defined tallies
! Adjoint method type
character(len=10) :: cmfd_adjoint_type = 'physical'
! Number of incomplete ilu factorization levels
integer :: cmfd_ilu_levels = 1
! Batch to begin cmfd
integer :: cmfd_begin = 1
! Tally reset list
integer :: n_cmfd_resets
type(SetInt) :: cmfd_reset
! Compute effective downscatter cross section
logical :: cmfd_downscatter = .false.
! Convergence monitoring
logical :: cmfd_power_monitor = .false.
! Cmfd output
logical :: cmfd_write_matrices = .false.
! Run an adjoint calculation (last batch only)
logical :: cmfd_run_adjoint = .false.
! CMFD run logicals
logical :: cmfd_on = .false.
! CMFD display info
character(len=25) :: cmfd_display = 'balance'
! Estimate of spectral radius of CMFD matrices and tolerances
real(8) :: cmfd_spectral = ZERO
real(8) :: cmfd_shift = 1.e6
real(8) :: cmfd_ktol = 1.e-8_8
real(8) :: cmfd_stol = 1.e-8_8
real(8) :: cmfd_atoli = 1.e-10_8
real(8) :: cmfd_rtoli = 1.e-5_8
! Information about state points to be written
integer :: n_state_points = 0
type(SetInt) :: statepoint_batch
! Information about source points to be written
integer :: n_source_points = 0
type(SetInt) :: sourcepoint_batch
! Various output options
logical :: output_summary = .true.
logical :: output_tallies = .true.
! ============================================================================
! RESONANCE SCATTERING VARIABLES
logical :: res_scat_on = .false. ! is resonance scattering treated?
integer :: res_scat_method = RES_SCAT_ARES ! resonance scattering method
real(8) :: res_scat_energy_min = 0.01_8
real(8) :: res_scat_energy_max = 1000.0_8
character(10), allocatable :: res_scat_nuclides(:)
!$omp threadprivate(micro_xs, micro_photon_xs, material_xs, fission_bank, &
!$omp& n_bank, trace, thread_id, current_work, filter_matches)
contains
!===============================================================================
! FREE_MEMORY deallocates and clears all global allocatable arrays in the
! program
!===============================================================================
subroutine free_memory()
integer :: i ! Loop Index
! Deallocate cells, surfaces, materials
if (allocated(cells)) deallocate(cells)
if (allocated(universes)) deallocate(universes)
if (allocated(lattices)) deallocate(lattices)
if (allocated(surfaces)) deallocate(surfaces)
if (allocated(materials)) deallocate(materials)
if (allocated(plots)) deallocate(plots)
! Deallocate geometry debugging information
if (allocated(overlap_check_cnt)) deallocate(overlap_check_cnt)
! Deallocate cross section data, listings, and cache
if (allocated(nuclides)) then
! First call the clear routines
do i = 1, size(nuclides)
call nuclides(i) % clear()
end do
deallocate(nuclides)
end if
if (allocated(elements)) deallocate(elements)
if (allocated(res_scat_nuclides)) deallocate(res_scat_nuclides)
if (allocated(nuclides_MG)) deallocate(nuclides_MG)
if (allocated(macro_xs)) deallocate(macro_xs)
if (allocated(sab_tables)) deallocate(sab_tables)
if (allocated(micro_xs)) deallocate(micro_xs)
! Deallocate external source
if (allocated(external_source)) deallocate(external_source)
! Deallocate k and entropy
if (allocated(k_generation)) deallocate(k_generation)
if (allocated(entropy)) deallocate(entropy)
if (allocated(entropy_p)) deallocate(entropy_p)
! Deallocate tally-related arrays
if (allocated(global_tallies)) deallocate(global_tallies)
if (allocated(meshes)) deallocate(meshes)
if (allocated(filters)) deallocate(filters)
if (allocated(tallies)) deallocate(tallies)
if (allocated(filter_matches)) deallocate(filter_matches)
! Deallocate fission and source bank and entropy
!$omp parallel
if (allocated(fission_bank)) deallocate(fission_bank)
!$omp end parallel
#ifdef _OPENMP
if (allocated(master_fission_bank)) deallocate(master_fission_bank)
#endif
if (allocated(source_bank)) deallocate(source_bank)
if (allocated(entropy_p)) deallocate(entropy_p)
! Deallocate array of work indices
if (allocated(work_index)) deallocate(work_index)
! Deallocate CMFD
call deallocate_cmfd(cmfd)
! Deallocate tally node lists
call active_analog_tallies % clear()
call active_tracklength_tallies % clear()
call active_current_tallies % clear()
call active_collision_tallies % clear()
call active_tallies % clear()
! Deallocate track_identifiers
if (allocated(track_identifiers)) deallocate(track_identifiers)
! Deallocate dictionaries
call cell_dict % clear()
call universe_dict % clear()
call lattice_dict % clear()
call surface_dict % clear()
call material_dict % clear()
call mesh_dict % clear()
call filter_dict % clear()
call tally_dict % clear()
call plot_dict % clear()
call nuclide_dict % clear()
call sab_dict % clear()
! Clear statepoint and sourcepoint batch set
call statepoint_batch % clear()
call sourcepoint_batch % clear()
! Deallocate entropy mesh
if (associated(entropy_mesh)) then
if (allocated(entropy_mesh % lower_left)) &
deallocate(entropy_mesh % lower_left)
if (allocated(entropy_mesh % upper_right)) &
deallocate(entropy_mesh % upper_right)
if (allocated(entropy_mesh % width)) deallocate(entropy_mesh % width)
deallocate(entropy_mesh)
end if
! Deallocate ufs
if (allocated(source_frac)) deallocate(source_frac)
if (associated(ufs_mesh)) then
if (allocated(ufs_mesh % lower_left)) deallocate(ufs_mesh % lower_left)
if (allocated(ufs_mesh % upper_right)) &
deallocate(ufs_mesh % upper_right)
if (allocated(ufs_mesh % width)) deallocate(ufs_mesh % width)
deallocate(ufs_mesh)
end if
end subroutine free_memory
!===============================================================================
! OVERALL_GENERATION determines the overall generation number
!===============================================================================
pure function overall_generation() result(gen)
integer :: gen
gen = gen_per_batch*(current_batch - 1) + current_gen
end function overall_generation
end module global

View file

@ -9,30 +9,26 @@ module initialize
use bank_header, only: Bank
use constants
use dict_header, only: DictIntInt, ElemKeyValueII
use set_header, only: SetInt
use error, only: fatal_error, warning
use geometry, only: neighbor_lists, count_instance, calc_offsets, &
maximum_levels
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,&
root_universe
use global
use hdf5_interface, only: file_open, read_attribute, file_close, &
hdf5_bank_t, hdf5_integer8_t
use input_xml, only: read_input_xml, read_plots_xml
use input_xml, only: read_input_xml
use material_header, only: Material
use message_passing
use mgxs_data, only: read_mgxs, create_macro_xs
use output, only: print_version, write_message, print_usage, &
print_plot
use random_lcg, only: initialize_prng
use state_point, only: load_state_point
use output, only: print_version, write_message, print_usage
use random_lcg, only: openmc_set_seed, seed
use settings
#ifdef _OPENMP
use simulation_header, only: n_threads
#endif
use string, only: to_str, starts_with, ends_with, str_to_int
use summary, only: write_summary
use tally_header, only: TallyObject
use tally_initialize,only: configure_tallies
use tally_filter
use tally, only: init_tally_routines
use timer_header
implicit none
@ -48,6 +44,8 @@ contains
subroutine openmc_init(intracomm) bind(C)
integer, intent(in), optional :: intracomm ! MPI intracommunicator
integer :: err
! Copy the communicator to a new variable. This is done to avoid changing
! the signature of this subroutine. If MPI is being used but no communicator
! was passed, assume MPI_COMM_WORLD.
@ -84,75 +82,16 @@ contains
! Read command line arguments
call read_command_line()
! Initialize random number generator -- if the user specifies a seed, it
! will be re-initialized later
err = openmc_set_seed(seed)
! Read XML input files
call read_input_xml()
! Initialize random number generator -- this has to be done after the input
! files have been read in case the user specified a seed for the random
! number generator
call initialize_prng()
! Read plots.xml if it exists -- this has to be done separate from the other
! XML files because we need the PRNG to be initialized first
if (run_mode == MODE_PLOTTING) call read_plots_xml()
! Use dictionaries to redefine index pointers
call adjust_indices()
! Initialize distribcell_filters
call prepare_distribcell()
! After reading input and basic geometry setup is complete, build lists of
! neighboring cells for efficient tracking
call neighbor_lists()
! Check to make sure there are not too many nested coordinate levels in the
! geometry since the coordinate list is statically allocated for performance
! reasons
if (maximum_levels(universes(root_universe)) > MAX_COORD) then
call fatal_error("Too many nested coordinate levels in the geometry. &
&Try increasing the maximum number of coordinate levels by &
&providing the CMake -Dmaxcoord= option.")
end if
if (run_mode /= MODE_PLOTTING) then
! Allocate and setup tally stride, filter_matches, and tally maps
call configure_tallies()
! Set up tally procedure pointers
call init_tally_routines()
! Determine how much work each processor should do
call calculate_work()
! Allocate source bank, and for eigenvalue simulations also allocate the
! fission bank
call allocate_banks()
! If this is a restart run, load the state point data and binary source
! file
if (restart_run) call load_state_point()
end if
if (master) then
if (run_mode == MODE_PLOTTING) then
! Display plotting information
if (verbosity >= 5) call print_plot()
else
! Write summary information
if (output_summary) call write_summary()
end if
end if
! Check for particle restart run
if (particle_restart_run) run_mode = MODE_PARTICLE
! Warn if overlap checking is on
if (master .and. check_overlaps .and. run_mode /= MODE_PLOTTING) then
call warning("Cell overlap checking is ON.")
end if
! Stop initialization timer
call time_initialize%stop()
@ -172,6 +111,7 @@ contains
integer, intent(in) :: intracomm ! MPI intracommunicator
#endif
integer :: mpi_err ! MPI error code
integer :: bank_blocks(6) ! Count for each datatype
#ifdef MPIF08
type(MPI_Datatype) :: bank_types(6)
@ -427,450 +367,4 @@ contains
end subroutine read_command_line
!===============================================================================
! ADJUST_INDICES changes the values for 'surfaces' for each cell and the
! material index assigned to each to the indices in the surfaces and material
! array rather than the unique IDs assigned to each surface and material. Also
! assigns boundary conditions to surfaces based on those read into the bc_dict
! dictionary
!===============================================================================
subroutine adjust_indices()
integer :: i ! index for various purposes
integer :: j ! index for various purposes
integer :: k ! loop index for lattices
integer :: m ! loop index for lattices
integer :: lid ! lattice IDs
integer :: i_array ! index in surfaces/materials array
integer :: id ! user-specified id
type(Cell), pointer :: c => null()
class(Lattice), pointer :: lat => null()
do i = 1, n_cells
! =======================================================================
! ADJUST REGION SPECIFICATION FOR EACH CELL
c => cells(i)
do j = 1, size(c%region)
id = c%region(j)
! Make sure that only regions are checked. Since OP_UNION is the
! operator with the lowest integer value, anything below it must denote
! a half-space
if (id < OP_UNION) then
if (surface_dict%has_key(abs(id))) then
i_array = surface_dict%get_key(abs(id))
c%region(j) = sign(i_array, id)
else
call fatal_error("Could not find surface " // trim(to_str(abs(id)))&
&// " specified on cell " // trim(to_str(c%id)))
end if
end if
end do
! Also adjust the indices in the reverse Polish notation
do j = 1, size(c%rpn)
id = c%rpn(j)
! Again, make sure that only regions are checked
if (id < OP_UNION) then
i_array = surface_dict%get_key(abs(id))
c%rpn(j) = sign(i_array, id)
end if
end do
! =======================================================================
! ADJUST UNIVERSE INDEX FOR EACH CELL
id = c%universe
if (universe_dict%has_key(id)) then
c%universe = universe_dict%get_key(id)
else
call fatal_error("Could not find universe " // trim(to_str(id)) &
&// " specified on cell " // trim(to_str(c%id)))
end if
! =======================================================================
! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL
if (c % material(1) == NONE) then
id = c % fill
if (universe_dict % has_key(id)) then
c % type = FILL_UNIVERSE
c % fill = universe_dict % get_key(id)
elseif (lattice_dict % has_key(id)) then
lid = lattice_dict % get_key(id)
c % type = FILL_LATTICE
c % fill = lid
else
call fatal_error("Specified fill " // trim(to_str(id)) // " on cell "&
// trim(to_str(c % id)) // " is neither a universe nor a &
&lattice.")
end if
else
do j = 1, size(c % material)
id = c % material(j)
if (id == MATERIAL_VOID) then
c % type = FILL_MATERIAL
else if (material_dict % has_key(id)) then
c % type = FILL_MATERIAL
c % material(j) = material_dict % get_key(id)
else
call fatal_error("Could not find material " // trim(to_str(id)) &
// " specified on cell " // trim(to_str(c % id)))
end if
end do
end if
end do
! ==========================================================================
! ADJUST UNIVERSE INDICES FOR EACH LATTICE
do i = 1, n_lattices
lat => lattices(i)%obj
select type (lat)
type is (RectLattice)
do m = 1, lat%n_cells(3)
do k = 1, lat%n_cells(2)
do j = 1, lat%n_cells(1)
id = lat%universes(j,k,m)
if (universe_dict%has_key(id)) then
lat%universes(j,k,m) = universe_dict%get_key(id)
else
call fatal_error("Invalid universe number " &
&// trim(to_str(id)) // " specified on lattice " &
&// trim(to_str(lat%id)))
end if
end do
end do
end do
type is (HexLattice)
do m = 1, lat%n_axial
do k = 1, 2*lat%n_rings - 1
do j = 1, 2*lat%n_rings - 1
if (j + k < lat%n_rings + 1) then
cycle
else if (j + k > 3*lat%n_rings - 1) then
cycle
end if
id = lat%universes(j, k, m)
if (universe_dict%has_key(id)) then
lat%universes(j, k, m) = universe_dict%get_key(id)
else
call fatal_error("Invalid universe number " &
&// trim(to_str(id)) // " specified on lattice " &
&// trim(to_str(lat%id)))
end if
end do
end do
end do
end select
if (lat%outer /= NO_OUTER_UNIVERSE) then
if (universe_dict%has_key(lat%outer)) then
lat%outer = universe_dict%get_key(lat%outer)
else
call fatal_error("Invalid universe number " &
&// trim(to_str(lat%outer)) &
&// " specified on lattice " // trim(to_str(lat%id)))
end if
end if
end do
! =======================================================================
! ADJUST INDICES FOR EACH TALLY FILTER
FILTER_LOOP: do i = 1, n_filters
select type(filt => filters(i) % obj)
type is (SurfaceFilter)
! Check if this is a surface filter only for surface currents
if (.not. filt % current) call filt % initialize()
class default
call filt % initialize()
end select
end do FILTER_LOOP
end subroutine adjust_indices
!===============================================================================
! CALCULATE_WORK determines how many particles each processor should simulate
!===============================================================================
subroutine calculate_work()
integer :: i ! loop index
integer :: remainder ! Number of processors with one extra particle
integer(8) :: i_bank ! Running count of number of particles
integer(8) :: min_work ! Minimum number of particles on each proc
integer(8) :: work_i ! Number of particles on rank i
allocate(work_index(0:n_procs))
! Determine minimum amount of particles to simulate on each processor
min_work = n_particles/n_procs
! Determine number of processors that have one extra particle
remainder = int(mod(n_particles, int(n_procs,8)), 4)
i_bank = 0
work_index(0) = 0
do i = 0, n_procs - 1
! Number of particles for rank i
if (i < remainder) then
work_i = min_work + 1
else
work_i = min_work
end if
! Set number of particles
if (rank == i) work = work_i
! Set index into source bank for rank i
i_bank = i_bank + work_i
work_index(i+1) = i_bank
end do
end subroutine calculate_work
!===============================================================================
! ALLOCATE_BANKS allocates memory for the fission and source banks
!===============================================================================
subroutine allocate_banks()
integer :: alloc_err ! allocation error code
! Allocate source bank
allocate(source_bank(work), STAT=alloc_err)
! Check for allocation errors
if (alloc_err /= 0) then
call fatal_error("Failed to allocate source bank.")
end if
if (run_mode == MODE_EIGENVALUE) then
#ifdef _OPENMP
! If OpenMP is being used, each thread needs its own private fission
! bank. Since the private fission banks need to be combined at the end of
! a generation, there is also a 'master_fission_bank' that is used to
! collect the sites from each thread.
n_threads = omp_get_max_threads()
!$omp parallel
thread_id = omp_get_thread_num()
if (thread_id == 0) then
allocate(fission_bank(3*work))
else
allocate(fission_bank(3*work/n_threads))
end if
!$omp end parallel
allocate(master_fission_bank(3*work), STAT=alloc_err)
#else
allocate(fission_bank(3*work), STAT=alloc_err)
#endif
! Check for allocation errors
if (alloc_err /= 0) then
call fatal_error("Failed to allocate fission bank.")
end if
end if
end subroutine allocate_banks
!===============================================================================
! PREPARE_DISTRIBCELL initializes any distribcell filters present and sets the
! offsets for distribcells
!===============================================================================
subroutine prepare_distribcell()
integer :: i, j ! Tally, filter loop counters
logical :: distribcell_active ! Does simulation use distribcell?
integer, allocatable :: univ_list(:) ! Target offsets
integer, allocatable :: counts(:,:) ! Target count
logical, allocatable :: found(:,:) ! Target found
! Assume distribcell is not needed until proven otherwise.
distribcell_active = .false.
! We need distribcell if any tallies have distribcell filters.
do i = 1, n_tallies
do j = 1, size(tallies(i) % filter)
select type(filt => filters(tallies(i) % filter(j)) % obj)
type is (DistribcellFilter)
distribcell_active = .true.
end select
end do
end do
! We also need distribcell if any distributed materials or distributed
! temperatues are present.
if (.not. distribcell_active) then
do i = 1, n_cells
if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then
distribcell_active = .true.
exit
end if
end do
end if
! If distribcell isn't used in this simulation then no more work left to do.
if (.not. distribcell_active) return
! Count the number of instances of each cell.
call count_instance(universes(root_universe))
! Set the number of bins in all distribcell filters.
do i = 1, n_tallies
do j = 1, size(tallies(i) % filter)
select type(filt => filters(tallies(i) % filter(j)) % obj)
type is (DistribcellFilter)
! Set the number of bins to the number of instances of the cell.
filt % n_bins = cells(filt % cell) % instances
end select
end do
end do
! Make sure the number of materials and temperatures matches the number of
! cell instances.
do i = 1, n_cells
associate (c => cells(i))
if (size(c % material) > 1) then
if (size(c % material) /= c % instances) then
call fatal_error("Cell " // trim(to_str(c % id)) // " was &
&specified with " // trim(to_str(size(c % material))) &
// " materials but has " // trim(to_str(c % instances)) &
// " distributed instances. The number of materials must &
&equal one or the number of instances.")
end if
end if
if (size(c % sqrtkT) > 1) then
if (size(c % sqrtkT) /= c % instances) then
call fatal_error("Cell " // trim(to_str(c % id)) // " was &
&specified with " // trim(to_str(size(c % sqrtkT))) &
// " temperatures but has " // trim(to_str(c % instances)) &
// " distributed instances. The number of temperatures must &
&equal one or the number of instances.")
end if
end if
end associate
end do
! Allocate offset maps at each level in the geometry
call allocate_offsets(univ_list, counts, found)
! Calculate offsets for each target distribcell
do i = 1, n_maps
do j = 1, n_universes
call calc_offsets(univ_list(i), i, universes(j), counts, found)
end do
end do
end subroutine prepare_distribcell
!===============================================================================
! ALLOCATE_OFFSETS determines the number of maps needed and allocates required
! memory for distribcell offset tables
!===============================================================================
recursive subroutine allocate_offsets(univ_list, counts, found)
integer, intent(out), allocatable :: univ_list(:) ! Target offsets
integer, intent(out), allocatable :: counts(:,:) ! Target count
logical, intent(out), allocatable :: found(:,:) ! Target found
integer :: i, j, k ! Loop counters
type(SetInt) :: cell_list ! distribells to track
! Begin gathering list of cells in distribcell tallies
n_maps = 0
! List all cells referenced in distribcell filters.
do i = 1, n_tallies
do j = 1, size(tallies(i) % filter)
select type(filt => filters(tallies(i) % filter(j)) % obj)
type is (DistribcellFilter)
call cell_list % add(filt % cell)
end select
end do
end do
! List all cells with multiple (distributed) materials or temperatures.
do i = 1, n_cells
if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then
call cell_list % add(i)
end if
end do
! Compute the number of unique universes containing these distribcells
! to determine the number of offset tables to allocate
do i = 1, n_universes
do j = 1, size(universes(i) % cells)
if (cell_list % contains(universes(i) % cells(j))) then
n_maps = n_maps + 1
end if
end do
end do
! Allocate the list of offset tables for each unique universe
allocate(univ_list(n_maps))
! Allocate list to accumulate target distribcell counts in each universe
allocate(counts(n_universes, n_maps))
counts(:,:) = 0
! Allocate list to track if target distribcells are found in each universe
allocate(found(n_universes, n_maps))
found(:,:) = .false.
! Search through universes for distributed cells and assign each one a
! unique distribcell array index.
k = 1
do i = 1, n_universes
do j = 1, size(universes(i) % cells)
if (cell_list % contains(universes(i) % cells(j))) then
cells(universes(i) % cells(j)) % distribcell_index = k
univ_list(k) = universes(i) % id
k = k + 1
end if
end do
end do
! Allocate the offset tables for lattices
do i = 1, n_lattices
associate(lat => lattices(i) % obj)
select type(lat)
type is (RectLattice)
allocate(lat % offset(n_maps, lat % n_cells(1), lat % n_cells(2), &
lat % n_cells(3)))
type is (HexLattice)
allocate(lat % offset(n_maps, 2 * lat % n_rings - 1, &
2 * lat % n_rings - 1, lat % n_axial))
end select
lat % offset(:, :, :, :) = 0
end associate
end do
! Allocate offset table for fill cells
do i = 1, n_cells
if (cells(i) % type /= FILL_MATERIAL) then
allocate(cells(i) % offset(n_maps))
end if
end do
! Free up memory
call cell_list % clear()
end subroutine allocate_offsets
end module initialize

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,18 @@
program main
use constants
use finalize, only: openmc_finalize
use global
use initialize, only: openmc_init
use message_passing
use particle_restart, only: run_particle_restart
use plot, only: openmc_plot_geometry
use simulation, only: openmc_run
use volume_calc, only: openmc_calculate_volumes
use openmc_api, only: openmc_init, openmc_finalize, openmc_run, &
openmc_plot_geometry, openmc_calculate_volumes
use particle_restart, only: run_particle_restart
use settings, only: run_mode
implicit none
#ifdef MPI
integer :: mpi_err ! MPI error code
#endif
! Initialize run -- when run with MPI, pass communicator
#ifdef MPI
#ifdef MPIF08

View file

@ -1,18 +1,36 @@
module material_header
use, intrinsic :: ISO_C_BINDING
use constants
use nuclide_header, only: Nuclide
use dict_header, only: DictIntInt
use error
use nuclide_header
use sab_header
use stl_vector, only: VectorReal, VectorInt
use string, only: to_str
implicit none
private
public :: free_memory_material
public :: openmc_extend_materials
public :: openmc_get_material_index
public :: openmc_material_add_nuclide
public :: openmc_material_get_id
public :: openmc_material_get_densities
public :: openmc_material_set_density
public :: openmc_material_set_densities
public :: openmc_material_set_id
!===============================================================================
! MATERIAL describes a material by its constituent nuclides
!===============================================================================
type Material
type, public :: Material
integer :: id ! unique identifier
character(len=104) :: name = "" ! User-defined name
integer :: n_nuclides ! number of nuclides
integer :: n_nuclides = 0 ! number of nuclides
integer, allocatable :: nuclide(:) ! index in nuclides array
integer, allocatable :: element(:) ! index in elements array
real(8) :: density ! total atom density in atom/b-cm
@ -45,25 +63,31 @@ module material_header
contains
procedure :: set_density => material_set_density
procedure :: assign_sab_tables => material_assign_sab_tables
end type Material
integer(C_INT32_T), public, bind(C) :: n_materials ! # of materials
type(Material), public, allocatable, target :: materials(:)
! Dictionary that maps user IDs to indices in 'materials'
type(DictIntInt), public :: material_dict
contains
!===============================================================================
! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm.
!===============================================================================
function material_set_density(m, density, nuclides) result(err)
function material_set_density(m, density) result(err)
class(Material), intent(inout) :: m
real(8), intent(in) :: density
type(Nuclide), intent(in) :: nuclides(:)
integer :: err
integer :: i
real(8) :: sum_percent
real(8) :: awr
err = -1
if (allocated(m % atom_density)) then
! Set total density based on value provided
m % density = density
@ -83,7 +107,393 @@ contains
+ m % atom_density(i) * awr * MASS_NEUTRON / N_AVOGADRO
end do
err = 0
else
err = E_ALLOCATE
call set_errmsg("Material atom density array hasn't been allocated.")
end if
end function material_set_density
!===============================================================================
! ASSIGN_SAB_TABLES assigns S(alpha,beta) tables to specific nuclides within
! materials so the code knows when to apply bound thermal scattering data
!===============================================================================
subroutine material_assign_sab_tables(this)
class(Material), intent(inout) :: this
integer :: j ! index over nuclides in material
integer :: k ! index over S(a,b) tables in material
integer :: m ! position for sorting
integer :: temp_nuclide ! temporary value for sorting
integer :: temp_table ! temporary value for sorting
real(8) :: temp_frac ! temporary value for sorting
logical :: found
type(VectorInt) :: i_sab_tables
type(VectorInt) :: i_sab_nuclides
type(VectorReal) :: sab_fracs
if (.not. allocated(this % i_sab_tables)) return
ASSIGN_SAB: do k = 1, size(this % i_sab_tables)
! In order to know which nuclide the S(a,b) table applies to, we need
! to search through the list of nuclides for one which has a matching
! name
found = .false.
associate (sab => sab_tables(this % i_sab_tables(k)))
FIND_NUCLIDE: do j = 1, size(this % nuclide)
if (any(sab % nuclides == nuclides(this % nuclide(j)) % name)) then
call i_sab_tables % push_back(this % i_sab_tables(k))
call i_sab_nuclides % push_back(j)
call sab_fracs % push_back(this % sab_fracs(k))
found = .true.
end if
end do FIND_NUCLIDE
end associate
! Check to make sure S(a,b) table matched a nuclide
if (.not. found) then
call fatal_error("S(a,b) table " // trim(this % &
sab_names(k)) // " did not match any nuclide on material " &
// trim(to_str(this % id)))
end if
end do ASSIGN_SAB
! Make sure each nuclide only appears in one table.
do j = 1, i_sab_nuclides % size()
do k = j+1, i_sab_nuclides % size()
if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then
call fatal_error(trim( &
nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) &
// " in material " // trim(to_str(this % id)) // " was found &
&in multiple S(a,b) tables. Each nuclide can only appear in &
&one S(a,b) table per material.")
end if
end do
end do
! Update i_sab_tables and i_sab_nuclides
deallocate(this % i_sab_tables)
deallocate(this % sab_fracs)
if (allocated(this % i_sab_nuclides)) deallocate(this % i_sab_nuclides)
m = i_sab_tables % size()
allocate(this % i_sab_tables(m))
allocate(this % i_sab_nuclides(m))
allocate(this % sab_fracs(m))
this % i_sab_tables(:) = i_sab_tables % data(1:m)
this % i_sab_nuclides(:) = i_sab_nuclides % data(1:m)
this % sab_fracs(:) = sab_fracs % data(1:m)
! Clear entries in vectors for next material
call i_sab_tables % clear()
call i_sab_nuclides % clear()
call sab_fracs % clear()
! If there are multiple S(a,b) tables, we need to make sure that the
! entries in i_sab_nuclides are sorted or else they won't be applied
! correctly in the cross_section module. The algorithm here is a simple
! insertion sort -- don't need anything fancy!
if (size(this % i_sab_tables) > 1) then
SORT_SAB: do k = 2, size(this % i_sab_tables)
! Save value to move
m = k
temp_nuclide = this % i_sab_nuclides(k)
temp_table = this % i_sab_tables(k)
temp_frac = this % i_sab_tables(k)
MOVE_OVER: do
! Check if insertion value is greater than (m-1)th value
if (temp_nuclide >= this % i_sab_nuclides(m-1)) exit
! Move values over until hitting one that's not larger
this % i_sab_nuclides(m) = this % i_sab_nuclides(m-1)
this % i_sab_tables(m) = this % i_sab_tables(m-1)
this % sab_fracs(m) = this % sab_fracs(m-1)
m = m - 1
! Exit if we've reached the beginning of the list
if (m == 1) exit
end do MOVE_OVER
! Put the original value into its new position
this % i_sab_nuclides(m) = temp_nuclide
this % i_sab_tables(m) = temp_table
this % sab_fracs(m) = temp_frac
end do SORT_SAB
end if
! Deallocate temporary arrays for names of nuclides and S(a,b) tables
if (allocated(this % names)) deallocate(this % names)
end subroutine material_assign_sab_tables
!===============================================================================
! FREE_MEMORY_MATERIAL deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_material()
n_materials = 0
if (allocated(materials)) deallocate(materials)
call material_dict % clear()
end subroutine free_memory_material
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_extend_materials(n, index_start, index_end) result(err) bind(C)
! Extend the materials array by n elements
integer(C_INT32_T), value, intent(in) :: n
integer(C_INT32_T), optional, intent(out) :: index_start
integer(C_INT32_T), optional, intent(out) :: index_end
integer(C_INT) :: err
type(Material), allocatable :: temp(:) ! temporary materials array
if (n_materials == 0) then
! Allocate materials array
allocate(materials(n))
else
! Allocate materials array with increased size
allocate(temp(n_materials + n))
! Move original materials to temporary array
temp(1:n_materials) = materials(:)
! Move allocation from temporary array
call move_alloc(FROM=temp, TO=materials)
end if
! Return indices in materials array
if (present(index_start)) index_start = n_materials + 1
if (present(index_end)) index_end = n_materials + n
n_materials = n_materials + n
err = 0
end function openmc_extend_materials
function openmc_get_material_index(id, index) result(err) bind(C)
! Returns the index in the materials array of a material with a given ID
integer(C_INT32_T), value :: id
integer(C_INT32_T), intent(out) :: index
integer(C_INT) :: err
if (allocated(materials)) then
if (material_dict % has(id)) then
index = material_dict % get(id)
err = 0
else
err = E_INVALID_ID
call set_errmsg("No material exists with ID=" // trim(to_str(id)) // ".")
end if
else
err = E_ALLOCATE
call set_errmsg("Memory has not been allocated for materials.")
end if
end function openmc_get_material_index
function openmc_material_add_nuclide(index, name, density) result(err) bind(C)
! Add a nuclide at a specified density in atom/b-cm to a material
integer(C_INT32_T), value, intent(in) :: index
character(kind=C_CHAR) :: name(*)
real(C_DOUBLE), value, intent(in) :: density
integer(C_INT) :: err
integer :: j, k, n
real(8) :: awr
integer, allocatable :: new_nuclide(:)
real(8), allocatable :: new_density(:)
logical, allocatable :: new_p0(:)
character(:), allocatable :: name_
name_ = to_f_string(name)
err = E_UNASSIGNED
if (index >= 1 .and. index <= size(materials)) then
associate (m => materials(index))
! Check if nuclide is already in material
do j = 1, m % n_nuclides
k = m % nuclide(j)
if (nuclides(k) % name == name_) then
awr = nuclides(k) % awr
m % density = m % density + density - m % atom_density(j)
m % density_gpcc = m % density_gpcc + (density - &
m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO
m % atom_density(j) = density
err = 0
end if
end do
! If nuclide wasn't found, extend nuclide/density arrays
if (err /= 0) then
! If nuclide hasn't been loaded, load it now
err = openmc_load_nuclide(name)
if (err == 0) then
! Extend arrays
n = m % n_nuclides
allocate(new_nuclide(n + 1))
if (n > 0) new_nuclide(1:n) = m % nuclide
call move_alloc(FROM=new_nuclide, TO=m % nuclide)
allocate(new_density(n + 1))
if (n > 0) new_density(1:n) = m % atom_density
call move_alloc(FROM=new_density, TO=m % atom_density)
allocate(new_p0(n + 1))
if (n > 0) new_p0(1:n) = m % p0
new_p0(n + 1) = .false.
call move_alloc(FROM=new_p0, TO=m % p0)
! Append new nuclide/density
k = nuclide_dict % get(to_lower(name_))
m % nuclide(n + 1) = k
m % atom_density(n + 1) = density
m % density = m % density + density
m % density_gpcc = m % density_gpcc + &
density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO
m % n_nuclides = n + 1
end if
end if
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_add_nuclide
function openmc_material_get_densities(index, nuclides, densities, n) &
result(err) bind(C)
! returns an array of nuclide densities in a material
integer(C_INT32_T), value :: index
type(C_PTR), intent(out) :: nuclides
type(C_PTR), intent(out) :: densities
integer(C_INT), intent(out) :: n
integer(C_INT) :: err
if (index >= 1 .and. index <= size(materials)) then
associate (m => materials(index))
if (allocated(m % atom_density)) then
nuclides = C_LOC(m % nuclide(1))
densities = C_LOC(m % atom_density(1))
n = size(m % atom_density)
err = 0
else
err = E_ALLOCATE
call set_errmsg("Material atom density array has not been allocated.")
end if
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_get_densities
function openmc_material_get_id(index, id) result(err) bind(C)
! returns the ID of a material
integer(C_INT32_T), value :: index
integer(C_INT32_T), intent(out) :: id
integer(C_INT) :: err
if (index >= 1 .and. index <= size(materials)) then
id = materials(index) % id
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_get_id
function openmc_material_set_id(index, id) result(err) bind(C)
! Set the ID of a material
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT32_T), value, intent(in) :: id
integer(C_INT) :: err
if (index >= 1 .and. index <= n_materials) then
materials(index) % id = id
call material_dict % set(id, index)
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_set_id
function openmc_material_set_density(index, density) result(err) bind(C)
! Set the total density of a material in atom/b-cm
integer(C_INT32_T), value, intent(in) :: index
real(C_DOUBLE), value, intent(in) :: density
integer(C_INT) :: err
err = E_UNASSIGNED
if (index >= 1 .and. index <= size(materials)) then
associate (m => materials(index))
err = m % set_density(density)
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_set_density
function openmc_material_set_densities(index, n, name, density) result(err) bind(C)
! Sets the densities for a list of nuclides in a material. If the nuclides
! don't already exist in the material, they will be added
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), value, intent(in) :: n
type(C_PTR), intent(in) :: name(n)
real(C_DOUBLE), intent(in) :: density(n)
integer(C_INT) :: err
integer :: i
integer :: stat
character(C_CHAR), pointer :: string(:)
character(len=:, kind=C_CHAR), allocatable :: name_
if (index >= 1 .and. index <= size(materials)) then
associate (m => materials(index))
! If nuclide/density arrays are not correct size, reallocate
if (n /= m % n_nuclides) then
deallocate(m % nuclide, m % atom_density, m % p0, STAT=stat)
allocate(m % nuclide(n), m % atom_density(n), m % p0(n))
end if
do i = 1, n
! Convert C string to Fortran string
call c_f_pointer(name(i), string, [10])
name_ = to_lower(to_f_string(string))
if (.not. nuclide_dict % has(name_)) then
err = openmc_load_nuclide(string)
if (err < 0) return
end if
m % nuclide(i) = nuclide_dict % get(name_)
m % atom_density(i) = density(i)
end do
m % n_nuclides = n
! Set isotropic flags to flags
m % p0(:) = .false.
! Set total density to the sum of the vector
err = m % set_density(sum(density))
! Assign S(a,b) tables
call m % assign_sab_tables()
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in materials array is out of bounds.")
end if
end function openmc_material_set_densities
end module material_header

View file

@ -1,8 +1,8 @@
module mesh
use algorithm, only: binary_search
use bank_header, only: bank
use constants
use global
use mesh_header
use message_passing
@ -10,116 +10,6 @@ module mesh
contains
!===============================================================================
! GET_MESH_BIN determines the tally bin for a particle in a structured mesh
!===============================================================================
pure subroutine get_mesh_bin(m, xyz, bin)
type(RegularMesh), intent(in) :: m ! mesh pointer
real(8), intent(in) :: xyz(:) ! coordinates
integer, intent(out) :: bin ! tally bin
integer :: n ! size of mesh
integer :: d ! mesh dimension index
integer :: ijk(3) ! indices in mesh
logical :: in_mesh ! was given coordinate in mesh at all?
! Get number of dimensions
n = m % n_dimension
! Loop over the dimensions of the mesh
do d = 1, n
! Check for cases where particle is outside of mesh
if (xyz(d) < m % lower_left(d)) then
bin = NO_BIN_FOUND
return
elseif (xyz(d) > m % upper_right(d)) then
bin = NO_BIN_FOUND
return
end if
end do
! Determine indices
call get_mesh_indices(m, xyz, ijk, in_mesh)
! Convert indices to bin
if (in_mesh) then
bin = mesh_indices_to_bin(m, ijk)
else
bin = NO_BIN_FOUND
end if
end subroutine get_mesh_bin
!===============================================================================
! GET_MESH_INDICES determines the indices of a particle in a structured mesh
!===============================================================================
pure subroutine get_mesh_indices(m, xyz, ijk, in_mesh)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz(:) ! coordinates to check
integer, intent(out) :: ijk(:) ! indices in mesh
logical, intent(out) :: in_mesh ! were given coords in mesh?
! Find particle in mesh
ijk(:m % n_dimension) = ceiling((xyz(:m % n_dimension) - m % lower_left)/m % width)
! Determine if particle is in mesh
if (any(ijk(:m % n_dimension) < 1) .or. &
any(ijk(:m % n_dimension) > m % dimension)) then
in_mesh = .false.
else
in_mesh = .true.
end if
end subroutine get_mesh_indices
!===============================================================================
! MESH_INDICES_TO_BIN maps (i), (i,j), or (i,j,k) indices to a single bin number
! for use in a TallyObject results array
!===============================================================================
pure function mesh_indices_to_bin(m, ijk) result(bin)
type(RegularMesh), intent(in) :: m
integer, intent(in) :: ijk(:)
integer :: bin
if (m % n_dimension == 1) then
bin = ijk(1)
elseif (m % n_dimension == 2) then
bin = (ijk(2) - 1) * m % dimension(1) + ijk(1)
elseif (m % n_dimension == 3) then
bin = ((ijk(3) - 1) * m % dimension(2) + (ijk(2) - 1)) &
* m % dimension(1) + ijk(1)
end if
end function mesh_indices_to_bin
!===============================================================================
! BIN_TO_MESH_INDICES maps a single mesh bin from a TallyObject results array to
! (i), (i,j), or (i,j,k) indices
!===============================================================================
pure subroutine bin_to_mesh_indices(m, bin, ijk)
type(RegularMesh), intent(in) :: m
integer, intent(in) :: bin
integer, intent(out) :: ijk(:)
if (m % n_dimension == 1) then
ijk(1) = bin
else if (m % n_dimension == 2) then
ijk(1) = mod(bin - 1, m % dimension(1)) + 1
ijk(2) = (bin - 1)/m % dimension(1) + 1
else if (m % n_dimension == 3) then
ijk(1) = mod(bin - 1, m % dimension(1)) + 1
ijk(2) = mod(bin - 1, m % dimension(1) * m % dimension(2)) &
/ m % dimension(1) + 1
ijk(3) = (bin - 1)/(m % dimension(1) * m % dimension(2)) + 1
end if
end subroutine bin_to_mesh_indices
!===============================================================================
! COUNT_BANK_SITES determines the number of fission bank sites in each cell of a
! given mesh as well as an optional energy group structure. This can be used for
@ -130,28 +20,28 @@ contains
subroutine count_bank_sites(m, bank_array, cnt, energies, size_bank, &
sites_outside)
type(RegularMesh), pointer :: m ! mesh to count sites
type(RegularMesh), intent(in) :: m ! mesh to count sites
type(Bank), intent(in) :: bank_array(:) ! fission or source bank
real(8), intent(out) :: cnt(:,:,:,:) ! weight of sites in each
real(8), intent(out) :: cnt(:,:) ! weight of sites in each
! cell and energy group
real(8), intent(in), optional :: energies(:) ! energy grid to search
integer(8), intent(in), optional :: size_bank ! # of bank sites (on each proc)
logical, intent(inout), optional :: sites_outside ! were there sites outside mesh?
real(8), allocatable :: cnt_(:,:)
integer :: i ! loop index for local fission sites
integer :: n_sites ! size of bank array
integer :: ijk(3) ! indices on mesh
integer :: n_groups ! number of groups in energies
integer :: e_bin ! energy_bin
logical :: in_mesh ! was single site outside mesh?
logical :: outside ! was any site outside mesh?
integer :: n ! number of energy groups / size
integer :: mesh_bin ! mesh bin
integer :: e_bin ! energy bin
#ifdef MPI
integer :: n ! total size of count variable
real(8) :: dummy ! temporary receive buffer for non-root reductions
integer :: mpi_err ! MPI error code
#endif
logical :: outside ! was any site outside mesh?
! initialize variables
cnt = ZERO
allocate(cnt_(size(cnt,1), size(cnt,2)))
cnt_ = ZERO
outside = .false.
! Set size of bank
@ -163,18 +53,18 @@ contains
! Determine number of energies in group structure
if (present(energies)) then
n_groups = size(energies) - 1
n = size(energies) - 1
else
n_groups = 1
n = 1
end if
! loop over fission sites and count how many are in each mesh box
FISSION_SITES: do i = 1, n_sites
! determine scoring bin for entropy mesh
call get_mesh_indices(m, bank_array(i) % xyz, ijk, in_mesh)
call m % get_bin(bank_array(i) % xyz, mesh_bin)
! if outside mesh, skip particle
if (.not. in_mesh) then
if (mesh_bin == NO_BIN_FOUND) then
outside = .true.
cycle
end if
@ -183,266 +73,34 @@ contains
if (present(energies)) then
if (bank_array(i) % E < energies(1)) then
e_bin = 1
elseif (bank_array(i) % E > energies(n_groups+1)) then
e_bin = n_groups
elseif (bank_array(i) % E > energies(n + 1)) then
e_bin = n
else
e_bin = binary_search(energies, n_groups + 1, bank_array(i) % E)
e_bin = binary_search(energies, n + 1, bank_array(i) % E)
end if
else
e_bin = 1
end if
! add to appropriate mesh box
cnt(e_bin,ijk(1),ijk(2),ijk(3)) = cnt(e_bin,ijk(1),ijk(2),ijk(3)) + &
bank_array(i) % wgt
cnt_(e_bin, mesh_bin) = cnt_(e_bin, mesh_bin) + bank_array(i) % wgt
end do FISSION_SITES
#ifdef MPI
! determine total number of mesh cells
n = n_groups * size(cnt,2) * size(cnt,3) * size(cnt,4)
! collect values from all processors
if (master) then
call MPI_REDUCE(MPI_IN_PLACE, cnt, n, MPI_REAL8, MPI_SUM, 0, &
mpi_intracomm, mpi_err)
else
! Receive buffer not significant at other processors
call MPI_REDUCE(cnt, dummy, n, MPI_REAL8, MPI_SUM, 0, &
mpi_intracomm, mpi_err)
end if
n = size(cnt_)
call MPI_REDUCE(cnt_, cnt, n, MPI_REAL8, MPI_SUM, 0, mpi_intracomm, mpi_err)
! Check if there were sites outside the mesh for any processor
if (present(sites_outside)) then
call MPI_REDUCE(outside, sites_outside, 1, MPI_LOGICAL, MPI_LOR, 0, &
mpi_intracomm, mpi_err)
end if
#else
sites_outside = outside
cnt = cnt_
#endif
end subroutine count_bank_sites
!===============================================================================
! MESH_INTERSECTS determines if a line between xyz0 and xyz1 intersects the
! outer boundary of the given mesh. This is important for determining whether a
! track will score to a mesh tally.
!===============================================================================
pure function mesh_intersects_1d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(1)
real(8), intent(in) :: xyz1(1)
logical :: intersects
real(8) :: x0 ! track start point
real(8) :: x1 ! track end point
real(8) :: xm0 ! lower-left coordinates of mesh
real(8) :: xm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
! Copy coordinates of ending point
x1 = xyz1(1)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
intersects = .true.
return
end if
! Check if line intersects right surface
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
intersects = .true.
return
end if
end function mesh_intersects_1d
pure function mesh_intersects_2d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(2)
real(8), intent(in) :: xyz1(2)
logical :: intersects
real(8) :: x0, y0 ! track start point
real(8) :: x1, y1 ! track end point
real(8) :: xi, yi ! track intersection point with mesh
real(8) :: xm0, ym0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! y
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! x
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection
! point y
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! x
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_2d
pure function mesh_intersects_3d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(3)
real(8), intent(in) :: xyz1(3)
logical :: intersects
real(8) :: x0, y0, z0 ! track start point
real(8) :: x1, y1, z1 ! track end point
real(8) :: xi, yi, zi ! track intersection point with mesh
real(8) :: xm0, ym0, zm0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1, zm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
z0 = xyz0(3)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
z1 = xyz1(3)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
zm0 = m % lower_left(3)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
zm1 = m % upper_right(3)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! (y,z)
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! (x,z)
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects bottom surface -- calculate the intersection
! point (x,y)
if ((z0 < zm0 .and. z1 > zm0) .or. (z0 > zm0 .and. z1 < zm0)) then
xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection point
! (y,z)
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! (x,z)
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects top surface -- calculate the intersection point
! (x,y)
if ((z0 < zm1 .and. z1 > zm1) .or. (z0 > zm1 .and. z1 < zm1)) then
xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_3d
end module mesh

View file

@ -1,14 +1,28 @@
module mesh_header
use, intrinsic :: ISO_C_BINDING
use hdf5
use constants
use dict_header, only: DictIntInt
use error, only: warning, fatal_error
use hdf5_interface
use string, only: to_str, to_lower
use xml_interface
implicit none
private
public :: free_memory_mesh
public :: openmc_extend_meshes
!===============================================================================
! STRUCTUREDMESH represents a tessellation of n-dimensional Euclidean space by
! congruent squares or cubes
!===============================================================================
type RegularMesh
integer :: id ! user-specified id
type, public :: RegularMesh
integer :: id = -1 ! user-specified id
integer :: type ! rectangular, hexagonal
integer :: n_dimension ! rank of mesh
real(8) :: volume_frac ! volume fraction of each cell
@ -16,6 +30,566 @@ module mesh_header
real(8), allocatable :: lower_left(:) ! lower-left corner of mesh
real(8), allocatable :: upper_right(:) ! upper-right corner of mesh
real(8), allocatable :: width(:) ! width of each mesh cell
contains
procedure :: from_xml => regular_from_xml
procedure :: get_bin => regular_get_bin
procedure :: get_indices => regular_get_indices
procedure :: get_bin_from_indices => regular_get_bin_from_indices
procedure :: get_indices_from_bin => regular_get_indices_from_bin
procedure :: intersects => regular_intersects
procedure :: to_hdf5 => regular_to_hdf5
end type RegularMesh
integer(C_INT32_T), public, bind(C) :: n_meshes = 0 ! # of structured meshes
type(RegularMesh), public, allocatable, target :: meshes(:)
! Dictionary that maps user IDs to indices in 'meshes'
type(DictIntInt), public :: mesh_dict
contains
subroutine regular_from_xml(this, node)
class(RegularMesh), intent(inout) :: this
type(XMLNode), intent(in) :: node
integer :: n
character(MAX_LINE_LEN) :: temp_str
! Copy mesh id
if (check_for_node(node, "id")) then
call get_node_value(node, "id", this % id)
! Check to make sure 'id' hasn't been used
if (mesh_dict % has(this % id)) then
call fatal_error("Two or more meshes use the same unique ID: " &
// to_str(this % id))
end if
end if
! Read mesh type
if (check_for_node(node, "type")) then
call get_node_value(node, "type", temp_str)
select case (to_lower(temp_str))
case ('rect', 'rectangle', 'rectangular')
call warning("Mesh type '" // trim(temp_str) // "' is deprecated. &
&Please use 'regular' instead.")
this % type = MESH_REGULAR
case ('regular')
this % type = MESH_REGULAR
case default
call fatal_error("Invalid mesh type: " // trim(temp_str))
end select
else
this % type = MESH_REGULAR
end if
! Determine number of dimensions for mesh
if (check_for_node(node, "dimension")) then
n = node_word_count(node, "dimension")
if (n /= 1 .and. n /= 2 .and. n /= 3) then
call fatal_error("Mesh must be one, two, or three dimensions.")
end if
this % n_dimension = n
! Allocate attribute arrays
allocate(this % dimension(n))
! Check that dimensions are all greater than zero
call get_node_array(node, "dimension", this % dimension)
if (any(this % dimension <= 0)) then
call fatal_error("All entries on the <dimension> element for a tally &
&mesh must be positive.")
end if
end if
! Check for lower-left coordinates
if (check_for_node(node, "lower_left")) then
n = node_word_count(node, "lower_left")
allocate(this % lower_left(n))
! Read mesh lower-left corner location
call get_node_array(node, "lower_left", this % lower_left)
else
call fatal_error("Must specify <lower_left> on a mesh.")
end if
if (check_for_node(node, "width")) then
! Make sure both upper-right or width were specified
if (check_for_node(node, "upper_right")) then
call fatal_error("Cannot specify both <upper_right> and <width> on a &
&mesh.")
end if
n = node_word_count(node, "width")
allocate(this % width(n))
allocate(this % upper_right(n))
! Check to ensure width has same dimensions
if (n /= size(this % lower_left)) then
call fatal_error("Number of entries on <width> must be the same as &
&the number of entries on <lower_left>.")
end if
! Check for negative widths
call get_node_array(node, "width", this % width)
if (any(this % width < ZERO)) then
call fatal_error("Cannot have a negative <width> on a tally mesh.")
end if
! Set width and upper right coordinate
this % upper_right = this % lower_left + this % dimension * this % width
elseif (check_for_node(node, "upper_right")) then
n = node_word_count(node, "upper_right")
allocate(this % upper_right(n))
allocate(this % width(n))
! Check to ensure width has same dimensions
if (n /= size(this % lower_left)) then
call fatal_error("Number of entries on <upper_right> must be the &
&same as the number of entries on <lower_left>.")
end if
! Check that upper-right is above lower-left
call get_node_array(node, "upper_right", this % upper_right)
if (any(this % upper_right < this % lower_left)) then
call fatal_error("The <upper_right> coordinates must be greater than &
&the <lower_left> coordinates on a tally mesh.")
end if
! Set width and upper right coordinate
this % width = (this % upper_right - this % lower_left) / this % dimension
else
call fatal_error("Must specify either <upper_right> and <width> on a &
&mesh.")
end if
if (allocated(this % dimension)) then
if (size(this % dimension) /= size(this % lower_left)) then
call fatal_error("Number of entries on <lower_left> must be the same &
&as the number of entries on <dimension>.")
end if
! Set volume fraction
this % volume_frac = ONE/real(product(this % dimension),8)
end if
end subroutine regular_from_xml
!===============================================================================
! GET_MESH_BIN determines the tally bin for a particle in a structured mesh
!===============================================================================
pure subroutine regular_get_bin(this, xyz, bin)
class(RegularMesh), intent(in) :: this
real(8), intent(in) :: xyz(:) ! coordinates
integer, intent(out) :: bin ! tally bin
integer :: n ! size of mesh
integer :: d ! mesh dimension index
integer :: ijk(3) ! indices in mesh
logical :: in_mesh ! was given coordinate in mesh at all?
! Get number of dimensions
n = this % n_dimension
! Loop over the dimensions of the mesh
do d = 1, n
! Check for cases where particle is outside of mesh
if (xyz(d) < this % lower_left(d)) then
bin = NO_BIN_FOUND
return
elseif (xyz(d) > this % upper_right(d)) then
bin = NO_BIN_FOUND
return
end if
end do
! Determine indices
call this % get_indices(xyz, ijk, in_mesh)
! Convert indices to bin
if (in_mesh) then
bin = this % get_bin_from_indices(ijk)
else
bin = NO_BIN_FOUND
end if
end subroutine regular_get_bin
!===============================================================================
! GET_MESH_INDICES determines the indices of a particle in a structured mesh
!===============================================================================
pure subroutine regular_get_indices(this, xyz, ijk, in_mesh)
class(RegularMesh), intent(in) :: this
real(8), intent(in) :: xyz(:) ! coordinates to check
integer, intent(out) :: ijk(:) ! indices in mesh
logical, intent(out) :: in_mesh ! were given coords in mesh?
! Find particle in mesh
ijk(:this % n_dimension) = ceiling((xyz(:this % n_dimension) - &
this % lower_left)/this % width)
! Determine if particle is in mesh
if (any(ijk(:this % n_dimension) < 1) .or. &
any(ijk(:this % n_dimension) > this % dimension)) then
in_mesh = .false.
else
in_mesh = .true.
end if
end subroutine regular_get_indices
!===============================================================================
! MESH_INDICES_TO_BIN maps (i), (i,j), or (i,j,k) indices to a single bin number
! for use in a TallyObject results array
!===============================================================================
pure function regular_get_bin_from_indices(this, ijk) result(bin)
class(RegularMesh), intent(in) :: this
integer, intent(in) :: ijk(:)
integer :: bin
if (this % n_dimension == 1) then
bin = ijk(1)
elseif (this % n_dimension == 2) then
bin = (ijk(2) - 1) * this % dimension(1) + ijk(1)
elseif (this % n_dimension == 3) then
bin = ((ijk(3) - 1) * this % dimension(2) + (ijk(2) - 1)) &
* this % dimension(1) + ijk(1)
end if
end function regular_get_bin_from_indices
!===============================================================================
! BIN_TO_MESH_INDICES maps a single mesh bin from a TallyObject results array to
! (i), (i,j), or (i,j,k) indices
!===============================================================================
pure subroutine regular_get_indices_from_bin(this, bin, ijk)
class(RegularMesh), intent(in) :: this
integer, intent(in) :: bin
integer, intent(out) :: ijk(:)
if (this % n_dimension == 1) then
ijk(1) = bin
else if (this % n_dimension == 2) then
ijk(1) = mod(bin - 1, this % dimension(1)) + 1
ijk(2) = (bin - 1)/this % dimension(1) + 1
else if (this % n_dimension == 3) then
ijk(1) = mod(bin - 1, this % dimension(1)) + 1
ijk(2) = mod(bin - 1, this % dimension(1) * this % dimension(2)) &
/ this % dimension(1) + 1
ijk(3) = (bin - 1)/(this % dimension(1) * this % dimension(2)) + 1
end if
end subroutine regular_get_indices_from_bin
!===============================================================================
! MESH_INTERSECTS determines if a line between xyz0 and xyz1 intersects the
! outer boundary of the given mesh. This is important for determining whether a
! track will score to a mesh tally.
!===============================================================================
pure function regular_intersects(this, xyz0, xyz1) result(intersects)
class(RegularMesh), intent(in) :: this
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
select case(this % n_dimension)
case (1)
intersects = mesh_intersects_1d(this, xyz0, xyz1)
case (2)
intersects = mesh_intersects_2d(this, xyz0, xyz1)
case (3)
intersects = mesh_intersects_3d(this, xyz0, xyz1)
end select
end function regular_intersects
pure function mesh_intersects_1d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0 ! track start point
real(8) :: x1 ! track end point
real(8) :: xm0 ! lower-left coordinates of mesh
real(8) :: xm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
! Copy coordinates of ending point
x1 = xyz1(1)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
intersects = .true.
return
end if
! Check if line intersects right surface
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
intersects = .true.
return
end if
end function mesh_intersects_1d
pure function mesh_intersects_2d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0, y0 ! track start point
real(8) :: x1, y1 ! track end point
real(8) :: xi, yi ! track intersection point with mesh
real(8) :: xm0, ym0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! y
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! x
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection
! point y
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! x
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_2d
pure function mesh_intersects_3d(m, xyz0, xyz1) result(intersects)
type(RegularMesh), intent(in) :: m
real(8), intent(in) :: xyz0(:)
real(8), intent(in) :: xyz1(:)
logical :: intersects
real(8) :: x0, y0, z0 ! track start point
real(8) :: x1, y1, z1 ! track end point
real(8) :: xi, yi, zi ! track intersection point with mesh
real(8) :: xm0, ym0, zm0 ! lower-left coordinates of mesh
real(8) :: xm1, ym1, zm1 ! upper-right coordinates of mesh
! Copy coordinates of starting point
x0 = xyz0(1)
y0 = xyz0(2)
z0 = xyz0(3)
! Copy coordinates of ending point
x1 = xyz1(1)
y1 = xyz1(2)
z1 = xyz1(3)
! Copy coordinates of mesh lower_left
xm0 = m % lower_left(1)
ym0 = m % lower_left(2)
zm0 = m % lower_left(3)
! Copy coordinates of mesh upper_right
xm1 = m % upper_right(1)
ym1 = m % upper_right(2)
zm1 = m % upper_right(3)
! Set default value for intersects
intersects = .false.
! Check if line intersects left surface -- calculate the intersection point
! (y,z)
if ((x0 < xm0 .and. x1 > xm0) .or. (x0 > xm0 .and. x1 < xm0)) then
yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects back surface -- calculate the intersection point
! (x,z)
if ((y0 < ym0 .and. y1 > ym0) .or. (y0 > ym0 .and. y1 < ym0)) then
xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects bottom surface -- calculate the intersection
! point (x,y)
if ((z0 < zm0 .and. z1 > zm0) .or. (z0 > zm0 .and. z1 < zm0)) then
xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
! Check if line intersects right surface -- calculate the intersection point
! (y,z)
if ((x0 < xm1 .and. x1 > xm1) .or. (x0 > xm1 .and. x1 < xm1)) then
yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0)
zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0)
if (yi >= ym0 .and. yi < ym1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects front surface -- calculate the intersection point
! (x,z)
if ((y0 < ym1 .and. y1 > ym1) .or. (y0 > ym1 .and. y1 < ym1)) then
xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0)
zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0)
if (xi >= xm0 .and. xi < xm1 .and. zi >= zm0 .and. zi < zm1) then
intersects = .true.
return
end if
end if
! Check if line intersects top surface -- calculate the intersection point
! (x,y)
if ((z0 < zm1 .and. z1 > zm1) .or. (z0 > zm1 .and. z1 < zm1)) then
xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0)
yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0)
if (xi >= xm0 .and. xi < xm1 .and. yi >= ym0 .and. yi < ym1) then
intersects = .true.
return
end if
end if
end function mesh_intersects_3d
!===============================================================================
! TO_HDF5 writes the mesh data to an HDF5 group
!===============================================================================
subroutine regular_to_hdf5(this, group)
class(RegularMesh), intent(in) :: this
integer(HID_T), intent(in) :: group
integer(HID_T) :: mesh_group
mesh_group = create_group(group, "mesh " // trim(to_str(this % id)))
call write_dataset(mesh_group, "type", "regular")
call write_dataset(mesh_group, "dimension", this % dimension)
call write_dataset(mesh_group, "lower_left", this % lower_left)
call write_dataset(mesh_group, "upper_right", this % upper_right)
call write_dataset(mesh_group, "width", this % width)
call close_group(mesh_group)
end subroutine regular_to_hdf5
!===============================================================================
! FREE_MEMORY_MESH deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_mesh()
n_meshes = 0
if (allocated(meshes)) deallocate(meshes)
call mesh_dict % clear()
end subroutine free_memory_mesh
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_extend_meshes(n, index_start, index_end) result(err) bind(C)
! Extend the meshes array by n elements
integer(C_INT32_T), value, intent(in) :: n
integer(C_INT32_T), optional, intent(out) :: index_start
integer(C_INT32_T), optional, intent(out) :: index_end
integer(C_INT) :: err
type(RegularMesh), allocatable :: temp(:) ! temporary meshes array
if (n_meshes == 0) then
! Allocate meshes array
allocate(meshes(n))
else
! Allocate meshes array with increased size
allocate(temp(n_meshes + n))
! Copy original meshes to temporary array
temp(1:n_meshes) = meshes
! Move allocation from temporary array
call move_alloc(FROM=temp, TO=meshes)
end if
! Return indices in meshes array
if (present(index_start)) index_start = n_meshes + 1
if (present(index_end)) index_end = n_meshes + n
n_meshes = n_meshes + n
err = 0
end function openmc_extend_meshes
end module mesh_header

View file

@ -16,7 +16,6 @@ module message_passing
integer :: rank = 0 ! rank of process
logical :: master = .true. ! master process?
logical :: mpi_enabled = .false. ! is MPI in use and initialized?
integer :: mpi_err ! MPI error code
#ifdef MPIF08
type(MPI_Datatype) :: MPI_BANK ! MPI datatype for fission bank
type(MPI_Comm) :: mpi_intracomm ! MPI intra-communicator

View file

@ -2,14 +2,16 @@ module mgxs_data
use constants
use algorithm, only: find
use dict_header, only: DictCharInt
use error, only: fatal_error
use geometry_header, only: get_temperatures
use global
use geometry_header, only: get_temperatures, cells
use hdf5_interface
use material_header, only: Material
use material_header, only: Material, materials, n_materials
use mgxs_header
use nuclide_header, only: n_nuclides
use output, only: write_message
use set_header, only: SetChar
use settings
use stl_vector, only: VectorReal
use string, only: to_lower
implicit none
@ -24,7 +26,6 @@ contains
subroutine read_mgxs()
integer :: i ! index in materials array
integer :: j ! index over nuclides in material
integer :: i_xsdata ! index in <xsdata> list
integer :: i_nuclide ! index in nuclides array
character(20) :: name ! name of library to load
integer :: representation ! Data representation
@ -34,7 +35,6 @@ contains
integer(HID_T) :: file_id
integer(HID_T) :: xsdata_group
logical :: file_exists
type(DictCharInt) :: xsdata_dict
type(VectorReal), allocatable :: temps(:)
character(MAX_WORD_LEN) :: word
integer, allocatable :: array(:)
@ -51,8 +51,7 @@ contains
call write_message("Loading cross section data...", 5)
! Get temperatures
call get_temperatures(cells, materials, material_dict, nuclide_dict, &
n_nuclides_total, temps)
call get_temperatures(temps)
! Open file for reading
file_id = file_open(path_cross_sections, 'r', parallel=.true.)
@ -72,7 +71,7 @@ contains
end if
! allocate arrays for MGXS storage and cross section cache
allocate(nuclides_MG(n_nuclides_total))
allocate(nuclides_MG(n_nuclides))
! ==========================================================================
! READ ALL MGXS CROSS SECTION TABLES
@ -85,7 +84,6 @@ contains
name = mat % names(j)
if (.not. already_read % contains(name)) then
i_xsdata = xsdata_dict % get_key(to_lower(name))
i_nuclide = mat % nuclide(j)
call write_message("Loading " // trim(name) // " data...", 6)
@ -234,7 +232,7 @@ contains
kT = cells(i) % sqrtkT(1)**2
end if
i_material = material_dict % get_key(cells(i) % material(j))
i_material = cells(i) % material(j)
! Add temperature if it hasn't already been added
if (find(kTs(i_material), kT) == -1) then

View file

@ -209,7 +209,25 @@ module mgxs_header
procedure :: calculate_xs => mgxsang_calculate_xs
end type MgxsAngle
contains
! Cross section arrays
type(MgxsContainer), allocatable, target :: nuclides_MG(:)
! Cross section caches
type(MgxsContainer), target, allocatable :: macro_xs(:)
! Number of energy groups
integer :: num_energy_groups
! Number of delayed groups
integer :: num_delayed_groups
! Energy group structure
real(8), allocatable :: energy_bins(:)
! Midpoint of the energy group structure
real(8), allocatable :: energy_bin_avg(:)
contains
!===============================================================================
! MGXS*_FROM_HDF5 reads in the data from the HDF5 Library. At the point of entry
@ -3533,4 +3551,15 @@ module mgxs_header
end subroutine find_angle
!===============================================================================
! FREE_MEMORY_MGXS deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_mgxs()
if (allocated(nuclides_MG)) deallocate(nuclides_MG)
if (allocated(macro_xs)) deallocate(macro_xs)
if (allocated(energy_bins)) deallocate(energy_bins)
if (allocated(energy_bin_avg)) deallocate(energy_bin_avg)
end subroutine free_memory_mgxs
end module mgxs_header

View file

@ -3,10 +3,11 @@ module multipole
use hdf5
use constants
use global
use error, only: fatal_error
use hdf5_interface
use multipole_header, only: MultipoleArray, FIT_T, FIT_A, FIT_F, &
MP_FISS, FORM_MLBW, FORM_RM
use nuclide_header, only: nuclides
implicit none

View file

@ -7,19 +7,19 @@ module nuclide_header
use algorithm, only: sort, find
use constants
use dict_header, only: DictIntInt
use dict_header, only: DictIntInt, DictCharInt
use endf, only: reaction_name, is_fission, is_disappearance
use endf_header, only: Function1D, Polynomial, Tabulated1D
use error, only: fatal_error, warning
use hdf5_interface, only: read_attribute, open_group, close_group, &
open_dataset, read_dataset, close_dataset, get_shape, get_datasets, &
object_exists, get_name, get_groups
use error
use hdf5_interface
use list_header, only: ListInt
use math, only: evaluate_legendre
use message_passing
use multipole_header, only: MultipoleArray
use product_header, only: AngleEnergyContainer
use reaction_header, only: Reaction
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
use settings
use stl_vector, only: VectorInt, VectorReal
use string
use urr_header, only: UrrData
@ -96,6 +96,7 @@ module nuclide_header
class(Function1D), allocatable :: fission_q_recov ! fragments, neutrons, gammas, betas
contains
procedure :: assign_0K_elastic_scattering
procedure :: clear => nuclide_clear
procedure :: from_hdf5 => nuclide_from_hdf5
procedure :: init_grid => nuclide_init_grid
@ -166,8 +167,80 @@ module nuclide_header
character(MAX_FILE_LEN) :: path
end type Library
! Cross section libraries
type(Library), allocatable :: libraries(:)
type(DictCharInt) :: library_dict
! Nuclear data for each nuclide
type(Nuclide), allocatable, target :: nuclides(:)
integer(C_INT), bind(C) :: n_nuclides
type(DictCharInt) :: nuclide_dict
! Cross section caches
type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide
type(MaterialMacroXS) :: material_xs ! Cache for current material
!$omp threadprivate(micro_xs, material_xs)
! Minimum/maximum energies
real(8) :: energy_min_neutron = ZERO
real(8) :: energy_max_neutron = INFINITY
contains
!===============================================================================
! ASSIGN_0K_ELASTIC_SCATTERING
!===============================================================================
subroutine assign_0K_elastic_scattering(this)
class(Nuclide), intent(inout) :: this
integer :: i
real(8) :: xs_cdf_sum
this % resonant = .false.
if (allocated(res_scat_nuclides)) then
! If resonant nuclides were specified, check the list explicitly
do i = 1, size(res_scat_nuclides)
if (this % name == res_scat_nuclides(i)) then
this % resonant = .true.
! Make sure nuclide has 0K data
if (.not. allocated(this % energy_0K)) then
call fatal_error("Cannot treat " // trim(this % name) // " as a &
&resonant scatterer because 0 K elastic scattering data is &
&not present.")
end if
exit
end if
end do
else
! Otherwise, assume that any that have 0 K elastic scattering data are
! resonant
this % resonant = allocated(this % energy_0K)
end if
if (this % resonant) then
! Build CDF for 0K elastic scattering
xs_cdf_sum = ZERO
allocate(this % xs_cdf(0:size(this % energy_0K)))
this % xs_cdf(0) = ZERO
associate (E => this % energy_0K, xs => this % elastic_0K)
do i = 1, size(E) - 1
! Negative cross sections result in a CDF that is not monotonically
! increasing. Set all negative xs values to zero.
if (xs(i) < ZERO) xs(i) = ZERO
! build xs cdf
xs_cdf_sum = xs_cdf_sum + (sqrt(E(i))*xs(i) + sqrt(E(i+1))*xs(i+1))&
/ TWO * (E(i+1) - E(i))
this % xs_cdf(i) = xs_cdf_sum
end do
end associate
end if
end subroutine assign_0K_elastic_scattering
!===============================================================================
! NUCLIDE_CLEAR resets and deallocates data in Nuclide
!===============================================================================
@ -521,7 +594,7 @@ contains
do i = 1, size(this % reactions)
call MTs % push_back(this % reactions(i) % MT)
call this % reaction_index % add_key(this % reactions(i) % MT, i)
call this % reaction_index % set(this % reactions(i) % MT, i)
associate (rx => this % reactions(i))
! Skip total inelastic level scattering, gas production cross sections
@ -745,4 +818,169 @@ contains
end subroutine nuclide_init_grid
!===============================================================================
! CHECK_DATA_VERSION checks for the right version of nuclear data within HDF5
! files
!===============================================================================
subroutine check_data_version(file_id)
integer(HID_T), intent(in) :: file_id
integer, allocatable :: version(:)
if (attribute_exists(file_id, 'version')) then
call read_attribute(version, file_id, 'version')
if (version(1) /= HDF5_VERSION(1)) then
call fatal_error("HDF5 data format uses version " // trim(to_str(&
version(1))) // "." // trim(to_str(version(2))) // " whereas &
&your installation of OpenMC expects version " // trim(to_str(&
HDF5_VERSION(1))) // ".x data.")
end if
else
call fatal_error("HDF5 data does not indicate a version. Your &
&installation of OpenMC expects version " // trim(to_str(&
HDF5_VERSION(1))) // ".x data.")
end if
end subroutine check_data_version
!===============================================================================
! FREE_MEMORY_NUCLIDE deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_nuclide()
integer :: i
! Deallocate cross section data, listings, and cache
if (allocated(nuclides)) then
! First call the clear routines
do i = 1, size(nuclides)
call nuclides(i) % clear()
end do
deallocate(nuclides)
end if
n_nuclides = 0
if (allocated(libraries)) deallocate(libraries)
call nuclide_dict % clear()
call library_dict % clear()
end subroutine free_memory_nuclide
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_get_nuclide_index(name, index) result(err) bind(C)
! Return the index in the nuclides array of a nuclide with a given name
character(kind=C_CHAR), intent(in) :: name(*)
integer(C_INT), intent(out) :: index
integer(C_INT) :: err
character(:), allocatable :: name_
! Copy array of C_CHARs to normal Fortran string
name_ = to_f_string(name)
if (allocated(nuclides)) then
if (nuclide_dict % has(to_lower(name_))) then
index = nuclide_dict % get(to_lower(name_))
err = 0
else
err = E_DATA
call set_errmsg("No nuclide named '" // trim(name_) // &
"' has been loaded.")
end if
else
err = E_ALLOCATE
call set_errmsg("Memory for nuclides has not been allocated.")
end if
end function openmc_get_nuclide_index
function openmc_load_nuclide(name) result(err) bind(C)
! Load a nuclide from the cross section library
character(kind=C_CHAR), intent(in) :: name(*)
integer(C_INT) :: err
integer :: i_library
integer :: n
integer(HID_T) :: file_id
integer(HID_T) :: group_id
character(:), allocatable :: name_
real(8) :: minmax(2) = [ZERO, INFINITY]
type(VectorReal) :: temperature
type(Nuclide), allocatable :: new_nuclides(:)
! Copy array of C_CHARs to normal Fortran string
name_ = to_f_string(name)
err = 0
if (.not. nuclide_dict % has(to_lower(name_))) then
if (library_dict % has(to_lower(name_))) then
! allocate extra space in nuclides array
n = n_nuclides
allocate(new_nuclides(n + 1))
new_nuclides(1:n) = nuclides(:)
call move_alloc(FROM=new_nuclides, TO=nuclides)
n = n + 1
i_library = library_dict % get(to_lower(name_))
! Open file and make sure version is sufficient
file_id = file_open(libraries(i_library) % path, 'r')
call check_data_version(file_id)
! Read nuclide data from HDF5
group_id = open_group(file_id, name_)
call nuclides(n) % from_hdf5(group_id, temperature, &
temperature_method, temperature_tolerance, minmax, &
master)
call close_group(group_id)
call file_close(file_id)
! Add entry to nuclide dictionary
call nuclide_dict % set(to_lower(name_), n)
n_nuclides = n
! Assign resonant scattering data
if (res_scat_on) call nuclides(n) % assign_0K_elastic_scattering()
! Initialize nuclide grid
call nuclides(n) % init_grid(energy_min_neutron, &
energy_max_neutron, n_log_bins)
else
err = E_DATA
call set_errmsg("Nuclide '" // trim(name_) // "' is not present &
&in library.")
end if
end if
end function openmc_load_nuclide
function openmc_nuclide_name(index, name) result(err) bind(C)
! Return the name of a nuclide with a given index
integer(C_INT), value, intent(in) :: index
type(c_ptr), intent(out) :: name
integer(C_INT) :: err
character(C_CHAR), pointer :: name_
err = E_UNASSIGNED
if (allocated(nuclides)) then
if (index >= 1 .and. index <= size(nuclides)) then
name_ => nuclides(index) % name(1:1)
name = C_LOC(name_)
err = 0
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in nuclides array is out of bounds.")
end if
else
err = E_ALLOCATE
call set_errmsg("Memory for nuclides has not been allocated yet.")
end if
end function openmc_nuclide_name
end module nuclide_header

View file

@ -1,24 +1,32 @@
module output
use, intrinsic :: ISO_C_BINDING
use, intrinsic :: ISO_FORTRAN_ENV
use cmfd_header
use constants
use eigenvalue, only: openmc_get_keff
use endf, only: reaction_name
use error, only: fatal_error, warning
use geometry_header, only: Cell, Universe, Lattice, RectLattice, &
HexLattice
use global
use geometry_header
use math, only: t_percentile
use mesh_header, only: RegularMesh
use mesh, only: mesh_indices_to_bin, bin_to_mesh_indices
use mesh_header, only: RegularMesh, meshes
use message_passing, only: master, n_procs
use mgxs_header, only: nuclides_MG
use nuclide_header
use particle_header, only: LocalCoord, Particle
use plot_header
use sab_header, only: SAlphaBeta
use settings
use simulation_header
use surface_header, only: surfaces
use string, only: to_upper, to_str
use tally_header, only: TallyObject
use tally_header
use tally_derivative_header
use tally_filter
use tally_filter_mesh, only: MeshFilter
use tally_filter_header, only: TallyFilterMatch
use timer_header
implicit none
@ -529,6 +537,7 @@ contains
subroutine print_runtime()
integer :: n_active
real(8) :: speed_inactive ! # of neutrons/second in inactive batches
real(8) :: speed_active ! # of neutrons/second in active batches
character(15) :: string
@ -561,6 +570,7 @@ contains
write(ou,100) "Total time elapsed", time_total % elapsed
! Calculate particle rate in active/inactive batches
n_active = n_batches - n_inactive
if (restart_run) then
if (restart_batch < n_inactive) then
speed_inactive = real(n_particles * (n_inactive - restart_batch) * &
@ -608,6 +618,8 @@ contains
real(8) :: t_n1 ! t-value with N-1 degrees of freedom
real(8) :: t_n3 ! t-value with N-3 degrees of freedom
real(8) :: x(2) ! mean and standard deviation
real(C_DOUBLE) :: k_combined(2)
integer(C_INT) :: err
! display header block for results
call header("Results", 4)
@ -634,10 +646,13 @@ contains
write(ou,102) "k-effective (Track-length)", x(1), t_n1 * x(2)
x(:) = mean_stdev(r(:, K_ABSORPTION), n)
write(ou,102) "k-effective (Absorption)", x(1), t_n1 * x(2)
if (n > 3) write(ou,102) "Combined k-effective", k_combined(1), &
t_n3 * k_combined(2)
if (n > 3) then
err = openmc_get_keff(k_combined)
write(ou,102) "Combined k-effective", k_combined(1), &
t_n3 * k_combined(2)
end if
end if
x(:) = mean_stdev(global_tallies(:, LEAKAGE), n)
x(:) = mean_stdev(r(:, LEAKAGE), n)
write(ou,102) "Leakage Fraction", x(1), t_n1 * x(2)
end associate
else
@ -722,11 +737,13 @@ contains
character(36) :: score_names(N_SCORE_TYPES) ! names of scoring function
character(36) :: score_name ! names of scoring function
! to be applied at write-time
type(TallyObject), pointer :: t
type(TallyFilterMatch), allocatable :: matches(:)
! Skip if there are no tallies
if (n_tallies == 0) return
allocate(matches(n_filters))
! Initialize names for scores
score_names(abs(SCORE_FLUX)) = "Flux"
score_names(abs(SCORE_TOTAL)) = "Total Reaction Rate"
@ -751,6 +768,7 @@ contains
score_names(abs(SCORE_INVERSE_VELOCITY)) = "Flux-Weighted Inverse Velocity"
score_names(abs(SCORE_FISS_Q_PROMPT)) = "Prompt fission power"
score_names(abs(SCORE_FISS_Q_RECOV)) = "Recoverable fission power"
score_names(abs(SCORE_CURRENT)) = "Current"
! Create filename for tally output
filename = trim(path_output) // "tallies.out"
@ -767,7 +785,7 @@ contains
end if
TALLY_LOOP: do i = 1, n_tallies
t => tallies(i)
associate (t => tallies(i) % obj)
nr = t % n_realizations
if (confidence_intervals) then
@ -809,7 +827,7 @@ contains
end if
! Handle surface current tallies separately
if (t % type == TALLY_SURFACE_CURRENT) then
if (t % type == TALLY_MESH_CURRENT) then
call write_surface_current(t, unit_tally)
cycle
end if
@ -822,8 +840,8 @@ contains
! Initialize bins, filter level, and indentation
do h = 1, size(t % filter)
call filter_matches(t % filter(h)) % bins % clear()
call filter_matches(t % filter(h)) % bins % push_back(0)
call matches(t % filter(h)) % bins % clear()
call matches(t % filter(h)) % bins % push_back(0)
end do
j = 1
indent = 0
@ -834,18 +852,18 @@ contains
if (size(t % filter) == 0) exit find_bin
! Increment bin combination
filter_matches(t % filter(j)) % bins % data(1) = &
filter_matches(t % filter(j)) % bins % data(1) + 1
matches(t % filter(j)) % bins % data(1) = &
matches(t % filter(j)) % bins % data(1) + 1
! =================================================================
! REACHED END OF BINS FOR THIS FILTER, MOVE TO NEXT FILTER
if (filter_matches(t % filter(j)) % bins % data(1) > &
if (matches(t % filter(j)) % bins % data(1) > &
filters(t % filter(j)) % obj % n_bins) then
! If this is the first filter, then exit
if (j == 1) exit print_bin
filter_matches(t % filter(j)) % bins % data(1) = 0
matches(t % filter(j)) % bins % data(1) = 0
j = j - 1
indent = indent - 2
@ -859,7 +877,7 @@ contains
! Print current filter information
write(UNIT=unit_tally, FMT='(1X,2A)') repeat(" ", indent), &
trim(filters(t % filter(j)) % obj % &
text_label(filter_matches(t % filter(j)) % bins % data(1)))
text_label(matches(t % filter(j)) % bins % data(1)))
indent = indent + 2
j = j + 1
end if
@ -870,7 +888,7 @@ contains
if (size(t % filter) > 0) then
write(UNIT=unit_tally, FMT='(1X,2A)') repeat(" ", indent), &
trim(filters(t % filter(j)) % obj % &
text_label(filter_matches(t % filter(j)) % bins % data(1)))
text_label(matches(t % filter(j)) % bins % data(1)))
end if
! Determine scoring index for this bin combination -- note that unlike
@ -879,7 +897,7 @@ contains
filter_index = 1
do h = 1, size(t % filter)
filter_index = filter_index + (max(filter_matches(t % filter(h)) &
filter_index = filter_index + (max(matches(t % filter(h)) &
% bins % data(1),1) - 1) * t % stride(h)
end do
@ -968,6 +986,7 @@ contains
end do print_bin
end associate
end do TALLY_LOOP
close(UNIT=unit_tally)
@ -1001,6 +1020,9 @@ contains
logical :: energy_filters ! energy filters present
character(MAX_LINE_LEN) :: string
type(RegularMesh), pointer :: m
type(TallyFilterMatch), allocatable :: matches(:)
allocate(matches(n_filters))
nr = t % n_realizations
@ -1017,8 +1039,8 @@ contains
! initialize bins array
do j = 1, size(t % filter)
call filter_matches(t % filter(j)) % bins % clear()
call filter_matches(t % filter(j)) % bins % push_back(1)
call matches(t % filter(j)) % bins % clear()
call matches(t % filter(j)) % bins % push_back(1)
end do
! determine how many energy in bins there are
@ -1040,8 +1062,8 @@ contains
do i = 1, n_cells
! Get the indices for this cell
call bin_to_mesh_indices(m, i, ijk)
filter_matches(i_filter_mesh) % bins % data(1) = i
call m % get_indices_from_bin(i, ijk)
matches(i_filter_mesh) % bins % data(1) = i
! Write the header for this cell
if (n_dim == 1) then
@ -1059,18 +1081,18 @@ contains
do l = 1, n
if (print_ebin) then
! Set incoming energy bin
filter_matches(i_filter_ein) % bins % data(1) = l
matches(i_filter_ein) % bins % data(1) = l
! Write incoming energy bin
write(UNIT=unit_tally, FMT='(3X,A)') &
trim(filters(i_filter_ein) % obj % text_label( &
filter_matches(i_filter_ein) % bins % data(1)))
matches(i_filter_ein) % bins % data(1)))
end if
filter_index = 1
do j = 1, size(t % filter)
if (t % filter(j) == i_filter_surf) cycle
filter_index = filter_index + (filter_matches(t % filter(j)) &
filter_index = filter_index + (matches(t % filter(j)) &
% bins % data(1) - 1) * t % stride(j)
end do

View file

@ -15,6 +15,7 @@ module particle_header
!===============================================================================
type LocalCoord
! Indices in various arrays for this level
integer :: cell = NONE
integer :: universe = NONE
@ -48,6 +49,10 @@ module particle_header
integer :: cell_instance ! offset for distributed properties
type(LocalCoord) :: coord(MAX_COORD) ! coordinates for all levels
! Particle coordinates before crossing a surface
integer :: last_n_coord ! number of current coordinates
integer :: last_cell(MAX_COORD) ! coordinates for all levels
! Energy Data
real(8) :: E ! post-collision energy
real(8) :: last_E ! pre-collision energy
@ -142,11 +147,12 @@ contains
this % fission = .false.
this % delayed_group = 0
this % n_delayed_bank(:) = 0
this % g = 1
this % g = NONE
! Set up base level coordinates
this % coord(1) % universe = root_universe
this % n_coord = 1
this % last_n_coord = 1
end subroutine initialize_particle
@ -209,6 +215,7 @@ contains
this % last_uvw = src % uvw
if (run_CE) then
this % E = src % E
this % g = NONE
else
this % g = int(src % E)
this % last_g = int(src % E)

View file

@ -4,11 +4,15 @@ module particle_restart
use bank_header, only: Bank
use constants
use global
use hdf5_interface, only: file_open, file_close, read_dataset
use mgxs_header, only: energy_bin_avg
use nuclide_header, only: micro_xs, n_nuclides
use output, only: write_message, print_particle
use particle_header, only: Particle
use random_lcg, only: set_particle_seed
use settings
use simulation_header
use tally_header, only: n_tallies
use tracking, only: transport
use hdf5, only: HID_T
@ -32,7 +36,7 @@ contains
! Set verbosity high
verbosity = 10
allocate(micro_xs(n_nuclides_total))
allocate(micro_xs(n_nuclides))
! Initialize the particle to be tracked
call p % initialize()

View file

@ -1,9 +1,10 @@
module particle_restart_write
use bank_header, only: Bank
use global
use bank_header, only: Bank, source_bank
use hdf5_interface
use particle_header, only: Particle
use settings
use simulation_header
use string, only: to_str
use hdf5

View file

@ -3,7 +3,7 @@ module photon_header
use hdf5, only: HID_T, HSIZE_T, SIZE_T
use constants, only: ZERO, HALF, SUBSHELLS
use dict_header, only: DictIntInt
use dict_header, only: DictIntInt, DictCharInt
use endf_header, only: Tabulated1D
use hdf5_interface
@ -57,6 +57,11 @@ module photon_header
procedure :: from_hdf5 => photon_from_hdf5
end type PhotonInteraction
type(PhotonInteraction), allocatable :: elements(:) ! Photon cross sections
integer :: n_elements ! Number of photon cross section tables
type(DictCharInt) :: element_dict
!===============================================================================
! ELEMENTMICROXS contains cached microscopic photon cross sections for a
! particular element at the current energy
@ -73,6 +78,9 @@ module photon_header
real(8) :: pair_production ! microscopic pair production xs
end type ElementMicroXS
type(ElementMicroXS), allocatable :: micro_photon_xs(:) ! Cache for each element
!$omp threadprivate(micro_photon_xs)
contains
subroutine photon_from_hdf5(this, group_id)
@ -167,7 +175,7 @@ contains
! Create mapping from designator to index
do j = 1, size(SUBSHELLS)
if (designators(i) == SUBSHELLS(j)) then
call this % shell_dict % add_key(j, i)
call this % shell_dict % set(j, i)
this % shells(i) % index_subshell = j
exit
end if

View file

@ -352,7 +352,7 @@ contains
call p % create_secondary(uvw, E, ELECTRON, run_ce=.true.)
! Fill hole left by emitted auger electron
i_hole = elm % shell_dict % get_key(secondary)
i_hole = elm % shell_dict % get(secondary)
call atomic_relaxation(p, elm, i_hole)
else
! Radiative transition -- get X-ray energy
@ -363,7 +363,7 @@ contains
end if
! Fill hole created by electron transitioning to the photoelectron hole
i_hole = elm % shell_dict % get_key(primary)
i_hole = elm % shell_dict % get(primary)
call atomic_relaxation(p, elm, i_hole)
end subroutine atomic_relaxation

View file

@ -5,22 +5,26 @@ module physics
use cross_section, only: elastic_xs_0K
use endf, only: reaction_name
use error, only: fatal_error, warning
use global
use material_header, only: Material
use material_header, only: Material, materials
use math
use mesh, only: get_mesh_indices
use mesh_header, only: meshes
use message_passing
use nuclide_header
use output, only: write_message
use particle_header, only: Particle
use particle_restart_write, only: write_particle_restart
use photon_header
use photon_physics, only: rayleigh_scatter, compton_scatter, &
atomic_relaxation
use physics_common
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
use reaction_header, only: Reaction
use sab_header, only: sab_tables
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
use settings
use simulation_header
use string, only: to_str
use tally_header
implicit none
@ -35,11 +39,6 @@ contains
type(Particle), intent(inout) :: p
! Store pre-collision particle properties
p % last_wgt = p % wgt
p % last_E = p % E
p % last_uvw = p % coord(1) % uvw
! Add to collision counter for particle
p % n_collision = p % n_collision + 1
@ -1399,10 +1398,9 @@ contains
integer :: nu_d(MAX_DELAYED_GROUPS) ! number of delayed neutrons born
integer :: i ! loop index
integer :: nu ! actual number of neutrons produced
integer :: ijk(3) ! indices in ufs mesh
integer :: mesh_bin ! mesh bin for source site
real(8) :: nu_t ! total nu
real(8) :: weight ! weight adjustment for ufs method
logical :: in_mesh ! source site in ufs mesh?
type(Nuclide), pointer :: nuc
! Get pointers
@ -1414,18 +1412,20 @@ contains
! the expected number of fission sites produced
if (ufs) then
! Determine indices on ufs mesh for current location
call get_mesh_indices(ufs_mesh, p % coord(1) % xyz, ijk, in_mesh)
if (.not. in_mesh) then
call write_particle_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
associate (m => meshes(index_ufs_mesh))
! Determine indices on ufs mesh for current location
call m % get_bin(p % coord(1) % xyz, mesh_bin)
if (mesh_bin == NO_BIN_FOUND) then
call write_particle_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (source_frac(1,ijk(1),ijk(2),ijk(3)) /= ZERO) then
weight = ufs_mesh % volume_frac / source_frac(1,ijk(1),ijk(2),ijk(3))
else
weight = ONE
end if
if (source_frac(1, mesh_bin) /= ZERO) then
weight = m % volume_frac / source_frac(1, mesh_bin)
else
weight = ONE
end if
end associate
else
weight = ONE
end if

View file

@ -1,9 +1,9 @@
module physics_common
use constants
use global, only: weight_cutoff, weight_survive
use particle_header, only: Particle
use random_lcg, only: prn
use settings, only: weight_cutoff, weight_survive
implicit none

View file

@ -2,21 +2,25 @@ module physics_mg
! This module contains the multi-group specific physics routines so as to not
! hinder performance of the CE versions with multiple if-thens.
use bank_header
use constants
use error, only: fatal_error, warning
use global
use material_header, only: Material
use material_header, only: Material, materials
use math, only: rotate_angle
use mgxs_header, only: Mgxs, MgxsContainer
use mesh, only: get_mesh_indices
use mesh_header, only: meshes
use mgxs_header
use message_passing
use nuclide_header, only: material_xs
use output, only: write_message
use particle_header, only: Particle
use particle_restart_write, only: write_particle_restart
use physics_common
use random_lcg, only: prn
use scattdata_header
use settings
use simulation_header
use string, only: to_str
use tally_header
implicit none
@ -31,12 +35,6 @@ contains
type(Particle), intent(inout) :: p
! Store pre-collision particle properties
p % last_wgt = p % wgt
p % last_g = p % g
p % last_E = p % E
p % last_uvw = p % coord(1) % uvw
! Add to collision counter for particle
p % n_collision = p % n_collision + 1
@ -177,12 +175,11 @@ contains
integer :: dg ! delayed group
integer :: gout ! group out
integer :: nu ! actual number of neutrons produced
integer :: ijk(3) ! indices in ufs mesh
integer :: mesh_bin ! mesh bin for source site
real(8) :: nu_t ! total nu
real(8) :: mu ! fission neutron angular cosine
real(8) :: phi ! fission neutron azimuthal angle
real(8) :: weight ! weight adjustment for ufs method
logical :: in_mesh ! source site in ufs mesh?
class(Mgxs), pointer :: xs
! Get Pointers
@ -194,20 +191,21 @@ contains
! the expected number of fission sites produced
if (ufs) then
associate (m => meshes(index_ufs_mesh))
! Determine indices on ufs mesh for current location
call m % get_bin(p % coord(1) % xyz, mesh_bin)
! Determine indices on ufs mesh for current location
call get_mesh_indices(ufs_mesh, p % coord(1) % xyz, ijk, in_mesh)
if (mesh_bin == NO_BIN_FOUND) then
call write_particle_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (.not. in_mesh) then
call write_particle_restart(p)
call fatal_error("Source site outside UFS mesh!")
end if
if (source_frac(1,ijk(1),ijk(2),ijk(3)) /= ZERO) then
weight = ufs_mesh % volume_frac / source_frac(1,ijk(1),ijk(2),ijk(3))
else
weight = ONE
end if
if (source_frac(1, mesh_bin) /= ZERO) then
weight = m % volume_frac / source_frac(1, mesh_bin)
else
weight = ONE
end if
end associate
else
weight = ONE
end if

View file

@ -7,15 +7,14 @@ module plot
use constants
use error, only: fatal_error
use geometry, only: find_cell, check_cell_overlap
use geometry_header, only: Cell, root_universe
use global
use geometry_header, only: Cell, root_universe, cells
use hdf5_interface
use mesh, only: get_mesh_indices
use mesh_header, only: RegularMesh
use output, only: write_message, time_stamp
use material_header, only: materials
use particle_header, only: LocalCoord, Particle
use plot_header
use progress_header, only: ProgressBar
use settings, only: check_overlaps
use string, only: to_str
implicit none
@ -241,8 +240,8 @@ contains
width = xyz_ur_plot - xyz_ll_plot
associate (m => pl % meshlines_mesh)
call get_mesh_indices(m, xyz_ll_plot, ijk_ll(:m % n_dimension), in_mesh)
call get_mesh_indices(m, xyz_ur_plot, ijk_ur(:m % n_dimension), in_mesh)
call m % get_indices(xyz_ll_plot, ijk_ll(:m % n_dimension), in_mesh)
call m % get_indices(xyz_ur_plot, ijk_ur(:m % n_dimension), in_mesh)
! sweep through all meshbins on this plane and draw borders
do i = ijk_ll(outer), ijk_ur(outer)

View file

@ -1,7 +1,10 @@
module plot_header
use, intrinsic :: ISO_C_BINDING
use constants
use mesh_header, only: RegularMesh
use dict_header, only: DictIntInt
use mesh_header, only: RegularMesh
implicit none
@ -50,4 +53,23 @@ module plot_header
integer, parameter :: PLOT_COLOR_CELLS = 1
integer, parameter :: PLOT_COLOR_MATS = 2
integer(C_INT32_T), bind(C) :: n_plots ! # of plots
type(ObjectPlot), allocatable, target :: plots(:)
! Dictionary that maps user IDs to indices in 'plots'
type(DictIntInt) :: plot_dict
contains
!===============================================================================
! FREE_MEMORY_PLOT deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_plot()
n_plots = 0
if (allocated(plots)) deallocate(plots)
call plot_dict % clear()
end subroutine free_memory_plot
end module plot_header

View file

@ -1,5 +1,7 @@
module random_lcg
use, intrinsic :: ISO_C_BINDING
use constants
implicit none
@ -7,28 +9,29 @@ module random_lcg
private
save
! Random number seed
integer(8), public :: seed = 1_8
! Starting seed
integer(C_INT64_T), public, bind(C) :: seed = 1_8
integer(8) :: prn_seed0 ! original seed
integer(8) :: prn_seed(N_STREAMS) ! current seed
integer(8) :: prn_mult ! multiplication factor, g
integer(8) :: prn_add ! additive factor, c
integer :: prn_bits ! number of bits, M
integer(8) :: prn_mod ! 2^M
integer(8) :: prn_mask ! 2^M - 1
integer(8) :: prn_stride ! stride between particles
real(8) :: prn_norm ! 2^(-M)
integer :: stream ! current RNG stream
! LCG parameters
integer(C_INT64_T), parameter :: prn_mult = 2806196910506780709_8 ! multiplication factor, g
integer(C_INT64_T), parameter :: prn_add = 1_8 ! additive factor, c
integer, parameter :: prn_bits = 63 ! number of bits, M
integer(C_INT64_T), parameter :: prn_mod = ibset(0_8, prn_bits) ! 2^M
integer(C_INT64_T), parameter :: prn_mask = not(prn_mod) ! 2^M - 1
integer(C_INT64_T), parameter :: prn_stride = 152917_8 ! stride between particles
real(C_DOUBLE), parameter :: prn_norm = 2._8**(-prn_bits) ! 2^(-M)
! Current PRNG state
integer(C_INT64_T) :: prn_seed(N_STREAMS) ! current seed
integer :: stream ! current RNG stream
!$omp threadprivate(prn_seed, stream)
public :: prn
public :: future_prn
public :: initialize_prng
public :: set_particle_seed
public :: advance_prn_seed
public :: prn_set_stream
public :: openmc_set_seed
contains
@ -38,10 +41,10 @@ contains
function prn() result(pseudo_rn)
real(8) :: pseudo_rn
real(C_DOUBLE) :: pseudo_rn
! This algorithm uses bit-masking to find the next integer(8) value to be
! used to calculate the random number
! This algorithm uses bit-masking to find the next integer(C_INT64_T) value
! to be used to calculate the random number
prn_seed(stream) = iand(prn_mult*prn_seed(stream) + prn_add, prn_mask)
@ -59,40 +62,14 @@ contains
function future_prn(n) result(pseudo_rn)
integer(8), intent(in) :: n ! number of prns to skip
integer(C_INT64_T), intent(in) :: n ! number of prns to skip
real(8) :: pseudo_rn
real(C_DOUBLE) :: pseudo_rn
pseudo_rn = future_seed(n, prn_seed(stream)) * prn_norm
end function future_prn
!===============================================================================
! INITIALIZE_PRNG sets up the random number generator, determining the seed and
! values for g, c, and m.
!===============================================================================
subroutine initialize_prng()
integer :: i
prn_seed0 = seed
!$omp parallel
do i = 1, N_STREAMS
prn_seed(i) = prn_seed0 + i - 1
end do
stream = STREAM_TRACKING
!$omp end parallel
prn_mult = 2806196910506780709_8
prn_add = 1_8
prn_bits = 63
prn_mod = ibset(0_8, prn_bits) ! clever way of calculating 2**bits
prn_mask = prn_mod - 1_8
prn_stride = 152917_8
prn_norm = 2._8**(-prn_bits)
end subroutine initialize_prng
!===============================================================================
! SET_PARTICLE_SEED sets the seed to a unique value based on the ID of the
! particle
@ -100,12 +77,12 @@ contains
subroutine set_particle_seed(id)
integer(8), intent(in) :: id
integer(C_INT64_T), intent(in) :: id
integer :: i
do i = 1, N_STREAMS
prn_seed(i) = future_seed(id*prn_stride, prn_seed0 + i - 1)
prn_seed(i) = future_seed(id*prn_stride, seed + i - 1)
end do
end subroutine set_particle_seed
@ -117,7 +94,7 @@ contains
subroutine advance_prn_seed(n)
integer(8), intent(in) :: n ! number of seeds to skip
integer(C_INT64_T), intent(in) :: n ! number of seeds to skip
prn_seed(stream) = future_seed(n, prn_seed(stream))
@ -132,15 +109,15 @@ contains
function future_seed(n, seed) result(new_seed)
integer(8), intent(in) :: n ! number of seeds to skip
integer(8), intent(in) :: seed ! original seed
integer(8) :: new_seed ! new seed
integer(C_INT64_T), intent(in) :: n ! number of seeds to skip
integer(C_INT64_T), intent(in) :: seed ! original seed
integer(C_INT64_T) :: new_seed ! new seed
integer(8) :: nskip ! positive number of seeds to skip
integer(8) :: g ! original multiplicative constant
integer(8) :: c ! original additive constnat
integer(8) :: g_new ! new effective multiplicative constant
integer(8) :: c_new ! new effective additive constant
integer(C_INT64_T) :: nskip ! positive number of seeds to skip
integer(C_INT64_T) :: g ! original multiplicative constant
integer(C_INT64_T) :: c ! original additive constnat
integer(C_INT64_T) :: g_new ! new effective multiplicative constant
integer(C_INT64_T) :: c_new ! new effective additive constant
! In cases where we want to skip backwards, we add the period of the random
! number generator until the number of PRNs to skip is positive since
@ -198,4 +175,26 @@ contains
end subroutine prn_set_stream
!===============================================================================
! C API FUNCTIONS
!===============================================================================
function openmc_set_seed(new_seed) result(err) bind(C)
! Saves the starting seed and sets up the PRNG thread state
integer(C_INT64_T), value, intent(in) :: new_seed
integer(C_INT) :: err
integer :: i
err = 0
seed = new_seed
!$omp parallel
do i = 1, N_STREAMS
prn_seed(i) = seed + i - 1
end do
stream = STREAM_TRACKING
!$omp end parallel
end function openmc_set_seed
end module random_lcg

View file

@ -12,14 +12,7 @@ element settings {
element energy_mode { ( "continuous-energy" | "ce" | "CE" | "multi-group" | "mg" | "MG" ) }? &
element entropy {
(element dimension { list { xsd:int+ } } |
attribute dimension { list { xsd:int+ } })? &
(element lower_left { list { xsd:double+ } } |
attribute lower_left { list { xsd:double+ } }) &
(element upper_right { list { xsd:double+ } } |
attribute upper_right { list { xsd:double+ } })
}? &
element entropy_mesh { xsd:positiveInteger }? &
element generations_per_batch { xsd:positiveInteger }? &
@ -34,6 +27,22 @@ element settings {
element max_order { xsd:nonNegativeInteger }? &
element mesh {
(element id { xsd:int } | attribute id { xsd:int }) &
(element type { ( "regular" ) } |
attribute type { ( "regular" ) })? &
(element dimension { list { xsd:positiveInteger+ } } |
attribute dimension { list { xsd:positiveInteger+ } }) &
(element lower_left { list { xsd:double+ } } |
attribute lower_left { list { xsd:double+ } }) &
(
(element upper_right { list { xsd:double+ } } |
attribute upper_right { list { xsd:double+ } }) |
(element width { list { xsd:double+ } } |
attribute width { list { xsd:double+ } })
)
}* &
element no_reduce { xsd:boolean }? &
element output {
@ -133,6 +142,8 @@ element settings {
(element batch_interval { xsd:positiveInteger } | attribute batch_interval { xsd:positiveInteger })?
}? &
element ufs_mesh { xsd:positiveInteger }? &
element verbosity { xsd:positiveInteger }? &
element volume_calc {
@ -148,15 +159,6 @@ element settings {
attribute upper_right { list { xsd:double+ } })
}* &
element uniform_fs{
(element dimension { list { xsd:positiveInteger+ } } |
attribute dimension { list { xsd:positiveInteger+ } }) &
(element lower_left { list { xsd:double+ } } |
attribute lower_left { list { xsd:double+ } }) &
(element upper_right { list { xsd:double+ } } |
attribute upper_right { list { xsd:double+ } })
}? &
element resonance_scattering {
(element enable { xsd:boolean } | attribute enable { xsd:boolean })? &
(element method { xsd:string } | attribute method { xsd:string })? &

View file

@ -62,59 +62,8 @@
</element>
</optional>
<optional>
<element name="entropy">
<interleave>
<optional>
<choice>
<element name="dimension">
<list>
<oneOrMore>
<data type="int"/>
</oneOrMore>
</list>
</element>
<attribute name="dimension">
<list>
<oneOrMore>
<data type="int"/>
</oneOrMore>
</list>
</attribute>
</choice>
</optional>
<choice>
<element name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<element name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
</interleave>
<element name="entropy_mesh">
<data type="positiveInteger"/>
</element>
</optional>
<optional>
@ -159,6 +108,96 @@
<data type="nonNegativeInteger"/>
</element>
</optional>
<zeroOrMore>
<element name="mesh">
<interleave>
<choice>
<element name="id">
<data type="int"/>
</element>
<attribute name="id">
<data type="int"/>
</attribute>
</choice>
<optional>
<choice>
<element name="type">
<value>regular</value>
</element>
<attribute name="type">
<value>regular</value>
</attribute>
</choice>
</optional>
<choice>
<element name="dimension">
<list>
<oneOrMore>
<data type="positiveInteger"/>
</oneOrMore>
</list>
</element>
<attribute name="dimension">
<list>
<oneOrMore>
<data type="positiveInteger"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<element name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<choice>
<element name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<element name="width">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="width">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
</choice>
</interleave>
</element>
</zeroOrMore>
<optional>
<element name="no_reduce">
<data type="boolean"/>
@ -595,6 +634,11 @@
</interleave>
</element>
</optional>
<optional>
<element name="ufs_mesh">
<data type="positiveInteger"/>
</element>
</optional>
<optional>
<element name="verbosity">
<data type="positiveInteger"/>
@ -670,60 +714,6 @@
</interleave>
</element>
</zeroOrMore>
<optional>
<element name="uniform_fs">
<interleave>
<choice>
<element name="dimension">
<list>
<oneOrMore>
<data type="positiveInteger"/>
</oneOrMore>
</list>
</element>
<attribute name="dimension">
<list>
<oneOrMore>
<data type="positiveInteger"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<element name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="lower_left">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
<choice>
<element name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</element>
<attribute name="upper_right">
<list>
<oneOrMore>
<data type="double"/>
</oneOrMore>
</list>
</attribute>
</choice>
</interleave>
</element>
</optional>
<optional>
<element name="resonance_scattering">
<interleave>

View file

@ -35,12 +35,14 @@ element tallies {
element filter {
(element id { xsd:int } | attribute id { xsd:int }) &
(
( (element type { ( "cell" | "cellborn" | "material" | "universe" |
"surface" | "distribcell" | "mesh" | "energy" | "energyout" | "mu" |
"polar" | "azimuthal" | "delayedgroup" | "energyfunction") } |
attribute type { ( "cell" | "cellborn" | "material" | "universe" |
"surface" | "distribcell" | "mesh" | "energy" | "energyout" | "mu" |
"polar" | "azimuthal" | "delayedgroup" | "energyfunction") }) &
( (element type { ( "cell" | "cellfrom" | "cellborn" | "material" |
"universe" | "surface" | "distribcell" | "mesh" | "energy" |
"energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" |
"energyfunction") } |
attribute type { ( "cell" | "cellfrom" | "cellborn" | "material" |
"universe" | "surface" | "distribcell" | "mesh" | "energy" |
"energyout" | "mu" | "polar" | "azimuthal" | "delayedgroup" |
"energyfunction") }) &
(element bins { list { xsd:double+ } } |
attribute bins { list { xsd:double+ } })
) |

View file

@ -168,6 +168,7 @@
<element name="type">
<choice>
<value>cell</value>
<value>cellfrom</value>
<value>cellborn</value>
<value>material</value>
<value>universe</value>
@ -186,6 +187,7 @@
<attribute name="type">
<choice>
<value>cell</value>
<value>cellfrom</value>
<value>cellborn</value>
<value>material</value>
<value>universe</value>

View file

@ -1,10 +1,11 @@
module sab_header
use, intrinsic :: ISO_C_BINDING
use, intrinsic :: ISO_FORTRAN_ENV
use algorithm, only: find, sort
use constants
use dict_header, only: DictIntInt
use dict_header, only: DictIntInt, DictCharInt
use distribution_univariate, only: Tabular
use error, only: warning, fatal_error
use hdf5, only: HID_T, HSIZE_T, SIZE_T
@ -78,6 +79,11 @@ module sab_header
procedure :: from_hdf5 => salphabeta_from_hdf5
end type SAlphaBeta
! S(a,b) tables
type(SAlphaBeta), allocatable, target :: sab_tables(:)
integer(C_INT), bind(C) :: n_sab_tables
type(DictCharInt) :: sab_dict
contains
subroutine salphabeta_from_hdf5(this, group_id, temperature, method, &
@ -354,4 +360,14 @@ contains
call close_group(kT_group)
end subroutine salphabeta_from_hdf5
!===============================================================================
! FREE_MEMORY_SAB deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_sab()
n_sab_tables = 0
if (allocated(sab_tables)) deallocate(sab_tables)
call sab_dict % clear()
end subroutine free_memory_sab
end module sab_header

158
src/settings.F90 Normal file
View file

@ -0,0 +1,158 @@
module settings
use, intrinsic :: ISO_C_BINDING
use constants
use set_header, only: SetInt
implicit none
! ============================================================================
! ENERGY TREATMENT RELATED VARIABLES
logical :: run_CE = .true. ! Run in CE mode?
! ============================================================================
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
! Unreoslved resonance probablity tables
logical :: urr_ptables_on = .true.
! Default temperature and method for choosing temperatures
integer :: temperature_method = TEMPERATURE_NEAREST
logical :: temperature_multipole = .false.
real(8) :: temperature_tolerance = 10.0_8
real(8) :: temperature_default = 293.6_8
real(8) :: temperature_range(2) = [ZERO, ZERO]
integer :: n_log_bins ! number of bins for logarithmic grid
logical :: photon_transport = .false.
integer :: electron_treatment = ELECTRON_LED
! ============================================================================
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
! Maximum Data Order
integer :: max_order
! Whether or not to convert Legendres to tabulars
logical :: legendre_to_tabular = .true.
! Number of points to use in the Legendre to tabular conversion
integer :: legendre_to_tabular_points = 33
! Assume all tallies are spatially distinct
logical :: assume_separate = .false.
! Use confidence intervals for results instead of standard deviations
logical :: confidence_intervals = .false.
! ============================================================================
! SIMULATION VARIABLES
integer(C_INT64_T), bind(C) :: n_particles = 0 ! # of particles per generation
integer(C_INT32_T), bind(C) :: n_batches ! # of batches
integer(C_INT32_T), bind(C) :: n_inactive ! # of inactive batches
integer(C_INT32_T), bind(C) :: gen_per_batch = 1 ! # of generations per batch
integer :: n_max_batches ! max # of batches
integer :: n_batch_interval = 1 ! batch interval for triggers
logical :: pred_batches = .false. ! predict batches for triggers
logical :: trigger_on = .false. ! flag for turning triggers on/off
logical :: entropy_on = .false.
integer :: index_entropy_mesh = -1
logical :: ufs = .false.
integer :: index_ufs_mesh = -1
! Write source at end of simulation
logical :: source_separate = .false.
logical :: source_write = .true.
logical :: source_latest = .false.
! Variance reduction settins
logical :: survival_biasing = .false.
real(8) :: weight_cutoff = 0.25_8
real(8) :: energy_cutoff(4) = [ZERO, 1000.0_8, ZERO, ZERO]
real(8) :: weight_survive = ONE
! Mode to run in (fixed source, eigenvalue, plotting, etc)
integer(C_INT), bind(C) :: run_mode = NONE
! Restart run
logical :: restart_run = .false.
! The verbosity controls how much information will be printed to the screen
! and in logs
integer(C_INT), bind(C) :: verbosity = 7
logical :: check_overlaps = .false.
! Trace for single particle
integer :: trace_batch
integer :: trace_gen
integer(8) :: trace_particle
! Particle tracks
logical :: write_all_tracks = .false.
integer, allocatable :: track_identifiers(:,:)
! Particle restart run
logical :: particle_restart_run = .false.
! Write out initial source
logical :: write_initial_source = .false.
! Whether create fission neutrons or not. Only applied for MODE_FIXEDSOURCE
logical :: create_fission_neutrons = .true.
! Information about state points to be written
integer :: n_state_points = 0
type(SetInt) :: statepoint_batch
! Information about source points to be written
integer :: n_source_points = 0
type(SetInt) :: sourcepoint_batch
character(MAX_FILE_LEN) :: path_input ! Path to input file
character(MAX_FILE_LEN) :: path_cross_sections = '' ! Path to cross_sections.xml
character(MAX_FILE_LEN) :: path_multipole ! Path to wmp library
character(MAX_FILE_LEN) :: path_source = '' ! Path to binary source
character(MAX_FILE_LEN) :: path_state_point ! Path to binary state point
character(MAX_FILE_LEN) :: path_source_point ! Path to binary source point
character(MAX_FILE_LEN) :: path_particle_restart ! Path to particle restart
character(MAX_FILE_LEN) :: path_output = '' ! Path to output directory
! Various output options
logical :: output_summary = .true.
logical :: output_tallies = .true.
! Resonance scattering settings
logical :: res_scat_on = .false. ! is resonance scattering treated?
integer :: res_scat_method = RES_SCAT_ARES ! resonance scattering method
real(8) :: res_scat_energy_min = 0.01_8
real(8) :: res_scat_energy_max = 1000.0_8
character(10), allocatable :: res_scat_nuclides(:)
! Is CMFD active
logical :: cmfd_run = .false.
! No reduction at end of batch
logical :: reduce_tallies = .true.
contains
!===============================================================================
! FREE_MEMORY_SETTINGS deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_settings()
if (allocated(res_scat_nuclides)) deallocate(res_scat_nuclides)
if (allocated(track_identifiers)) deallocate(track_identifiers)
call statepoint_batch % clear()
call sourcepoint_batch % clear()
end subroutine free_memory_settings
end module settings

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