Fixed merge conflicts with develop

This commit is contained in:
Will Boyd 2015-07-26 16:09:15 -07:00
commit 4e6aca6028
331 changed files with 6506 additions and 222815 deletions

View file

@ -1,11 +1,21 @@
sudo: false
language: python
python:
- "2.7"
- "3.4"
addons:
apt:
packages:
- gfortran
- g++
cache:
directories:
- $HOME/mpich_install
- $HOME/hdf5_install
- $HOME/phdf5_install
before_install:
# ============== Handle Python third-party packages ==============
- sudo apt-get update
- if [[ "$TRAVIS_PYTHON_VERSION" == "2.7" ]]; then
wget https://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh;
else
@ -20,13 +30,12 @@ before_install:
- conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy scipy h5py
- source activate test-environment
# ============== Install GCC, MPICH, HDF5, PHDF5 ==============
- sudo apt-get install -qq -y gfortran g++
# Install GCC, MPICH, HDF5, PHDF5
- ./tests/travis_install.sh
- export FC=gfortran
- export MPI_DIR=$PWD/mpich_install
- export PHDF5_DIR=$PWD/phdf5_install
- export HDF5_DIR=$PWD/hdf5_install
- export MPI_DIR=$HOME/mpich_install
- export PHDF5_DIR=$HOME/phdf5_install
- export HDF5_DIR=$HOME/hdf5_install
install: true

View file

@ -24,12 +24,17 @@ option(profile "Compile with profiling flags" OFF)
option(debug "Compile with debug flags" OFF)
option(optimize "Turn on all compiler optimization flags" OFF)
option(verbose "Create verbose Makefiles" OFF)
option(coverage "Compile with flags" OFF)
option(coverage "Compile with coverage analysis flags" OFF)
option(mpif08 "Use Fortran 2008 MPI interface" OFF)
if (verbose)
set(CMAKE_VERBOSE_MAKEFILE on)
endif()
# Maximum number of nested coordinates levels
set(maxcoord 10 CACHE STRING "Maximum number of nested coordinate levels")
add_definitions(-DMAX_COORD=${maxcoord})
#===============================================================================
# MPI for distributed-memory parallelism / HDF5 for binary output
#===============================================================================
@ -51,6 +56,12 @@ elseif($ENV{FC} MATCHES "h5pfc$")
set(HDF5_ENABLED TRUE)
endif()
# Check for Fortran 2008 MPI interface
if(MPI_ENABLED AND mpif08)
message("-- Using Fortran 2008 MPI bindings")
add_definitions(-DMPIF08)
endif()
#===============================================================================
# Set compile/link flags based on which compiler is being used
#===============================================================================

View file

@ -17,13 +17,18 @@ import sys, os
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../sphinxext'))
sys.path.insert(0, os.path.abspath('../..'))
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.pngmath', 'sphinxcontrib.tikz', 'sphinx_numfig']
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.pngmath',
'sphinxcontrib.tikz',
'sphinx_numfig']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

View file

@ -18,9 +18,7 @@ from the git repository as such:
.. code-block:: sh
git clone https://bitbucket.org/philexander/tikz.git
cd tikz
sudo python setup.py install
sudo pip install https://bitbucket.org/philexander/tikz/get/HEAD.tar.gz
The Numfig_ package can be installed directly with pip:

View file

@ -125,8 +125,16 @@ program, subroutine, function, if, associate, etc. Emacs users should set the
variables f90-if-indent, f90-do-indent, f90-continuation-indent,
f90-type-indent, f90-associate-indent, and f90-program indent to 2.
Continuation lines should be indented by an extra 5 spaces. This is the default
value of f90-continuation-indent in Emacs.
Continuation lines should be indented by at least 5 spaces. They may be indented
more in order to make the content match the context. For example, either of
these are valid continuation indentations:
.. code-block:: fortran
local_xyz(1) = xyz(1) - (this % lower_left(1) + &
(i_xyz(1) - HALF)*this % pitch(1))
call which_data(scatt_type, get_scatt, get_nuscatt, get_chi_t, get_chi_p, &
get_chi_d, scatt_order)
Whitespace in Expressions
-------------------------

View file

@ -32,6 +32,7 @@ free to send a message to the User's Group `mailing list`_.
methods/index
usersguide/index
devguide/index
pythonapi/index
publications
license
developers

View file

@ -10,9 +10,8 @@ Overviews
- Paul K. Romano, Nicholas E. Horelik, Bryan R. Herman, Adam G. Nelson, Benoit
Forget, and Kord Smith, "OpenMC: A State-of-the-Art Monte Carlo Code for
Research and Development," *Proc. Joint International Conference on
Supercomputing in Nuclear Applications and Monte Carlo*, Paris, France,
Oct. 27--31 (2013). `<http://dx.doi.org/10.1051/snamc/201406016>`_
Research and Development," *Ann. Nucl. Energy*, **82**, 90--97
(2015). `<http://dx.doi.org/10.1016/j.anucene.2014.07.048>`_
- Paul K. Romano, Bryan R. Herman, Nicholas E. Horelik, Benoit Forget, Kord
Smith, and Andrew R. Siegel, "Progress and Status of the OpenMC Monte Carlo

View file

@ -0,0 +1,8 @@
.. _pythonapi_ace:
==========
ACE Format
==========
.. automodule:: openmc.ace
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_cmfd:
====
CMFD
====
.. automodule:: openmc.cmfd
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_element:
=======
Element
=======
.. automodule:: openmc.element
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_executor:
========
Executor
========
.. automodule:: openmc.executor
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_filter:
======
Filter
======
.. automodule:: openmc.filter
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_geometry:
========
Geometry
========
.. automodule:: openmc.geometry
:members:

View file

@ -0,0 +1,32 @@
.. _pythonapi:
==========
Python API
==========
--------
Contents
--------
.. toctree::
:maxdepth: 1
ace
cmfd
element
executor
filter
geometry
material
mesh
nuclide
opencg_compatible
particle_restart
plots
settings
statepoint
summary
surface
tallies
trigger
universe

View file

@ -0,0 +1,8 @@
.. _pythonapi_material:
=========
Materials
=========
.. automodule:: openmc.material
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_mesh:
====
Mesh
====
.. automodule:: openmc.mesh
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_nuclide:
=======
Nuclide
=======
.. automodule:: openmc.nuclide
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_opencg_compatible:
====================
OpenCG Compatibility
====================
.. automodule:: openmc.opencg_compatible
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_particle_restart:
================
Particle Restart
================
.. automodule:: openmc.particle_restart
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_plots:
=====
Plots
=====
.. automodule:: openmc.plots
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_settings:
========
Settings
========
.. automodule:: openmc.settings
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_statepoint:
==========
Statepoint
==========
.. automodule:: openmc.statepoint
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_summary:
=======
Summary
=======
.. automodule:: openmc.summary
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_surface:
=======
Surface
=======
.. automodule:: openmc.surface
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_tallies:
=======
Tallies
=======
.. automodule:: openmc.tallies
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_trigger:
=======
Trigger
=======
.. automodule:: openmc.trigger
:members:

View file

@ -0,0 +1,8 @@
.. _pythonapi_universe:
========
Universe
========
.. automodule:: openmc.universe
:members:

View file

@ -441,24 +441,22 @@ attributes/sub-elements:
has the following attributes:
:type:
The type of spatial distribution. Valid options are "box" and "point". A
"box" spatial distribution has coordinates sampled uniformly in a
parallelepiped. A "point" spatial distribution has coordinates specified
by a triplet.
The type of spatial distribution. Valid options are "box", "fission", and
"point". A "box" spatial distribution has coordinates sampled uniformly in
a parallelepiped. A "fission" spatial distribution samples locations from
a "box" distribution but only locations in fissionable materials are
accepted. A "point" spatial distribution has coordinates specified by a
triplet.
*Default*: None
:parameters:
For a "box" spatial distribution, ``parameters`` should be given as six
real numbers, the first three of which specify the lower-left corner of a
parallelepiped and the last three of which specify the upper-right
corner. Source sites are sampled uniformly through that parallelepiped.
To filter a "box" spatial distribution by fissionable material, specify
"fission" tag instead of "box". The ``parameters`` should be given as six
real numbers, the first three of which specify the lower-left corner of a
parallelepiped and the last three of which specify the upper-right
corner. Source sites are sampled uniformly through that parallelepiped.
For a "box" or "fission" spatial distribution, ``parameters`` should be
given as six real numbers, the first three of which specify the lower-left
corner of a parallelepiped and the last three of which specify the
upper-right corner. Source sites are sampled uniformly through that
parallelepiped.
For a "point" spatial distribution, ``parameters`` should be given as
three real numbers which specify the (x,y,z) location of an isotropic
@ -481,7 +479,7 @@ attributes/sub-elements:
:parameters:
For an "isotropic" angular distribution, ``parameters`` should not be
specified
specified.
For a "monodirectional" angular distribution, ``parameters`` should be
given as three real numbers which specify the angular cosines with respect
@ -499,7 +497,7 @@ attributes/sub-elements:
"watt", and "maxwell". The "monoenergetic" option produces source sites at
a single energy. The "watt" option produces source sites whose energy is
sampled from a Watt fission spectrum. The "maxwell" option produce source
sites whose energy is sampled from a Maxwell fission spectrum
sites whose energy is sampled from a Maxwell fission spectrum.
*Default*: watt
@ -605,8 +603,6 @@ survival biasing, otherwise known as implicit capture or absorption.
*Default*: false
.. _trace:
``<threads>`` Element
---------------------
@ -615,6 +611,8 @@ a simulation. It has no attributes and accepts a positive integer value.
*Default*: None (Determined by environment variable :envvar:`OMP_NUM_THREADS`)
.. _trace:
``<trace>`` Element
-------------------
@ -631,9 +629,9 @@ integers: the batch number, generation number, and particle number.
The ``<track>`` element specifies particles for which OpenMC will output binary
files describing particle position at every step of its transport. This element
should be followed by triplets of integers. Each triplet describes one particle
. The integers in each triplet specify the batch number, generation number, and
particle number, respectively.
should be followed by triplets of integers. Each triplet describes one
particle. The integers in each triplet specify the batch number, generation
number, and particle number, respectively.
*Default*: None
@ -1224,8 +1222,8 @@ The ``<tally>`` element accepts the following sub-elements:
The ``filter`` element has the following attributes/sub-elements:
:type:
The type of the filter. Accepted options are "cell", "cellborn",
"material", "universe", "energy", "energyout", "mesh", and
The type of the filter. Accepted options are "cell", "cellborn",
"material", "universe", "energy", "energyout", "mesh", and
"distribcell".
:bins:
@ -1307,24 +1305,25 @@ The ``<tally>`` element accepts the following sub-elements:
physical quantities:
:flux:
Total flux
Total flux in particle-cm per source particle.
:total:
Total reaction rate
Total reaction rate in reactions per source particle.
:scatter:
Total scattering rate. Can also be identified with the ``scatter-0``
response type.
response type. Units are reactions per source particle.
:absorption:
Total absorption rate. This accounts for all reactions which do not
produce secondary neutrons.
produce secondary neutrons. Units are reactions per source particle.
:fission:
Total fission rate
Total fission rate in reactions per source particle.
:nu-fission:
Total production of neutrons due to fission
Total production of neutrons due to fission. Units are neutrons produced
per source neutron.
:kappa-fission:
The recoverable energy production rate due to fission. The recoverable
@ -1333,51 +1332,55 @@ The ``<tally>`` element accepts the following sub-elements:
total energies, and the total energy released by the delayed :math:`\beta`
particles. The neutrino energy does not contribute to this response. The
prompt and delayed :math:`\gamma`-rays are assumed to deposit their energy
locally.
locally. Units are MeV per source particle.
:scatter-N:
Tally the N\ :sup:`th` \ scattering moment, where N is the Legendre
expansion order of the change in particle angle :math:`\left(\mu\right)`.
N must be between 0 and 10. As an example, tallying the
2\ :sup:`nd` \ scattering moment would be specified as
``<scores> scatter-2 </scores>``.
N must be between 0 and 10. As an example, tallying the 2\ :sup:`nd` \
scattering moment would be specified as ``<scores> scatter-2
</scores>``. Units are reactions per source particle.
:scatter-PN:
Tally all of the scattering moments from order 0 to N, where N is the
Legendre expansion order of the change in particle angle
:math:`\left(\mu\right)`. That is, ``scatter-P1`` is equivalent to
requesting tallies of ``scatter-0`` and ``scatter-1``. Like for
``scatter-N``, N must be between 0 and 10. As an example, tallying up
to the 2\ :sup:`nd` \ scattering moment would be specified as
``<scores> scatter-P2 </scores>``.
``scatter-N``, N must be between 0 and 10. As an example, tallying up to
the 2\ :sup:`nd` \ scattering moment would be specified as ``<scores>
scatter-P2 </scores>``. Units are reactions per source particle.
:scatter-YN:
``scatter-YN`` is similar to ``scatter-PN`` except an additional
expansion is performed for the incoming particle direction
``scatter-YN`` is similar to ``scatter-PN`` except an additional expansion
is performed for the incoming particle direction
:math:`\left(\Omega\right)` using the real spherical harmonics. This is
useful for performing angular flux moment weighting of the scattering
moments. Like ``scatter-PN``, ``scatter-YN`` will tally all of the
moments from order 0 to N; N again must be between 0 and 10.
moments. Like ``scatter-PN``, ``scatter-YN`` will tally all of the moments
from order 0 to N; N again must be between 0 and 10. Units are reactions
per source particle.
:nu-scatter, nu-scatter-N, nu-scatter-PN, nu-scatter-YN:
These scores are similar in functionality to their ``scatter*``
equivalents except the total production of neutrons due to
scattering is scored vice simply the scattering rate. This accounts for
multiplicity from (n,2n), (n,3n), and (n,4n) reactions.
equivalents except the total production of neutrons due to scattering is
scored vice simply the scattering rate. This accounts for multiplicity
from (n,2n), (n,3n), and (n,4n) reactions. Units are neutrons produced per
source particle.
:flux-YN:
Spherical harmonic expansion of the direction of motion
:math:`\left(\Omega\right)` of the total flux. This score will tally
all of the harmonic moments of order 0 to N. N must be between 0 and 10.
:math:`\left(\Omega\right)` of the total flux. This score will tally all
of the harmonic moments of order 0 to N. N must be between 0
and 10. Units are particle-cm per source particle.
:total-YN:
The total reaction rate expanded via spherical harmonics about the
direction of motion of the neutron, :math:`\Omega`.
This score will tally all of the harmonic moments of order 0 to N. N must
be between 0 and 10.
be between 0 and 10. Units are reactions per source particle.
:current:
Partial currents on the boundaries of each cell in a mesh.
Partial currents on the boundaries of each cell in a mesh. Units are
particles per source particle.
.. note::
This score can only be used if a mesh filter has been
@ -1385,7 +1388,7 @@ The ``<tally>`` element accepts the following sub-elements:
other score.
:events:
Number of scoring events
Number of scoring events. Units are events per source particle.
:trigger:
Precision trigger applied to all filter bins and nuclides for this tally.
@ -1675,6 +1678,15 @@ The ``<begin>`` element controls what batch CMFD calculations should begin.
*Default*: 1
``<dhat_reset>`` Element
------------------------
The ``<dhat_reset>`` element controls whether :math:`\widehat{D}` nonlinear
CMFD parameters should be reset to zero before solving CMFD eigenproblem.
It can be turned on with "true" and off with "false".
*Default*: false
``<display>`` Element
---------------------
@ -1691,15 +1703,6 @@ The ``<display>`` element sets one additional CMFD output column. Options are:
*Default*: balance
``<dhat_reset>`` Element
------------------------
The ``<dhat_reset>`` element controls whether :math:`\widehat{D}` nonlinear
CMFD parameters should be reset to zero before solving CMFD eigenproblem.
It can be turned on with "true" and off with "false".
*Default*: false
``<downscatter>`` Element
-------------------------
@ -1743,11 +1746,11 @@ The CMFD mesh is a structured Cartesian mesh. This element has the following
attributes/sub-elements:
:lower_left:
The lower-left corner of the structured mesh. If only two coordinate are
The lower-left corner of the structured mesh. If only two coordinates are
given, it is assumed that the mesh is an x-y mesh.
:upper_right:
The upper-right corner of the structrued mesh. If only two coordinate are
The upper-right corner of the structrued mesh. If only two coordinates are
given, it is assumed that the mesh is an x-y mesh.
:dimension:
@ -1770,7 +1773,7 @@ attributes/sub-elements:
:map:
An optional acceleration map can be specified to overlay on the coarse
mesh spatial grid. If this option is used a ``1`` is used for a
mesh spatial grid. If this option is used, a ``1`` is used for a
non-accelerated region and a ``2`` is used for an accelerated region.
For a simple 4x4 coarse mesh with a 2x2 fuel lattice surrounded by
reflector, the map is:

View file

@ -160,6 +160,13 @@ openmp
Enables shared-memory parallelism using the OpenMP API. The Fortran compiler
being used must support OpenMP.
coverage
Compile and link code instrumented for coverage analysis. This is typically
used in conjunction with gcov_.
maxcoord
Maximum number of nested coordinate levels in geometry. Defaults to 10.
To set any of these options (e.g. turning on debug mode), the following form
should be used:
@ -167,6 +174,8 @@ should be used:
cmake -Ddebug=on /path/to/openmc
.. _gcov: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html
Compiling with MPI
++++++++++++++++++

View file

@ -108,7 +108,7 @@ energy_filter = openmc.Filter(type='energy', bins=[0., 20.])
energyout_filter = openmc.Filter(type='energyout', bins=[0., 20.])
# Instantiate the first Tally
first_tally = openmc.Tally(tally_id=1, label='first tally')
first_tally = openmc.Tally(tally_id=1, name='first tally')
first_tally.add_filter(cell_filter)
scores = ['total', 'scatter', 'nu-scatter', \
'absorption', 'fission', 'nu-fission']
@ -116,7 +116,7 @@ for score in scores:
first_tally.add_score(score)
# Instantiate the second Tally
second_tally = openmc.Tally(tally_id=2, label='second tally')
second_tally = openmc.Tally(tally_id=2, name='second tally')
second_tally.add_filter(cell_filter)
second_tally.add_filter(energy_filter)
scores = ['total', 'scatter', 'nu-scatter', \
@ -125,7 +125,7 @@ for score in scores:
second_tally.add_score(score)
# Instantiate the third Tally
third_tally = openmc.Tally(tally_id=3, label='third tally')
third_tally = openmc.Tally(tally_id=3, name='third tally')
third_tally.add_filter(cell_filter)
third_tally.add_filter(energy_filter)
third_tally.add_filter(energyout_filter)

View file

@ -1,13 +1,137 @@
import numpy as np
def check_type(name, value, expected_type, expected_iter_type=None):
"""Ensure that an object is of an expected type. Optionally, if the object is
iterable, check that each element is of a particular type.
Parameters
----------
name : str
Description of value being checked
value : object
Object to check type of
expected_type : type
type to check object against
expected_iter_type : type or None, optional
Expected type of each element in value, assuming it is iterable. If
None, no check will be performed.
"""
if not isinstance(value, expected_type):
msg = 'Unable to set {0} to {1} which is not of type {2}'.format(
name, value, expected_type.__name__)
raise ValueError(msg)
if expected_iter_type:
for item in value:
if not isinstance(item, expected_iter_type):
msg = 'Unable to set {0} to {1} since each item must be ' \
'of type {2}'.format(name, value,
expected_iter_type.__name__)
raise ValueError(msg)
def is_integer(val):
return isinstance(val, (int, np.int32, np.int64))
def check_length(name, value, length_min, length_max=None):
"""Ensure that a sized object has length within a given range.
Parameters
----------
name : str
Description of value being checked
value : collections.Sized
Object to check length of
length_min : int
Minimum length of object
length_max : int or None, optional
Maximum length of object. If None, it is assumed object must be of
length length_min.
"""
if length_max is None:
if len(value) != length_min:
msg = 'Unable to set {0} to {1} since it must be of ' \
'length {2}'.format(name, value, length_min)
raise ValueError(msg)
elif not length_min <= len(value) <= length_max:
if length_min == length_max:
msg = 'Unable to set {0} to {1} since it must be of ' \
'length {2}'.format(name, value, length_min)
else:
msg = 'Unable to set {0} to {1} since it must have length ' \
'between {2} and {3}'.format(name, value, length_min,
length_max)
raise ValueError(msg)
def is_float(val):
return isinstance(val, (float, np.float32, np.float64))
def check_value(name, value, accepted_values):
"""Ensure that an object's value is contained in a set of acceptable values.
Parameters
----------
name : str
Description of value being checked
value : collections.Iterable
Object to check
accepted_values : collections.Container
Container of acceptable values
def is_string(val):
return isinstance(val, (str, np.str))
"""
if value not in accepted_values:
msg = 'Unable to set {0} to {1} since it is not in {2}'.format(
name, value, accepted_values)
raise ValueError(msg)
def check_less_than(name, value, maximum, equality=False):
"""Ensure that an object's value is less than a given value.
Parameters
----------
name : str
Description of the value being checked
value : object
Object to check
maximum : object
Maximum value to check against
equality : bool, optional
Whether equality is allowed. Defaluts to False.
"""
if equality:
if value > maximum:
msg = 'Unable to set {0} to {1} since it is greater than ' \
'{2}'.format(name, value, maximum)
raise ValueError(msg)
else:
if value >= maximum:
msg = 'Unable to set {0} to {1} since it is greater than ' \
'or equal to {2}'.format(name, value, maximum)
raise ValueError(msg)
def check_greater_than(name, value, minimum, equality=False):
"""Ensure that an object's value is less than a given value.
Parameters
----------
name : str
Description of the value being checked
value : object
Object to check
minimum : object
Minimum value to check against
equality : bool, optional
Whether equality is allowed. Defaluts to False.
"""
if equality:
if value < minimum:
msg = 'Unable to set {0} to {1} since it is less than ' \
'{2}'.format(name, value, minimum)
raise ValueError(msg)
else:
if value <= minimum:
msg = 'Unable to set {0} to {1} since it is less than ' \
'or equal to {2}'.format(name, value, minimum)
raise ValueError(msg)

View file

@ -1,15 +1,77 @@
"""This module can be used to specify parameters used for coarse mesh finite
difference (CMFD) acceleration in OpenMC. CMFD was first proposed by [Smith]_
and is widely used in accelerating neutron transport problems.
References
----------
.. [Smith] K. Smith, "Nodal method storage reduction by non-linear
iteration", *Trans. Am. Nucl. Soc.*, **44**, 265 (1983).
"""
from collections import Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import numpy as np
from openmc.checkvalue import *
from openmc.clean_xml import *
from openmc.checkvalue import (check_type, check_length, check_value,
check_greater_than, check_less_than)
if sys.version_info[0] >= 3:
basestring = str
class CMFDMesh(object):
"""A structured Cartesian mesh used for Coarse Mesh Finite Difference (CMFD)
acceleration.
Attributes
----------
lower_left : Iterable of float
The lower-left corner of the structured mesh. If only two coordinates are
given, it is assumed that the mesh is an x-y mesh.
upper_right : Iterable of float
The upper-right corner of the structrued mesh. If only two coordinates
are given, it is assumed that the mesh is an x-y mesh.
dimension : Iterable of int
The number of mesh cells in each direction.
width : Iterable of float
The width of mesh cells in each direction.
energy : Iterable of float
Energy bins in MeV, listed in ascending order (e.g. [0.0, 0.625e-7,
20.0]) for CMFD tallies and acceleration. If no energy bins are listed,
OpenMC automatically assumes a one energy group calculation over the
entire energy range.
albedo : Iterable of float
Surface ratio of incoming to outgoing partial currents on global
boundary conditions. They are listed in the following order: -x +x -y +y
-z +z.
map : Iterable of int
An optional acceleration map can be specified to overlay on the coarse
mesh spatial grid. If this option is used, a ``1`` is used for a
non-accelerated region and a ``2`` is used for an accelerated region.
For a simple 4x4 coarse mesh with a 2x2 fuel lattice surrounded by
reflector, the map is:
::
[1, 1, 1, 1,
1, 2, 2, 1,
1, 2, 2, 1,
1, 1, 1, 1]
Therefore a 2x2 system of equations is solved rather than a 4x4. This is
extremely important to use in reflectors as neutrons will not contribute
to any tallies far away from fission source neutron regions. A ``2``
must be used to identify any fission source region.
"""
def __init__(self):
self._lower_left = None
self._upper_right = None
self._dimension = None
@ -18,625 +80,441 @@ class CMFDMesh(object):
self._albedo = None
self._map = None
@property
def lower_left(self):
return self._lower_left
@property
def upper_right(self):
return self._upper_right
@property
def dimension(self):
return self._dimension
@property
def width(self):
return self._width
@property
def energy(self):
return self._energy
@property
def albedo(self):
return self._albedo
@property
def map(self):
return self._mape
return self._map
@lower_left.setter
def lower_left(self, lower_left):
if not isinstance(lower_left, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh with lower_left {0} which is ' \
'not a Python list, tuple or NumPy array'.format(lower_left)
raise ValueError(msg)
elif len(lower_left) != 2 and len(lower_left) != 3:
msg = 'Unable to set CMFD Mesh with lower_left {0} since it ' \
'must include 2 or 3 dimensions'.format(lower_left)
raise ValueError(msg)
for coord in lower_left:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set CMFD Mesh with lower_left {0} which is ' \
'not an integer or a floating point value'.format(coord)
raise ValueError(msg)
check_type('CMFD mesh lower_left', lower_left, Iterable, Real)
check_length('CMFD mesh lower_left', lower_left, 2, 3)
self._lower_left = lower_left
@upper_right.setter
def upper_right(self, upper_right):
if not isinstance(upper_right, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh with upper_right {0} which is ' \
'not a Python list, tuple or NumPy array'.format(upper_right)
raise ValueError(msg)
if len(upper_right) != 2 and len(upper_right) != 3:
msg = 'Unable to set CMFD Mesh with upper_right {0} since it ' \
'must include 2 or 3 dimensions'.format(upper_right)
raise ValueError(msg)
for coord in upper_right:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set CMFD Mesh with upper_right {0} which ' \
'is not an integer or floating point value'.format(coord)
raise ValueError(msg)
check_type('CMFD mesh upper_right', upper_right, Iterable, Real)
check_length('CMFD mesh upper_right', upper_right, 2, 3)
self._upper_right = upper_right
@dimension.setter
def dimension(self, dimension):
if not isinstance(dimension, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh with dimension {0} which is ' \
'not a Python list, tuple or NumPy array'.format(dimension)
raise ValueError(msg)
elif len(dimension) != 2 and len(dimension) != 3:
msg = 'Unable to set CMFD Mesh with dimension {0} since it ' \
'must include 2 or 3 dimensions'.format(dimension)
raise ValueError(msg)
for dim in dimension:
if not is_integer(dim):
msg = 'Unable to set CMFD Mesh with dimension {0} which ' \
'is a non-integer'.format(dim)
raise ValueError(msg)
check_type('CMFD mesh dimension', dimension, Iterable, Integral)
check_length('CMFD mesh dimension', dimension, 2, 3)
self._dimension = dimension
@width.setter
def width(self, width):
if not width is None:
if not isinstance(width, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh with width {0} which ' \
'is not a Python list, tuple or NumPy array'.format(width)
raise ValueError(msg)
if len(width) != 2 and len(width) != 3:
msg = 'Unable to set CMFD Mesh with width {0} since it must ' \
'include 2 or 3 dimensions'.format(width)
raise ValueError(msg)
for dim in width:
if not is_integer(dim) and not is_float(dim):
msg = 'Unable to set CMFD Mesh with width {0} which is ' \
'not an integer or floating point value'.format(width)
raise ValueError(msg)
check_type('CMFD mesh width', width, Iterable, Real)
check_length('CMFD mesh width', width, 2, 3)
self._width = width
@energy.setter
def energy(self, energy):
if not isinstance(energy, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh energy to {0} which is not ' \
'a Python tuple/list or NumPy array'.format(energy)
raise ValueError(msg)
check_type('CMFD mesh energy', energy, Iterable, Real)
for e in energy:
if not is_integer(e) and not is_float(e):
msg = 'Unable to set CMFD Mesh energy to {0} which is not ' \
'an integer or floating point value'.format(e)
raise ValueError(msg)
elif e < 0:
msg = 'Unable to set CMFD Mesh energy to {0} which is ' \
'is a negative integer'.format(e)
raise ValueError(msg)
check_greater_than('CMFD mesh energy', e, 0, True)
self._energy = energy
@albedo.setter
def albedo(self, albedo):
if not isinstance(albedo, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh albedo to {0} which is not ' \
'a Python tuple/list or NumPy array'.format(albedo)
raise ValueError(msg)
if not len(albedo) == 6:
msg = 'Unable to set CMFD Mesh albedo to {0} which is not ' \
'length 6 for +/-x,y,z'.format(albedo)
raise ValueError(msg)
check_type('CMFD mesh albedo', albedo, Iterable, Real)
check_length('CMFD mesh albedo', albedo, 6)
for a in albedo:
if not is_integer(a) and not is_float(a):
msg = 'Unable to set CMFD Mesh albedo to {0} which is not ' \
'an integer or floating point value'.format(a)
raise ValueError(msg)
elif a < 0 or a > 1:
msg = 'Unable to set CMFD Mesh albedo to {0} which is ' \
'is not in [0,1]'.format(a)
raise ValueError(msg)
check_greater_than('CMFD mesh albedo', a, 0, True)
check_less_than('CMFD mesh albedo', a, 1, True)
self._albedo = albedo
@map.setter
def map(self, map):
if not isinstance(map, (tuple, list, np.ndarray)):
msg = 'Unable to set CMFD Mesh map to {0} which is not ' \
'a Python tuple/list or NumPy array'.format(map)
raise ValueError(msg)
for m in map:
if m != 1 and m != 2:
msg = 'Unable to set CMFD Mesh map to {0} which is ' \
'is not 1 or 2'.format(m)
raise ValueError(msg)
self._map = map
def get_mesh_xml(self):
def map(self, meshmap):
check_type('CMFD mesh map', meshmap, Iterable, Integral)
for m in meshmap:
check_value('CMFD mesh map', m, [1, 2])
self._map = meshmap
def _get_xml_element(self):
element = ET.Element("mesh")
if len(self._lower_left) == 2:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1}'.format(self._lower_left[0],
self._lower_left[1])
else:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1} {2}'.format(self._lower_left[0],
self._lower_left[1],
self._lower_left[2])
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(map(str, self._lower_left))
if not self._upper_right is None:
if len(self._upper_right) == 2:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1}'.format(self._upper_right[0],
self._upper_right[1])
else:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1} {2}'.format(self._upper_right[0],
self._upper_right[1],
self._upper_right[2])
if self.upper_right is not None:
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(map(str, self.upper_right))
if len(self._dimension) == 2:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1}'.format(self._dimension[0],
self._dimension[1])
else:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1} {2}'.format(self._dimension[0],
self._dimension[1],
self._dimension[2])
subelement = ET.SubElement(element, "dimension")
subelement.text = ' '.join(map(str, self.dimension))
if not self._width is None:
if len(self._width) == 2:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1}'.format(self._width[0],
self._width[1])
else:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1} {2}'.format(self._width[0],
self._width[1],
self._width[2])
if not self._energy is None:
if self.width is not None:
subelement = ET.SubElement(element, "width")
subelement.text = ' '.join(map(str, self.width))
if self.energy is not None:
subelement = ET.SubElement(element, "energy")
subelement.text = ' '.join(map(str, self.energy))
energy = ''
for e in self._energy:
energy += '{0} '.format(e)
subelement.set("energy", energy.rstrip(' '))
if not self._albedo is None:
if self.albedo is not None:
subelement = ET.SubElement(element, "albedo")
subelement.text = ' '.join(map(str, self.albedo))
albedo = ''
for a in self._albedo:
albedo += '{0} '.format(a)
subelement.set("albedo", albedo.rstrip(' '))
if not self._map is None:
if self.map is not None:
subelement = ET.SubElement(element, "map")
map = ''
for m in self._map:
map += '{0} '.format(m)
subelement.set("map", map.rstrip(' '))
subelement.text = ' '.join(map(str, self.map))
return element
class CMFDFile(object):
"""Parameters that control the use of coarse-mesh finite difference acceleration
in OpenMC. This corresponds directly to the cmfd.xml input file.
Attributes
----------
begin : int
Batch number at which CMFD calculations should begin
dhat_reset : bool
Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should be
reset to zero before solving CMFD eigenproblem.
display : {'balance', 'dominance', 'entropy', 'source'}
Set one additional CMFD output column. Options are:
* "balance" - prints the RMS [%] of the resdiual from the neutron balance
equation on CMFD tallies.
* "dominance" - prints the estimated dominance ratio from the CMFD
iterations.
* "entropy" - prints the *entropy* of the CMFD predicted fission source.
* "source" - prints the RMS [%] between the OpenMC fission source and
CMFD fission source.
downscatter : bool
Indicate whether an effective downscatter cross section should be used
when using 2-group CMFD.
feedback : bool
Indicate or not the CMFD diffusion result is used to adjust the weight
of fission source neutrons on the next OpenMC batch. Defaults to False.
gauss_seidel_tolerance : Iterable of float
Two parameters specifying the absolute inner tolerance and the relative
inner tolerance for Gauss-Seidel iterations when performing CMFD.
ktol : float
Tolerance on the eigenvalue when performing CMFD power iteration
cmfd_mesh : CMFDMesh
Structured mesh to be used for acceleration
norm : float
Normalization factor applied to the CMFD fission source distribution
power_monitor : bool
View convergence of power iteration during CMFD acceleration
run_adjoint : bool
Perform adjoint calculation on the last batch
shift : float
Optional Wielandt shift parameter for accelerating power iterations. By
default, it is very large so there is effectively no impact.
spectral : float
Optional spectral radius that can be used to accelerate the convergence
of Gauss-Seidel iterations during CMFD power iteration.
stol : float
Tolerance on the fission source when performing CMFD power iteration
tally_reset : list of int
List of batch numbers at which CMFD tallies should be reset
write_matrices : bool
Write sparse matrices that are used during CMFD acceleration (loss,
production) to file
"""
def __init__(self):
self._active_flush = None
self._begin = None
self._dhat_reset = None
self._display = None
self._downscatter = None
self._feedback = None
self._inactive = None
self._inactive_flush = None
self._gauss_seidel_tolerance = None
self._ktol = None
self._cmfd_mesh = None
self._norm = None
self._num_flushes = None
self._power_monitor = None
self._run_adjoint = None
self._shift = None
self._spectral = None
self._stol = None
self._tally_reset = None
self._write_matrices = None
self._cmfd_file = ET.Element("cmfd")
self._cmfd_mesh_element = None
@property
def active_flush(self):
return self._active_flush
@property
def begin(self):
return self._begin
@property
def dhat_reset(self):
return self._dhat_reset
@property
def display(self):
return self._display
@property
def downscatter(self):
return self._downscatter
@property
def feedback(self):
return self._feedback
@property
def gauss_seidel_tolerance(self):
return self._gauss_seidel_tolerance
@property
def inactive(self):
return self._inactive
@property
def inactive_flush(self):
return self._inactive_flush
def ktol(self):
return self._ktol
@property
def cmfd_mesh(self):
return self._cmfd_mesh
@property
def norm(self):
return self._norm
@property
def num_flushes(self):
return self._num_flushes
@property
def power_monitor(self):
return self._power_monitor
@property
def run_adjoint(self):
return self._run_adjoint
@property
def shift(self):
return self._shift
@property
def solver(self):
return self._solver
def spectral(self):
return self._spectral
@property
def stol(self):
return self._stol
@property
def tally_reset(self):
return self._tally_reset
@property
def write_matrices(self):
return self._write_matrices
@active_flush.setter
def active_flush(self, active_flush):
if not is_integer(active_flush):
msg = 'Unable to set CMFD active flush batch to a non-integer ' \
'value {0}'.format(active_flush)
raise ValueError(msg)
if active_flush < 0:
msg = 'Unable to set CMFD active flush batch to a negative ' \
'value {0}'.format(active_flush)
raise ValueError(msg)
self._active_flush = active_flush
@begin.setter
def begin(self, begin):
if not is_integer(begin):
msg = 'Unable to set CMFD begin batch to a non-integer ' \
'value {0}'.format(begin)
raise ValueError(msg)
if begin <= 0:
msg = 'Unable to set CMFD begin batch batch to a negative ' \
'value {0}'.format(begin)
raise ValueError(msg)
check_type('CMFD begin batch', begin, Integral)
check_greater_than('CMFD begin batch', begin, 0)
self._begin = begin
@dhat_reset.setter
def dhat_reset(self, dhat_reset):
check_type('CMFD Dhat reset', dhat_reset, bool)
self._dhat_reset = dhat_reset
@display.setter
def display(self, display):
if not is_string(display):
msg = 'Unable to set CMFD display to a non-string ' \
'value'.format(display)
raise ValueError(msg)
if display not in ['balance', 'dominance', 'entropy', 'source']:
msg = 'Unable to set CMFD display to {0} which is ' \
'not an accepted value'.format(display)
raise ValueError(msg)
check_type('CMFD display', display, basestring)
check_value('CMFD display', display,
['balance', 'dominance', 'entropy', 'source'])
self._display = display
@downscatter.setter
def downscatter(self, downscatter):
check_type('CMFD downscatter', downscatter, bool)
self._downscatter = downscatter
@feedback.setter
def feedback(self, feedback):
if not isinstance(feedback, bool):
msg = 'Unable to set CMFD feedback to {0} which is ' \
'a non-boolean value'.format(feedback)
raise ValueError(msg)
check_type('CMFD feedback', feedback, bool)
self._feedback = feedback
@gauss_seidel_tolerance.setter
def gauss_seidel_tolerance(self, gauss_seidel_tolerance):
check_type('CMFD Gauss-Seidel tolerance', gauss_seidel_tolerance,
Iterable, Real)
check_length('Gauss-Seidel tolerance', gauss_seidel_tolerance, 2)
self._gauss_seidel_tolerance = gauss_seidel_tolerance
@inactive.setter
def inactive(self, inactive):
if not isinstance(inactive, bool):
msg = 'Unable to set CMFD inactive batch to {0} which is ' \
' a non-boolean value'.format(inactive)
raise ValueError(msg)
self._inactive = inactive
@inactive_flush.setter
def inactive_flush(self, inactive_flush):
if not is_integer(inactive_flush):
msg = 'Unable to set CMFD inactive flush batch to {0} which is ' \
'a non-integer value'.format(inactive_flush)
raise ValueError(msg)
if inactive_flush <= 0:
msg = 'Unable to set CMFD inactive flush batch to {0} which is ' \
'a negative value {0}'.format(inactive_flush)
raise ValueError(msg)
self._inactive_flush = inactive_flush
@ktol.setter
def ktol(self, ktol):
check_type('CMFD eigenvalue tolerance', ktol, Real)
self._ktol = ktol
@cmfd_mesh.setter
def cmfd_mesh(self, mesh):
if not isinstance(mesh, CMFDMesh):
msg = 'Unable to set CMFD mesh to {0} which is not a ' \
'CMFDMesh object'.format(mesh)
raise ValueError(msg)
check_type('CMFD mesh', mesh, CMFDMesh)
self._mesh = mesh
@norm.setter
def norm(self, norm):
if not is_integer(norm) and not is_float(norm):
msg = 'Unable to set the CMFD norm to {0} which is not ' \
'an integer or floating point value'.format(norm)
raise ValueError(msg)
check_type('CMFD norm', norm, Real)
self._norm = norm
@num_flushes.setter
def num_flushes(self, num_flushes):
if not is_integer(num_flushes):
msg = 'Unable to set the CMFD number of flushes to {0} ' \
'which is not an integer value'.format(num_flushes)
raise ValueError(msg)
if num_flushes < 0:
msg = 'Unable to set CMFD number of flushes to a negative ' \
'value {0}'.format(num_flushes)
raise ValueError(msg)
self._num_flushes = num_flushes
@power_monitor.setter
def power_monitor(self, power_monitor):
if not isinstance(power_monitor, bool):
msg = 'Unable to set CMFD power monitor to {0} which is a ' \
'non-boolean value'.format(power_monitor)
raise ValueError(msg)
check_type('CMFD power monitor', power_monitor, bool)
self._power_monitor = power_monitor
@run_adjoint.setter
def run_adjoint(self, run_adjoint):
if not isinstance(run_adjoint, bool):
msg = 'Unable to set CMFD run adjoint to {0} which is a ' \
'non-boolean value'.format(run_adjoint)
raise ValueError(msg)
check_type('CMFD run adjoint', run_adjoint, bool)
self._run_adjoint = run_adjoint
@shift.setter
def shift(self, shift):
check_type('CMFD Wielandt shift', shift, Real)
self._shift = shift
@spectral.setter
def spectral(self, spectral):
check_type('CMFD spectral radius', spectral, Real)
self._spectral = spectral
@stol.setter
def stol(self, stol):
check_type('CMFD fission source tolerance', stol, Real)
self._stol = stol
@tally_reset.setter
def tally_reset(self, tally_reset):
check_type('tally reset batches', tally_reset, Iterable, Integral)
self._tally_reset = tally_reset
@write_matrices.setter
def write_matrices(self, write_matrices):
if not isinstance(write_matrices, bool):
msg = 'Unable to set CMFD write matrices to {0} which is a ' \
'non-boolean value'.format(write_matrices)
raise ValueError(msg)
check_type('CMFD write matrices', write_matrices, bool)
self._write_matrices = write_matrices
def create_active_flush_subelement(self):
if not self._active_flush is None:
element = ET.SubElement(self._cmfd_file, "active_flush")
element.text = '{0}'.format(str(self._active_flush))
def create_begin_subelement(self):
if not self._begin is None:
def _create_begin_subelement(self):
if self._begin is not None:
element = ET.SubElement(self._cmfd_file, "begin")
element.text = '{0}'.format(str(self._begin))
element.text = str(self._begin)
def _create_dhat_reset_subelement(self):
if self._dhat_reset is not None:
element = ET.SubElement(self._cmfd_file, "dhat_reset")
element.text = str(self._dhat_reset).lower()
def create_display_subelement(self):
if not self._display is None:
def _create_display_subelement(self):
if self._display is not None:
element = ET.SubElement(self._cmfd_file, "display")
element.text = '{0}'.format(str(self._display))
element.text = str(self._display)
def _create_downscatter_subelement(self):
if self._downscatter is not None:
element = ET.SubElement(self._cmfd_file, "downscatter")
element.text = str(self._downscatter).lower()
def create_feedback_subelement(self):
if not self._feedback is None:
def _create_feedback_subelement(self):
if self._feedback is not None:
element = ET.SubElement(self._cmfd_file, "feeback")
element.text = '{0}'.format(str(self._feedback).lower())
element.text = str(self._feedback).lower()
def _create_gauss_seidel_tolerance_subelement(self):
if self._gauss_seidel_tolerance is not None:
element = ET.SubElement(self._cmfd_file, "gauss_seidel_tolerance")
element.text = ' '.join(map(str, self._gauss_seidel_tolerance))
def create_inactive_subelement(self):
def _create_ktol_subelement(self):
if self._ktol is not None:
element = ET.SubElement(self._ktol, "ktol")
element.text = str(self._ktol)
if not self._inactive is None:
element = ET.SubElement(self._cmfd_file, "inactive")
element.text = '{0}'.format(str(self._inactive).lower())
def create_inactive_flush_subelement(self):
if not self._inactive_flush is None:
element = ET.SubElement(self._cmfd_file, "inactive_flush")
element.text = '{0}'.format(str(self._inactive_flush))
def create_mesh_subelement(self):
if not self._mesh is None:
xml_element = self._mesh.get_mesh_xml()
def _create_mesh_subelement(self):
if self._mesh is not None:
xml_element = self._mesh._get_xml_element()
self._cmfd_file.append(xml_element)
def create_norm_subelement(self):
if not self._num_flushes is None:
def _create_norm_subelement(self):
if self._norm is not None:
element = ET.SubElement(self._cmfd_file, "norm")
element.text = '{0}'.format(str(self._norm))
element.text = str(self._norm)
def create_num_flushes_subelement(self):
if not self._num_flushes is None:
element = ET.SubElement(self._cmfd_file, "num_flushes")
element.text = '{0}'.format(str(self._num_flushes))
def create_power_monitor_subelement(self):
if not self._power_monitor is None:
def _create_power_monitor_subelement(self):
if self._power_monitor is not None:
element = ET.SubElement(self._cmfd_file, "power_monitor")
element.text = '{0}'.format(str(self._power_monitor).lower())
element.text = str(self._power_monitor).lower()
def create_run_adjoint_subelement(self):
if not self._run_adjoint is None:
def _create_run_adjoint_subelement(self):
if self._run_adjoint is not None:
element = ET.SubElement(self._cmfd_file, "run_adjoint")
element.text = '{0}'.format(str(self._run_adjoint).lower())
element.text = str(self._run_adjoint).lower()
def _create_shift_subelement(self):
if self._shift is not None:
element = ET.SubElement(self._shift, "shift")
element.text = str(self._shift)
def create_write_matrices_subelement(self):
def _create_spectral_subelement(self):
if self._spectral is not None:
element = ET.SubElement(self._spectral, "spectral")
element.text = str(self._spectral)
if not self._write_matrices is None:
def _create_stol_subelement(self):
if self._stol is not None:
element = ET.SubElement(self._stol, "stol")
element.text = str(self._stol)
def _create_tally_reset_subelement(self):
if self._tally_reset is not None:
element = ET.SubElement(self._tally_reset, "tally_reset")
element.text = ' '.join(map(str, self._tally_reset))
def _create_write_matrices_subelement(self):
if self._write_matrices is not None:
element = ET.SubElement(self._cmfd_file, "write_matrices")
element.text = '{0}'.format(str(self._write_matrices).lower())
element.text = str(self._write_matrices).lower()
def export_to_xml(self):
"""Create a cmfd.xml file using the class data that can be used for an OpenMC
simulation.
self.create_active_flush_subelement()
self.create_begin_subelement()
self.create_display_subelement()
self.create_feedback_subelement()
self.create_inactive_subelement()
self.create_inactive_flush_subelement()
self.create_mesh_subelement()
self.create_norm_subelement()
self.create_num_flushes_subelement()
self.create_power_monitor_subelement()
self.create_run_adjoint_subelement()
self.create_write_matrices_subelement()
"""
self._create_begin_subelement()
self._create_dhat_reset_subelement()
self._create_display_subelement()
self._create_downscatter_subelement()
self._create_feedback_subelement()
self._create_gauss_seidel_tolerance_subelement()
self._create_ktol_subelement()
self._create_mesh_subelement()
self._create_norm_subelement()
self._create_power_monitor_subelement()
self._create_run_adjoint_subelement()
self._create_shift_subelement()
self._create_spectral_subelement()
self._create_stol_subelement()
self._create_tally_reset_subelement()
self._create_write_matrices_subelement()
# Clean the indentation in the file to be user-readable
clean_xml_indentation(self._cmfd_file)

View file

@ -1,79 +1,78 @@
from openmc.checkvalue import *
import sys
from openmc.checkvalue import check_type
if sys.version_info[0] >= 3:
basestring = str
class Element(object):
"""A natural element used in a material via <element>. Internally, OpenMC will
expand the natural element into isotopes based on the known natural
abundances.
Parameters
----------
name : str
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
Attributes
----------
name : str
Chemical symbol of the element, e.g. Pu
xs : str
Cross section identifier, e.g. 71c
"""
def __init__(self, name='', xs=None):
# Initialize class attributes
self._name = ''
self._xs = None
# Set the Material class attributes
# Set class attributes
self.name = name
if not xs is None:
if xs is not None:
self.xs = xs
def __eq__(self, element2):
# Check type
if not isinstance(element2, Element):
return False
# Check name
# Check name and xs
if self._name != element2._name:
return False
# Check xs
elif self._xs != element2._xs:
return False
else:
return True
def __hash__(self):
hashable = []
hashable.append(self._name)
hashable.append(self._xs)
return hash(tuple(hashable))
return hash((self._name, self._xs))
@property
def xs(self):
return self._xs
@property
def name(self):
return self._name
@xs.setter
def xs(self, xs):
if not is_string(xs):
msg = 'Unable to set cross-section identifier xs for Element ' \
'with a non-string value {0}'.format(xs)
raise ValueError(msg)
check_type('cross section identifier', xs, basestring)
self._xs = xs
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Element with a non-string ' \
'value {0}'.format(name)
raise ValueError(msg)
check_type('name', name, basestring)
self._name = name
def __repr__(self):
string = 'Element - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
return string

View file

@ -1,83 +1,128 @@
from __future__ import print_function
import subprocess
from numbers import Integral
import os
import sys
from openmc.checkvalue import *
from openmc.checkvalue import check_type
if sys.version_info[0] >= 3:
basestring = str
class Executor(object):
"""Control execution of OpenMC
Attributes
----------
working_directory : str
Path to working directory to run in
"""
def __init__(self):
self._working_directory = '.'
def _run_openmc(self, command, output):
# Launch a subprocess to run OpenMC
p = subprocess.Popen(command, shell=True,
p = subprocess.Popen(command, shell=True,
cwd=self._working_directory,
stdout=subprocess.PIPE)
# Capture and re-print OpenMC output in real-time
while (True and output):
line = p.stdout.readline()
print(line),
print(line, end='')
# If OpenMC is finished, break loop
if line == '' and p.poll() != None:
if not line and p.poll() != None:
break
# Return the returncode (integer, zero if no problems encountered)
return p.returncode
@property
def working_directory(self):
return self._working_directory
@working_directory.setter
def working_directory(self, working_directory):
if not is_string(working_directory):
msg = 'Unable to set Executor\'s working directory to {0} ' \
'since it is not a string'.format(working_directory)
raise ValueError(msg)
elif not os.path.isdir(working_directory):
check_type("Executor's working directory", working_directory,
basestring)
if not os.path.isdir(working_directory):
msg = 'Unable to set Executor\'s working directory to {0} ' \
'which does not exist'.format(working_directory)
raise ValueError(msg)
self._working_directory = working_directory
def plot_geometry(self, output=True, openmc_exec='openmc'):
"""Run OpenMC in plotting mode"""
def plot_geometry(self, output=True):
self._run_openmc('openmc -p', output)
return self._run_openmc(openmc_exec + ' -p', output)
def run_simulation(self, particles=None, threads=None,
geometry_debug=False, restart_file=None,
tracks=False, mpi_procs=1, output=True):
tracks=False, mpi_procs=1, output=True,
openmc_exec='openmc', mpi_exec=None):
"""Run an OpenMC simulation.
Parameters
----------
particles : int
Number of particles to simulate per generation
threads : int
Number of OpenMP threads
geometry_debug : bool
Turn on geometry debugging during simulation
restart_file : str
Path to restart file to use
tracks : bool
Write tracks for all particles
mpi_procs : int
Number of MPI processes
output : bool
Capture OpenMC output from standard out
openmc_exec : str
Path to OpenMC executable
"""
post_args = ' '
pre_args = ''
if is_integer(particles) and particles > 0:
if isinstance(particles, Integral) and particles > 0:
post_args += '-n {0} '.format(particles)
if is_integer(threads) and threads > 0:
if isinstance(threads, Integral) and threads > 0:
post_args += '-s {0} '.format(threads)
if geometry_debug:
post_args += '-g '
if is_string(restart_file):
if isinstance(restart_file, basestring):
post_args += '-r {0} '.format(restart_file)
if tracks:
post_args += '-t'
if is_integer(mpi_procs) and mpi_procs > 1:
pre_args += 'mpirun -n {0} '.format(mpi_procs)
if isinstance(mpi_procs, Integral) and mpi_procs > 1:
np_present = True
else:
np_present = False
command = pre_args + 'openmc ' + post_args
if mpi_exec is not None and isinstance(mpi_exec, basestring):
mpi_exec_present = True
else:
mpi_exec_present = False
self._run_openmc(command, output)
if np_present or mpi_exec_present:
if mpi_exec_present:
pre_args += mpi_exec + ' '
else:
pre_args += 'mpirun '
pre_args += '-n {0} '.format(mpi_procs)
command = pre_args + openmc_exec + ' ' + post_args
return self._run_openmc(command, output)

View file

@ -1,15 +1,38 @@
from collections import Iterable
import copy
from numbers import Real, Integral
import numpy as np
from openmc import Mesh
from openmc.checkvalue import *
from openmc.constants import *
from openmc.checkvalue import check_type
class Filter(object):
"""A filter used to constrain a tally to a specific criterion, e.g. only tally
events when the particle is in a certain cell and energy range.
Parameters
----------
type : str
The type of the tally filter. Acceptable values are "universe",
"material", "cell", "cellborn", "surface", "mesh", "energy",
"energyout", and "distribcell".
bins : int or Iterable of int or Iterable of float
The bins for the filter. This takes on different meaning for different
filters.
Attributes
----------
type : str
The type of the tally filter.
bins : int or Iterable of int or Iterable of float
The bins for the filter
"""
# Initialize Filter class attributes
def __init__(self, type=None, bins=None):
self.type = type
self._num_bins = 0
self.bins = bins
@ -17,9 +40,7 @@ class Filter(object):
self._offset = -1
self._stride = None
def __eq__(self, filter2):
# Check type
if self._type != filter2._type:
return False
@ -35,21 +56,14 @@ class Filter(object):
else:
return True
def __hash__(self):
hashable = []
hashable.append(self._type)
hashable.append(self._bins)
return hash(tuple(hashable))
return hash((self._type, self._bins))
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._type = self._type
clone._bins = copy.deepcopy(self._bins, memo)
@ -66,64 +80,52 @@ class Filter(object):
else:
return existing
@property
def type(self):
return self._type
@property
def bins(self):
return self._bins
@property
def num_bins(self):
return self._num_bins
@property
def mesh(self):
return self._mesh
@property
def offset(self):
return self._offset
@property
def stride(self):
return self._stride
@type.setter
def type(self, type):
if type is None:
self._type = type
elif not type in FILTER_TYPES.values():
elif type not in FILTER_TYPES.values():
msg = 'Unable to set Filter type to "{0}" since it is not one ' \
'of the supported types'.format(type)
raise ValueError(msg)
self._type = type
@bins.setter
def bins(self, bins):
if bins is None:
self.num_bins = 0
elif self._type is None:
msg = 'Unable to set bins for Filter to "{0}" since ' \
'the Filter type has not yet been set'.format(bins)
raise ValueError(msg)
# If the bin edge is a single value, it is a Cell, Material, etc. ID
if not isinstance(bins, (tuple, list, np.ndarray)):
if not isinstance(bins, Iterable):
bins = [bins]
# If the bins are in a collection, convert it to a list
@ -132,30 +134,23 @@ class Filter(object):
if self._type in ['cell', 'cellborn', 'surface', 'material',
'universe', 'distribcell']:
for edge in bins:
if not is_integer(edge):
if not isinstance(edge, Integral):
msg = 'Unable to add bin "{0}" to a {1} Filter since ' \
'it is a non-integer'.format(edge, self._type)
'it is not an integer'.format(edge, self._type)
raise ValueError(msg)
elif edge < 0:
msg = 'Unable to add bin "{0}" to a {1} Filter since ' \
'it is a negative integer'.format(edge, self._type)
'it is negative'.format(edge, self._type)
raise ValueError(msg)
elif self._type in ['energy', 'energyout']:
for edge in bins:
if not is_integer(edge) and not is_float(edge):
if not isinstance(edge, Real):
msg = 'Unable to add bin edge "{0}" to a {1} Filter ' \
'since it is a non-integer or floating point ' \
'value'.format(edge, self._type)
raise ValueError(msg)
elif edge < 0.:
msg = 'Unable to add bin edge "{0}" to a {1} Filter ' \
'since it is a negative value'.format(edge, self._type)
@ -163,27 +158,22 @@ class Filter(object):
# Check that bin edges are monotonically increasing
for index in range(len(bins)):
if index > 0 and bins[index] < bins[index-1]:
msg = 'Unable to add bin edges "{0}" to a {1} Filter ' \
'since they are not monotonically ' \
'increasing'.format(bins, self._type)
raise ValueError(msg)
# mesh filters
elif self._type == 'mesh':
if not len(bins) == 1:
msg = 'Unable to add bins "{0}" to a mesh Filter since ' \
'only a single mesh can be used per tally'.format(bins)
raise ValueError(msg)
elif not is_integer(bins[0]):
elif not isinstance(bins[0], Integral):
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a non-integer'.format(bins[0])
raise ValueError(msg)
elif bins[0] < 0:
msg = 'Unable to add bin "{0}" to mesh Filter since it ' \
'is a negative integer'.format(bins[0])
@ -192,12 +182,10 @@ class Filter(object):
# If all error checks passed, add bin edges
self._bins = bins
# FIXME
@num_bins.setter
def num_bins(self, num_bins):
if not is_integer(num_bins) or num_bins < 0:
if not isinstance(num_bins, Integral) or num_bins < 0:
msg = 'Unable to set the number of bins "{0}" for a {1} Filter ' \
'since it is not a positive ' \
'integer'.format(num_bins, self._type)
@ -205,39 +193,22 @@ class Filter(object):
self._num_bins = num_bins
@mesh.setter
def mesh(self, mesh):
if not isinstance(mesh, Mesh):
msg = 'Unable to set Mesh to "{0}" for Filter since it is not a ' \
'Mesh object'.format(mesh)
raise ValueError(msg)
check_type('filter mesh', mesh, Mesh)
self._mesh = mesh
self.type = 'mesh'
self.bins = self._mesh._id
@offset.setter
def offset(self, offset):
if not is_integer(offset):
msg = 'Unable to set offset "{0}" for a {1} Filter since it is a ' \
'non-integer value'.format(offset, self._type)
raise ValueError(msg)
check_type('filter offset', offset, Integral)
self._offset = offset
@stride.setter
def stride(self, stride):
if not is_integer(stride):
msg = 'Unable to set stride "{0}" for a {1} Filter since it is a ' \
'non-integer value'.format(stride, self._type)
raise ValueError(msg)
check_type('filter stride', stride, Integral)
if stride < 0:
msg = 'Unable to set stride "{0}" for a {1} Filter since it is a ' \
'negative value'.format(stride, self._type)
@ -245,8 +216,20 @@ class Filter(object):
self._stride = stride
def can_merge(self, filter):
"""Determine if filter can be merged with another.
Parameters
----------
filter : Filter
Filter to compare with
Returns
-------
bool
Whether the filter can be merged
"""
if not isinstance(filter, Filter):
return False
@ -270,8 +253,20 @@ class Filter(object):
else:
return True
def merge(self, filter):
"""Merge this filter with another.
Parameters
----------
filter : Filter
Filter to merge with
Returns
-------
merged_filter : Filter
Filter resulting from the merge
"""
if not self.can_merge(filter):
msg = 'Unable to merge {0} with {1} filters'.format(self._type, filter._type)
@ -287,33 +282,30 @@ class Filter(object):
return merged_filter
def get_bin_index(self, filter_bin):
"""Returns the index in the Filter for some bin.
Parameters
----------
filter_bin : int, tuple
The bin is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. The bin is an integer for
the cell instance ID for 'distribcell' Filters. The bin is
a 2-tuple of floats for 'energy' and 'energyout' filters
corresponding to the energy boundaries of the bin of interest.
The bin is a (x,y,z) 3-tuple for 'mesh' filters corresponding to
the mesh cell of interest.
filter_bin : int or tuple
The bin is the integer ID for 'material', 'surface', 'cell',
'cellborn', and 'universe' Filters. The bin is an integer for the
cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of
floats for 'energy' and 'energyout' filters corresponding to the
energy boundaries of the bin of interest. The bin is a (x,y,z)
3-tuple for 'mesh' filters corresponding to the mesh cell of
interest.
Returns
-------
filter_index : int
The index in the Tally data array for this filter bin.
"""
# FIXME: This does not work for distribcells!!!
try:
# Filter bins for a mesh are an (x,y,z) tuple
if self.type == 'mesh':
# Convert (x,y,z) to a single bin -- this is similar to
# subroutine mesh_indices_to_bin in openmc/src/mesh.F90.
if (len(self.mesh.dimension) == 3):
@ -350,9 +342,7 @@ class Filter(object):
return filter_index
def __repr__(self):
string = 'Filter\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type)
string += '{0: <16}{1}{2}\n'.format('\tBins', '=\t', self._bins)

View file

@ -2,7 +2,7 @@ from xml.etree import ElementTree as ET
import openmc
from openmc.clean_xml import *
from openmc.checkvalue import check_type
def reset_auto_ids():
openmc.reset_auto_material_id()
@ -12,28 +12,28 @@ def reset_auto_ids():
class Geometry(object):
"""Geometry representing a collection of surfaces, cells, and universes.
Attributes
----------
root_universe : openmc.universe.Universe
Root universe which contains all others
"""
def __init__(self):
# Initialize Geometry class attributes
self._root_universe = None
self._offsets = {}
@property
def root_universe(self):
return self._root_universe
@root_universe.setter
def root_universe(self, root_universe):
if not isinstance(root_universe, openmc.Universe):
msg = 'Unable to add root Universe {0} to Geometry since ' \
'it is not a Universe'.format(root_universe)
raise ValueError(msg)
elif root_universe._id != 0:
check_type('root universe', root_universe, openmc.Universe)
if root_universe._id != 0:
msg = 'Unable to add root Universe {0} to Geometry since ' \
'it has ID={1} instead of ' \
'ID=0'.format(root_universe, root_universe._id)
@ -41,24 +41,26 @@ class Geometry(object):
self._root_universe = root_universe
def get_offset(self, path, filter_offset):
"""
Returns the corresponding location in the results array for a given
path and filter number.
"""Returns the corresponding location in the results array for a given path and
filter number. This is primarily intended to post-processing result when
a distribcell filter is used.
Parameters
----------
path : list
A list of IDs that form the path to the target. It should begin
with 0 for the base universe, and should cover every universe,
cell, and lattice passed through. For the case of the lattice,
a tuple should be provided to indicate which coordinates in the
lattice should be entered. This should be in the
form: (lat_id, i_x, i_y, i_z)
A list of IDs that form the path to the target. It should begin with
0 for the base universe, and should cover every universe, cell, and
lattice passed through. For the case of the lattice, a tuple should
be provided to indicate which coordinates in the lattice should be
entered. This should be in the form: (lat_id, i_x, i_y, i_z)
filter_offset : int
An integer that specifies which offset map the filter is using
An integer that specifies which offset map the filter is using
Returns
-------
offset : int
Location in the results array for the path and filter
"""
@ -74,16 +76,39 @@ class Geometry(object):
# Return the final offset
return offset
def get_all_cells(self):
"""Return all cells defined
Returns
-------
list of openmc.universe.Cell
Cells in the geometry
"""
return self._root_universe.get_all_cells()
def get_all_universes(self):
"""Return all universes defined
Returns
-------
list of openmc.universe.Universe
Universes in the geometry
"""
return self._root_universe.get_all_universes()
def get_all_nuclides(self):
"""Return all nuclides assigned to a material in the geometry
Returns
-------
list of openmc.nuclide.Nuclide
Nuclides in the geometry
"""
nuclides = {}
materials = self.get_all_materials()
@ -93,8 +118,15 @@ class Geometry(object):
return nuclides
def get_all_materials(self):
"""Return all materials assigned to a cell
Returns
-------
list of openmc.material.Material
Materials in the geometry
"""
material_cells = self.get_all_material_cells()
materials = set()
@ -104,9 +136,7 @@ class Geometry(object):
return list(materials)
def get_all_material_cells(self):
all_cells = self.get_all_cells()
material_cells = set()
@ -116,16 +146,21 @@ class Geometry(object):
return list(material_cells)
def get_all_material_universes(self):
"""Return all universes composed of at least one non-fill cell
Returns
-------
list of openmc.universe.Universe
Universes with non-fill cells
"""
all_universes = self.get_all_universes()
material_universes = set()
for universe_id, universe in all_universes.items():
cells = universe._cells
for cell_id, cell in cells.items():
if cell._type == 'normal':
material_universes.add(universe)
@ -133,33 +168,35 @@ class Geometry(object):
return list(material_universes)
class GeometryFile(object):
"""Geometry file used for an OpenMC simulation. Corresponds directly to the
geometry.xml input file.
Attributes
----------
geometry : Geometry
The geometry to be used
"""
def __init__(self):
# Initialize GeometryFile class attributes
self._geometry = None
self._geometry_file = ET.Element("geometry")
@property
def geometry(self):
return self._geometry
@geometry.setter
def geometry(self, geometry):
if not isinstance(geometry, Geometry):
msg = 'Unable to set the Geometry to {0} for the GeometryFile ' \
'since it is not a Geometry object'.format(geometry)
raise ValueError(msg)
check_type('the geometry', geometry, Geometry)
self._geometry = geometry
def export_to_xml(self):
"""Create a geometry.xml file that can be used for a simulation.
"""
root_universe = self._geometry._root_universe
root_universe.create_xml_subelement(self._geometry_file)
@ -168,7 +205,7 @@ class GeometryFile(object):
sort_xml_elements(self._geometry_file)
clean_xml_indentation(self._geometry_file)
# Write the XML Tree to the materials.xml file
# Write the XML Tree to the geometry.xml file
tree = ET.ElementTree(self._geometry_file)
tree.write("geometry.xml", xml_declaration=True,
encoding='utf-8', method="xml")

View file

@ -1,10 +1,14 @@
from collections import MappingView
from collections import Iterable
from copy import deepcopy
from numbers import Real, Integral
import warnings
from xml.etree import ElementTree as ET
import sys
if sys.version_info[0] >= 3:
basestring = str
import openmc
from openmc.checkvalue import *
from openmc.checkvalue import check_type, check_value, check_greater_than
from openmc.clean_xml import *
@ -14,6 +18,7 @@ MATERIAL_IDS = []
# A static variable for auto-generated Material IDs
AUTO_MATERIAL_ID = 10000
def reset_auto_material_id():
global AUTO_MATERIAL_ID, MATERIAL_IDS
AUTO_MATERIAL_ID = 10000
@ -23,20 +28,36 @@ def reset_auto_material_id():
# Units for density supported by OpenMC
DENSITY_UNITS = ['g/cm3', 'g/cc', 'kg/cm3', 'at/b-cm', 'at/cm3', 'sum']
# ENDF temperatures
ENDF_TEMPS = np.array([300, 600, 700, 900, 1200, 1500])
# ENDF ZAIDs
ENDF_ZAIDS = np.array(['70c', '71c', '72c', '73c', '74c'])
# Constant for density when not needed
NO_DENSITY = 99999.
class Material(object):
"""A material composed of a collection of nuclides/elements that can be assigned
to a region of space.
Parameters
----------
material_id : int, optional
Unique identifier for the material. If not specified, an identifier will
automatically be assigned.
name : str, optional
Name of the material. If not specified, the name will be the empty
string.
Attributes
----------
id : int
Unique identifier for the material
density : float
Density of the material (units defined separately)
density_units : str
Units used for `density`. Can be one of 'g/cm3', 'g/cc', 'kg/cm3',
'atom/b-cm', 'atom/cm3', or 'sum'.
"""
def __init__(self, material_id=None, name=''):
# Initialize class attributes
self.id = material_id
self.name = name
@ -62,95 +83,75 @@ class Material(object):
# If specified, this file will be used instead of composition values
self._distrib_otf_file = None
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def density(self):
return self._density
@property
def density_units(self):
return self._density_units
@property
def convert_to_distrib_comps(self):
return self._convert_to_distrib_comps
@property
def distrib_otf_file(self):
return self._distrib_otf_file
@id.setter
def id(self, material_id):
global AUTO_MATERIAL_ID, MATERIAL_IDS
# If the Material already has an ID, remove it from global list
if hasattr(self, '_id') and not self._id is None:
if hasattr(self, '_id') and self._id is not None:
MATERIAL_IDS.remove(self._id)
if material_id is None:
self._id = AUTO_MATERIAL_ID
MATERIAL_IDS.append(AUTO_MATERIAL_ID)
AUTO_MATERIAL_ID += 1
# Check that the ID is an integer and wasn't already used
elif not is_integer(material_id):
msg = 'Unable to set a non-integer Material ' \
'ID {0}'.format(material_id)
raise ValueError(msg)
elif material_id in MATERIAL_IDS:
msg = 'Unable to set Material ID to {0} since a Material with ' \
'this ID was already initialized'.format(material_id)
raise ValueError(msg)
elif material_id < 0:
msg = 'Unable to set Material ID to {0} since it must be a ' \
'non-negative integer'.format(material_id)
raise ValueError(msg)
else:
check_type('material ID', material_id, Integral)
if material_id in MATERIAL_IDS:
msg = 'Unable to set Material ID to {0} since a Material with ' \
'this ID was already initialized'.format(material_id)
raise ValueError(msg)
check_greater_than('material ID', material_id, 0)
self._id = material_id
MATERIAL_IDS.append(material_id)
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Material ID={0} with a non-string ' \
'value {1}'.format(self._id, name)
raise ValueError(msg)
else:
self._name = name
check_type('name for Material ID={0}'.format(self._id),
name, basestring)
self._name = name
def set_density(self, units, density=NO_DENSITY):
"""Set the density of the material
if not is_float(density):
msg = 'Unable to set the density for Material ID={0} to a ' \
'non-floating point value {1}'.format(self._id, density)
raise ValueError(msg)
Parameters
----------
units : str
Physical units of density
density : float, optional
Value of the density. Must be specified unless units is given as
'sum'.
elif not units in DENSITY_UNITS:
msg = 'Unable to set the density for Material ID={0} with ' \
'units {1}'.format(self._id, units)
raise ValueError(msg)
"""
check_type('the density for Material ID={0}'.format(self._id),
density, Real)
check_value('density units', units, DENSITY_UNITS)
if density == NO_DENSITY and units is not 'sum':
msg = 'Unable to set the density Material ID={0} ' \
@ -161,45 +162,52 @@ class Material(object):
self._density = density
self._density_units = units
@distrib_otf_file.setter
def distrib_otf_file(self, filename):
# TODO: remove this when distributed materials are merged
warnings.warn('This feature is not yet implemented in a release ' \
warnings.warn('This feature is not yet implemented in a release '
'version of openmc')
if not is_string(filename) and not filename is None:
if not isinstance(filename, basestring) and filename is not None:
msg = 'Unable to add OTF material file to Material ID={0} with a ' \
'non-string name {1}'.format(self._id, filename)
raise ValueError(msg)
self._distrib_otf_file = filename
@convert_to_distrib_comps.setter
def convert_to_distrib_comps(self):
# TODO: remove this when distributed materials are merged
warnings.warn('This feature is not yet implemented in a release ' \
warnings.warn('This feature is not yet implemented in a release '
'version of openmc')
self._convert_to_distrib_comps = True
def add_nuclide(self, nuclide, percent, percent_type='ao'):
"""Add a nuclide to the material
Parameters
----------
nuclide : str or openmc.nuclide.Nuclide
Nuclide to add
percent : float
Atom or weight percent
percent_type : str
'ao' for atom percent and 'wo' for weight percent
"""
if not isinstance(nuclide, (openmc.Nuclide, str)):
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
'non-Nuclide value {1}'.format(self._id, nuclide)
raise ValueError(msg)
elif not is_float(percent):
elif not isinstance(percent, Real):
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
'non-floating point value {1}'.format(self._id, percent)
raise ValueError(msg)
elif not percent_type in ['ao', 'wo', 'at/g-cm']:
elif percent_type not in ['ao', 'wo', 'at/g-cm']:
msg = 'Unable to add a Nuclide to Material ID={0} with a ' \
'percent type {1}'.format(self._id, percent_type)
raise ValueError(msg)
@ -213,8 +221,15 @@ class Material(object):
self._nuclides[nuclide._name] = (nuclide, percent, percent_type)
def remove_nuclide(self, nuclide):
"""Remove a nuclide from the material
Parameters
----------
nuclide : openmc.nuclide.Nuclide
Nuclide to remove
"""
if not isinstance(nuclide, openmc.Nuclide):
msg = 'Unable to remove a Nuclide {0} in Material ID={1} ' \
@ -225,20 +240,31 @@ class Material(object):
if nuclide._name in self._nuclides:
del self._nuclides[nuclide._name]
def add_element(self, element, percent, percent_type='ao'):
"""Add a natural element to the material
Parameters
----------
element : openmc.element.Element
Element to add
percent : float
Atom or weight percent
percent_type : str
'ao' for atom percent and 'wo' for weight percent
"""
if not isinstance(element, openmc.Element):
msg = 'Unable to add an Element to Material ID={0} with a ' \
'non-Element value {1}'.format(self._id, element)
raise ValueError(msg)
if not is_float(percent):
if not isinstance(percent, Real):
msg = 'Unable to add an Element to Material ID={0} with a ' \
'non-floating point value {1}'.format(self._id, percent)
raise ValueError(msg)
if not percent_type in ['ao', 'wo']:
if percent_type not in ['ao', 'wo']:
msg = 'Unable to add an Element to Material ID={0} with a ' \
'percent type {1}'.format(self._id, percent_type)
raise ValueError(msg)
@ -248,36 +274,58 @@ class Material(object):
self._elements[element._name] = (element, percent, percent_type)
def remove_element(self, element):
"""Remove a natural element from the material
Parameters
----------
element : openmc.element.Element
Element to remove
"""
# If the Material contains the Element, delete it
if element._name in self._elements:
del self._elements[element._name]
def add_s_alpha_beta(self, name, xs):
r"""Add an :math:`S(\alpha,\beta)` table to the material
if not is_string(name):
Parameters
----------
name : str
Name of the :math:`S(\alpha,\beta)` table
xs : str
Cross section identifier, e.g. '71t'
"""
if not isinstance(name, basestring):
msg = 'Unable to add an S(a,b) table to Material ID={0} with a ' \
'non-string table name {1}'.format(self._id, name)
raise ValueError(msg)
if not is_string(xs):
if not isinstance(xs, basestring):
msg = 'Unable to add an S(a,b) table to Material ID={0} with a ' \
'non-string cross-section identifier {1}'.format(self._id, xs)
raise ValueError(msg)
self._sab.append((name, xs))
def make_isotropic_in_lab(self):
for nuclide_name in self._nuclides:
self._nuclides[nuclide_name][0].make_isotropic_in_lab()
def get_all_nuclides(self):
"""Returns all nuclides in the material
Returns
-------
nuclides : dict
Dictionary whose keys are nuclide names and values are 2-tuples of
(nuclide, density)
"""
nuclides = {}
@ -288,9 +336,7 @@ class Material(object):
return nuclides
def _repr__(self):
string = 'Material\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
@ -322,9 +368,7 @@ class Material(object):
return string
def get_nuclide_xml(self, nuclide, distrib=False):
def _get_nuclide_xml(self, nuclide, distrib=False):
xml_element = ET.Element("nuclide")
xml_element.set("name", nuclide[0]._name)
@ -334,7 +378,7 @@ class Material(object):
else:
xml_element.set("wo", str(nuclide[1]))
if not nuclide[0]._xs is None:
if nuclide[0]._xs is not None:
xml_element.set("xs", nuclide[0]._xs)
if not nuclide[0]._scattering is None:
@ -342,9 +386,7 @@ class Material(object):
return xml_element
def get_element_xml(self, element, distrib=False):
def _get_element_xml(self, element, distrib=False):
xml_element = ET.Element("element")
xml_element.set("name", str(element[0]._name))
@ -356,28 +398,31 @@ class Material(object):
return xml_element
def get_nuclides_xml(self, nuclides, distrib=False):
def _get_nuclides_xml(self, nuclides, distrib=False):
xml_elements = []
for nuclide in nuclides.values():
xml_elements.append(self.get_nuclide_xml(nuclide, distrib))
xml_elements.append(self._get_nuclide_xml(nuclide, distrib))
return xml_elements
def get_elements_xml(self, elements, distrib=False):
def _get_elements_xml(self, elements, distrib=False):
xml_elements = []
for element in elements.values():
xml_elements.append(self.get_element_xml(element, distrib))
xml_elements.append(self._get_element_xml(element, distrib))
return xml_elements
def get_material_xml(self):
"""Return XML representation of the material
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing material data
"""
# Create Material XML element
element = ET.Element("material")
@ -393,19 +438,17 @@ class Material(object):
subelement.set("units", self._density_units)
if not self._convert_to_distrib_comps:
# Create nuclide XML subelements
subelements = self.get_nuclides_xml(self._nuclides)
subelements = self._get_nuclides_xml(self._nuclides)
for subelement in subelements:
element.append(subelement)
# Create element XML subelements
subelements = self.get_elements_xml(self._elements)
subelements = self._get_elements_xml(self._elements)
for subelement in subelements:
element.append(subelement)
else:
subelement = ET.SubElement(element, "compositions")
comps = []
@ -418,29 +461,24 @@ class Material(object):
raise ValueError(msg)
comps.append(per)
if self._distrib_otf_file is None:
# Create values and units subelements
subsubelement = ET.SubElement(subelement, "values")
subsubelement.text = ' '.join([str(c) for c in comps])
subsubelement = ET.SubElement(subelement, "units")
subsubelement.text = dist_per_type
else:
# Specify the materials file
subsubelement = ET.SubElement(subelement, "otf_file_path")
subsubelement.text = self._distrib_otf_file
# Create nuclide XML subelements
subelements = self.get_nuclides_xml(self._nuclides, distrib=True)
for subelement_nuc in subelements:
subelement.append(subelement_nuc)
# Create element XML subelements
subelements = self.get_elements_xml(self._elements, distrib=True)
subelements = self._get_elements_xml(self._elements, distrib=True)
for subelement_ele in subelements:
subelement.append(subelement_ele)
@ -454,31 +492,41 @@ class Material(object):
class MaterialsFile(object):
"""Materials file used for an OpenMC simulation. Corresponds directly to the
materials.xml input file.
Attributes
----------
default_xs : str
The default cross section identifier applied to a nuclide when none is
specified
"""
def __init__(self):
# Initialize MaterialsFile class attributes
self._materials = []
self._default_xs = None
self._materials_file = ET.Element("materials")
@property
def default_xs(self):
return self._default_xs
@default_xs.setter
def default_xs(self, xs):
if not is_string(xs):
msg = 'Unable to set default xs to a non-string value'.format(xs)
raise ValueError(msg)
check_type('default xs', xs, basestring)
self._default_xs = xs
def add_material(self, material):
"""Add a material to the file.
Parameters
----------
material : Material
Material to add
"""
if not isinstance(material, Material):
msg = 'Unable to add a non-Material {0} to the ' \
@ -487,19 +535,33 @@ class MaterialsFile(object):
self._materials.append(material)
def add_materials(self, materials):
"""Add multiple materials to the file.
if not isinstance(materials, (tuple, list, MappingView)):
Parameters
----------
materials : tuple or list of Material
Materials to add
"""
if not isinstance(materials, Iterable):
msg = 'Unable to create OpenMC materials.xml file from {0} which ' \
'is not a Python tuple/list'.format(materials)
'is not iterable'.format(materials)
raise ValueError(msg)
for material in materials:
self.add_material(material)
def remove_material(self, material):
"""Remove a material from the file
def remove_materials(self, material):
Parameters
----------
material : Material
Material to remove
"""
if not isinstance(material, Material):
msg = 'Unable to remove a non-Material {0} from the ' \
@ -509,26 +571,25 @@ class MaterialsFile(object):
self._materials.remove(material)
def make_isotropic_in_lab(self):
for material in self._materials:
materials.make_isotropic_in_lab()
def create_material_subelements(self):
def _create_material_subelements(self):
subelement = ET.SubElement(self._materials_file, "default_xs")
if not self._default_xs is None:
if self._default_xs is not None:
subelement.text = self._default_xs
for material in self._materials:
xml_element = material.get_material_xml()
self._materials_file.append(xml_element)
def export_to_xml(self):
"""Create a materials.xml file that can be used for a simulation.
self.create_material_subelements()
"""
self._create_material_subelements()
# Clean the indentation in the file to be user-readable
sort_xml_elements(self._materials_file)

View file

@ -1,8 +1,14 @@
from collections import Iterable
import copy
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
from openmc.checkvalue import *
from openmc.checkvalue import (check_type, check_length, check_value,
check_greater_than)
if sys.version_info[0] >= 3:
basestring = str
# "Static" variable for auto-generated and Mesh IDs
AUTO_MESH_ID = 10000
@ -14,9 +20,37 @@ def reset_auto_mesh_id():
class Mesh(object):
"""A structured Cartesian mesh in two or three dimensions
Parameters
----------
mesh_id : int
Unique identifier for the mesh
name : str
Name of the mesh
Attributes
----------
id : int
Unique identifier for the mesh
name : str
Name of the mesh
type : str
Type of the mesh
dimension : Iterable of int
The number of mesh cells in each direction.
lower_left : Iterable of float
The lower-left corner of the structured mesh. If only two coordinate are
given, it is assumed that the mesh is an x-y mesh.
upper_right : Iterable of float
The upper-right corner of the structrued mesh. If only two coordinate
are given, it is assumed that the mesh is an x-y mesh.
width : Iterable of float
The width of mesh cells in each direction.
"""
def __init__(self, mesh_id=None, name=''):
# Initialize Mesh class attributes
self.id = mesh_id
self.name = name
@ -26,9 +60,7 @@ class Mesh(object):
self._upper_right = None
self._width = None
def __eq__(self, mesh2):
# Check type
if self._type != mesh2._type:
return False
@ -49,14 +81,11 @@ class Mesh(object):
else:
return True
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is the first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._id = self._id
clone._name = self._name
@ -74,201 +103,87 @@ class Mesh(object):
else:
return existing
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def type(self):
return self._type
@property
def dimension(self):
return self._dimension
@property
def lower_left(self):
return self._lower_left
@property
def upper_right(self):
return self._upper_right
@property
def width(self):
return self._width
@property
def num_mesh_cells(self):
return np.prod(self._dimension)
@id.setter
def id(self, mesh_id):
if mesh_id is None:
global AUTO_MESH_ID
self._id = AUTO_MESH_ID
AUTO_MESH_ID += 1
# Check that the ID is an integer and wasn't already used
elif not is_integer(mesh_id):
msg = 'Unable to set a non-integer Mesh ID {0}'.format(mesh_id)
raise ValueError(msg)
elif mesh_id < 0:
msg = 'Unable to set Mesh ID to {0} since it must be a ' \
'non-negative integer'.format(mesh_id)
raise ValueError(msg)
else:
check_type('mesh ID', mesh_id, Integral)
check_greater_than('mesh ID', mesh_id, 0)
self._id = mesh_id
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Mesh ID={0} with a non-string ' \
'value {1}'.format(self._id, name)
raise ValueError(msg)
else:
self._name = name
check_type('name for mesh ID={0}'.format(self._id), name, basestring)
self._name = name
@type.setter
def type(self, type):
if not is_string(type):
msg = 'Unable to set Mesh ID={0} for type {1} which is not ' \
'a string'.format(self._id, type)
raise ValueError(msg)
elif not type in ['rectangular', 'hexagonal']:
msg = 'Unable to set Mesh ID={0} for type {1} which since ' \
'only rectangular and hexagonal meshes are ' \
'supported '.format(self._id, type)
raise ValueError(msg)
self._type = type
def type(self, meshtype):
check_type('type for mesh ID={0}'.format(self._id),
meshtype, basestring)
check_value('type for mesh ID={0}'.format(self._id),
meshtype, ['rectangular', 'hexagonal'])
self._type = meshtype
@dimension.setter
def dimension(self, dimension):
if not isinstance(dimension, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with dimension {1} which is ' \
'not a Python list, tuple or NumPy ' \
'array'.format(self._id, dimension)
raise ValueError(msg)
elif len(dimension) != 2 and len(dimension) != 3:
msg = 'Unable to set Mesh ID={0} with dimension {1} since it ' \
'must include 2 or 3 dimensions'.format(self._id, dimension)
raise ValueError(msg)
for dim in dimension:
if not is_integer(dim):
msg = 'Unable to set Mesh ID={0} with dimension {1} which ' \
'is a non-integer'.format(self._id, dim)
raise ValueError(msg)
check_type('mesh dimension', dimension, Iterable, Integral)
check_length('mesh dimension', dimension, 2, 3)
self._dimension = dimension
@lower_left.setter
def lower_left(self, lower_left):
if not isinstance(lower_left, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with lower_left {1} which is ' \
'not a Python list, tuple or NumPy ' \
'array'.format(self._id, lower_left)
raise ValueError(msg)
elif len(lower_left) != 2 and len(lower_left) != 3:
msg = 'Unable to set Mesh ID={0} with lower_left {1} since it ' \
'must include 2 or 3 dimensions'.format(self._id, lower_left)
raise ValueError(msg)
for coord in lower_left:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set Mesh ID={0} with lower_left {1} which ' \
'is neither neither an integer nor a floating point ' \
'value'.format(self._id, coord)
raise ValueError(msg)
check_type('mesh lower_left', lower_left, Iterable, Real)
check_length('mesh lower_left', lower_left, 2, 3)
self._lower_left = lower_left
@upper_right.setter
def upper_right(self, upper_right):
if not isinstance(upper_right, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
'is not a Python list, tuple or NumPy ' \
'array'.format(self._id, upper_right)
raise ValueError(msg)
if len(upper_right) != 2 and len(upper_right) != 3:
msg = 'Unable to set Mesh ID={0} with upper_right {1} since it ' \
'must include 2 or 3 dimensions'.format(self._id, upper_right)
raise ValueError(msg)
for coord in upper_right:
if not is_integer(coord) and not is_float(coord):
msg = 'Unable to set Mesh ID={0} with upper_right {1} which ' \
'is neither an integer nor a floating point ' \
'value'.format(self._id, coord)
raise ValueError(msg)
check_type('mesh upper_right', upper_right, Iterable, Real)
check_length('mesh upper_right', upper_right, 2, 3)
self._upper_right = upper_right
@width.setter
def width(self, width):
if not width is None:
if not isinstance(width, (tuple, list, np.ndarray)):
msg = 'Unable to set Mesh ID={0} with width {1} which ' \
'is not a Python list, tuple or NumPy ' \
'array'.format(self._id, width)
raise ValueError(msg)
if len(width) != 2 and len(width) != 3:
msg = 'Unable to set Mesh ID={0} with width {1} since it must ' \
'include 2 or 3 dimensions'.format(self._id, width)
raise ValueError(msg)
for dim in width:
if not is_integer(dim) and not is_float(dim):
msg = 'Unable to set Mesh ID={0} with width {1} which is ' \
'neither an integer nor a floating point ' \
'value'.format(self._id, width)
raise ValueError(msg)
check_type('mesh width', width, Iterable, Real)
check_length('mesh width', width, 2, 3)
self._width = width
def __repr__(self):
string = 'Mesh\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
@ -279,53 +194,32 @@ class Mesh(object):
string += '{0: <16}{1}{2}\n'.format('\tPixels', '=\t', self._width)
return string
def get_mesh_xml(self):
"""Return XML representation of the mesh
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing mesh data
"""
element = ET.Element("mesh")
element.set("id", str(self._id))
element.set("type", self._type)
if len(self._dimension) == 2:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1}'.format(self._dimension[0],
self._dimension[1])
else:
subelement = ET.SubElement(element, "dimension")
subelement.text = '{0} {1} {2}'.format(self._dimension[0],
self._dimension[1],
self._dimension[2])
subelement = ET.SubElement(element, "dimension")
subelement.text = ' '.join(map(str, self._dimension))
if len(self._lower_left) == 2:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1}'.format(self._lower_left[0],
self._lower_left[1])
else:
subelement = ET.SubElement(element, "lower_left")
subelement.text = '{0} {1} {2}'.format(self._lower_left[0],
self._lower_left[1],
self._lower_left[2])
subelement = ET.SubElement(element, "lower_left")
subelement.text = ' '.join(map(str, self._lower_left))
if not self._upper_right is None:
if len(self._upper_right) == 2:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1}'.format(self._upper_right[0],
self._upper_right[1])
else:
subelement = ET.SubElement(element, "upper_right")
subelement.text = '{0} {1} {2}'.format(self._upper_right[0],
self._upper_right[1],
self._upper_right[2])
if self._upper_right is not None:
subelement = ET.SubElement(element, "upper_right")
subelement.text = ' '.join(map(str, self._upper_right))
if not self._width is None:
if len(self._width) == 2:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1}'.format(self._width[0],
self._width[1])
else:
subelement = ET.SubElement(element, "width")
subelement.text = '{0} {1} {2}'.format(self._width[0],
self._width[1],
self._width[2])
if self._width is not None:
subelement = ET.SubElement(element, "width")
subelement.text = ' '.join(map(str, self._width))
return element
return element

View file

@ -1,10 +1,35 @@
from openmc.checkvalue import *
from numbers import Integral
import sys
from openmc.checkvalue import check_type
if sys.version_info[0] >= 3:
basestring = str
class Nuclide(object):
"""A nuclide that can be used in a material.
Parameters
----------
name : str
Name of the nuclide, e.g. U-235
xs : str
Cross section identifier, e.g. 71c
Attributes
----------
name : str
Name of the nuclide, e.g. U-235
xs : str
Cross section identifier, e.g. 71c
zaid : int
1000*(atomic number) + mass number. As an example, the zaid of U-235
would be 92235.
"""
def __init__(self, name='', xs=None):
# Initialize class attributes
self._name = ''
self._xs = None
@ -14,12 +39,10 @@ class Nuclide(object):
# Set the Material class attributes
self.name = name
if not xs is None:
if xs is not None:
self.xs = xs
def __eq__(self, nuclide2):
# Check type
if not isinstance(nuclide2, Nuclide):
return False
@ -35,63 +58,41 @@ class Nuclide(object):
else:
return True
def __hash__(self):
return hash((self._name, self._xs))
@property
def name(self):
return self._name
@property
def xs(self):
return self._xs
@property
def zaid(self):
return self._zaid
@property
def scattering(self):
return self._scattering
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Nuclide with a non-string ' \
'value {0}'.format(name)
raise ValueError(msg)
check_type('name', name, basestring)
self._name = name
@xs.setter
def xs(self, xs):
if not is_string(xs):
msg = 'Unable to set cross-section identifier xs for Nuclide ' \
'with a non-string value {0}'.format(xs)
raise ValueError(msg)
check_type('cross-section identifier', xs, basestring)
self._xs = xs
@zaid.setter
def zaid(self, zaid):
if not is_integer(zaid):
msg = 'Unable to set zaid for Nuclide ' \
'with a non-integer {0}'.format(zaid)
raise ValueError(msg)
check_type('zaid', zaid, Integral)
self._zaid = zaid
<<<<<<< HEAD
@scattering.setter
def scattering(self, scattering):
@ -104,13 +105,17 @@ class Nuclide(object):
self._scattering = scattering
=======
>>>>>>> develop
def __repr__(self):
string = 'Nuclide - {0}\n'.format(self._name)
string += '{0: <16}{1}{2}\n'.format('\tXS', '=\t', self._xs)
if self._zaid is not None:
string += '{0: <16}{1}{2}\n'.format('\tZAID', '=\t', self._zaid)
<<<<<<< HEAD
if self._scattering is not None:
string += '{0: <16}{1}{2}\n'.format('\tscattering', '=\t',
self._scattering)
=======
>>>>>>> develop
return string

View file

@ -62,8 +62,20 @@ OPENMC_LATTICES = {}
OPENCG_LATTICES = {}
def get_opencg_material(openmc_material):
"""Return an OpenCG material corresponding to an OpenMC material.
Parameters
----------
openmc_material : openmc.material.Material
OpenMC material
Returns
-------
opencg_material : opencg.Material
Equivalent OpenCG material
"""
if not isinstance(openmc_material, openmc.Material):
msg = 'Unable to create an OpenCG Material from {0} ' \
@ -91,6 +103,19 @@ def get_opencg_material(openmc_material):
def get_openmc_material(opencg_material):
"""Return an OpenMC material corresponding to an OpenCG material.
Parameters
----------
opencg_material : opencg.Material
OpenCG material
Returns
-------
openmc_material : openmc.material.Material
Equivalent OpenMC material
"""
if not isinstance(opencg_material, opencg.Material):
msg = 'Unable to create an OpenMC Material from {0} ' \
@ -118,6 +143,25 @@ def get_openmc_material(opencg_material):
def is_opencg_surface_compatible(opencg_surface):
"""Determine whether OpenCG surface is compatible with OpenMC geometry.
A surface is considered compatible if there is a one-to-one correspondence
between OpenMC and OpenCG surface types. Note that some OpenCG surfaces,
e.g. SquarePrism, do not have a one-to-one correspondence with OpenMC
surfaces but can still be converted into an equivalent collection of OpenMC
surfaces.
Parameters
----------
opencg_surface : opencg.Surface
OpenCG surface
Returns
-------
bool
Whether OpenCG surface is compatible with OpenMC
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to check if OpenCG Surface is compatible' \
@ -132,6 +176,19 @@ def is_opencg_surface_compatible(opencg_surface):
def get_opencg_surface(openmc_surface):
"""Return an OpenCG surface corresponding to an OpenMC surface.
Parameters
----------
openmc_surface : openmc.surface.Surface
OpenMC surface
Returns
-------
opencg_surface : opencg.Surface
Equivalent OpenCG surface
"""
if not isinstance(openmc_surface, openmc.Surface):
msg = 'Unable to create an OpenCG Surface from {0} ' \
@ -205,6 +262,19 @@ def get_opencg_surface(openmc_surface):
def get_openmc_surface(opencg_surface):
"""Return an OpenMC surface corresponding to an OpenCG surface.
Parameters
----------
opencg_surface : opencg.Surface
OpenCG surface
Returns
-------
openmc_surface : openmc.surface.Surface
Equivalent OpenMC surface
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to create an OpenMC Surface from {0} which ' \
@ -269,7 +339,6 @@ def get_openmc_surface(opencg_surface):
'Surface type in OpenMC'.format(opencg_surface._type)
raise ValueError(msg)
# Add the OpenMC Surface to the global collection of all OpenMC Surfaces
OPENMC_SURFACES[surface_id] = openmc_surface
@ -280,6 +349,23 @@ def get_openmc_surface(opencg_surface):
def get_compatible_opencg_surfaces(opencg_surface):
"""Generate OpenCG surfaces that are compatible with OpenMC equivalent to an
OpenCG surface that is not compatible. For example, this method may be used
to convert a ZSquarePrism OpenCG surface into a collection of equivalent
XPlane and YPlane OpenCG surfaces.
Parameters
----------
opencg_surface : opencg.Surface
OpenCG surface that is incompatible with OpenMC
Returns
-------
surfaces : list of opencg.Surface
Collection of surfaces equivalent to the original one but compatible
with OpenMC
"""
if not isinstance(opencg_surface, opencg.Surface):
msg = 'Unable to create an OpenMC Surface from {0} which ' \
@ -349,6 +435,19 @@ def get_compatible_opencg_surfaces(opencg_surface):
def get_opencg_cell(openmc_cell):
"""Return an OpenCG cell corresponding to an OpenMC cell.
Parameters
----------
openmc_cell : openmc.universe.Cell
OpenMC cell
Returns
-------
opencg_cell : opencg.Cell
Equivalent OpenCG cell
"""
if not isinstance(openmc_cell, openmc.Cell):
msg = 'Unable to create an OpenCG Cell from {0} which ' \
@ -398,7 +497,26 @@ def get_opencg_cell(openmc_cell):
def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
"""Generate OpenCG cells that are compatible with OpenMC equivalent to an OpenCG
cell that is not compatible.
Parameters
----------
opencg_cell : opencg.Cell
OpenCG cell
opencg_surface : opencg.Surface
OpenCG surface that causes the incompatibility, e.g. an instance of
XSquarePrism
halfspace : {-1, 1}
Which halfspace defined by the surface is contained in the cell
Returns
-------
compatible_cells : list of opencg.Cell
Collection of cells equivalent to the original one but compatible with
OpenMC
"""
if not isinstance(opencg_cell, opencg.Cell):
msg = 'Unable to create compatible OpenMC Cell from {0} which ' \
'is not an OpenCG Cell'.format(opencg_cell)
@ -409,7 +527,7 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
'not an OpenCG Surface'.format(opencg_surface)
raise ValueError(msg)
elif not halfspace in [-1, +1]:
elif halfspace not in [-1, +1]:
msg = 'Unable to create compatible Cell since {0}' \
'is not a +/-1 halfspace'.format(halfspace)
raise ValueError(msg)
@ -418,8 +536,8 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
compatible_cells = []
# SquarePrism Surfaces
if opencg_surface._type in ['x-squareprism',
'y-squareprism', 'z-squareprism']:
if opencg_surface._type in ['x-squareprism', 'y-squareprism',
'z-squareprism']:
# Get the compatible Surfaces (XPlanes and YPlanes)
compatible_surfaces = get_compatible_opencg_surfaces(opencg_surface)
@ -436,13 +554,11 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
# If Cell is outside SquarePrism, add "outside" of Surface halfspaces
else:
# Create 8 Cell clones to represent each of the disjoint planar
# Surface halfspace intersections
num_clones = 8
for clone_id in range(num_clones):
# Create a cloned OpenCG Cell with Surfaces compatible with OpenMC
clone = opencg_cell.clone()
compatible_cells.append(clone)
@ -500,6 +616,14 @@ def get_compatible_opencg_cells(opencg_cell, opencg_surface, halfspace):
def make_opencg_cells_compatible(opencg_universe):
"""Make all cells in an OpenCG universe compatible with OpenMC.
Parameters
----------
opencg_universe : opencg.Universe
Universe to check
"""
if not isinstance(opencg_universe, opencg.Universe):
msg = 'Unable to make compatible OpenCG Cells for {0} which ' \
@ -545,8 +669,20 @@ def make_opencg_cells_compatible(opencg_universe):
return
def get_openmc_cell(opencg_cell):
"""Return an OpenMC cell corresponding to an OpenCG cell.
Parameters
----------
opencg_cell : opencg.Cell
OpenCG cell
Returns
-------
openmc_cell : openmc.universe.Cell
Equivalent OpenMC cell
"""
if not isinstance(opencg_cell, opencg.Cell):
msg = 'Unable to create an OpenMC Cell from {0} which ' \
@ -597,8 +733,20 @@ def get_openmc_cell(opencg_cell):
return openmc_cell
def get_opencg_universe(openmc_universe):
"""Return an OpenCG universe corresponding to an OpenMC universe.
Parameters
----------
openmc_universe : openmc.universe.Universe
OpenMC universe
Returns
-------
opencg_universe : opencg.Universe
Equivalent OpenCG universe
"""
if not isinstance(openmc_universe, openmc.Universe):
msg = 'Unable to create an OpenCG Universe from {0} which ' \
@ -633,6 +781,19 @@ def get_opencg_universe(openmc_universe):
def get_openmc_universe(opencg_universe):
"""Return an OpenMC universe corresponding to an OpenCG universe.
Parameters
----------
opencg_universe : opencg.Universe
OpenCG universe
Returns
-------
openmc_universe : openmc.universe.Universe
Equivalent OpenMC universe
"""
if not isinstance(opencg_universe, opencg.Universe):
msg = 'Unable to create an OpenMC Universe from {0} which ' \
@ -670,6 +831,19 @@ def get_openmc_universe(opencg_universe):
def get_opencg_lattice(openmc_lattice):
"""Return an OpenCG lattice corresponding to an OpenMC lattice.
Parameters
----------
openmc_lattice : openmc.universe.Lattice
OpenMC lattice
Returns
-------
opencg_lattice : opencg.Lattice
Equivalent OpenCG lattice
"""
if not isinstance(openmc_lattice, openmc.Lattice):
msg = 'Unable to create an OpenCG Lattice from {0} which ' \
@ -701,7 +875,7 @@ def get_opencg_lattice(openmc_lattice):
lower_left = new_lower_left
# Initialize an empty array for the OpenCG nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)[::-1]), \
universe_array = np.ndarray(tuple(np.array(dimension)[::-1]),
dtype=opencg.Universe)
# Create OpenCG Universes for each unique nested Universe in this Lattice
@ -723,8 +897,8 @@ def get_opencg_lattice(openmc_lattice):
opencg_lattice.setUniverses(universe_array)
offset = np.array(lower_left, dtype=np.float64) - \
((np.array(pitch, dtype=np.float64) * \
np.array(dimension, dtype=np.float64))) / -2.0
((np.array(pitch, dtype=np.float64) *
np.array(dimension, dtype=np.float64))) / -2.0
opencg_lattice.setOffset(offset)
# Add the OpenMC Lattice to the global collection of all OpenMC Lattices
@ -737,6 +911,19 @@ def get_opencg_lattice(openmc_lattice):
def get_openmc_lattice(opencg_lattice):
"""Return an OpenMC lattice corresponding to an OpenCG lattice.
Parameters
----------
opencg_lattice : opencg.Lattice
OpenCG lattice
Returns
-------
openmc_lattice : openmc.universe.Lattice
Equivalent OpenMC lattice
"""
if not isinstance(opencg_lattice, opencg.Lattice):
msg = 'Unable to create an OpenMC Lattice from {0} which ' \
@ -756,7 +943,7 @@ def get_openmc_lattice(opencg_lattice):
universes = opencg_lattice._universes
# Initialize an empty array for the OpenMC nested Universes in this Lattice
universe_array = np.ndarray(tuple(np.array(dimension)), \
universe_array = np.ndarray(tuple(np.array(dimension)),
dtype=openmc.Universe)
# Create OpenMC Universes for each unique nested Universe in this Lattice
@ -773,11 +960,11 @@ def get_openmc_lattice(opencg_lattice):
universe_array[x][y][z] = unique_universes[universe_id]
# Reverse y-dimension in array to match ordering in OpenCG
universe_array = universe_array[:,::-1,:]
universe_array = universe_array[:, ::-1, :]
lower_left = np.array(offset, dtype=np.float64) + \
((np.array(width, dtype=np.float64) * \
np.array(dimension, dtype=np.float64))) / -2.0
((np.array(width, dtype=np.float64) *
np.array(dimension, dtype=np.float64))) / -2.0
openmc_lattice = openmc.RectLattice(lattice_id=lattice_id)
openmc_lattice.dimension = dimension
@ -795,6 +982,19 @@ def get_openmc_lattice(opencg_lattice):
def get_opencg_geometry(openmc_geometry):
"""Return an OpenCG geometry corresponding to an OpenMC geometry.
Parameters
----------
openmc_geometry : openmc.universe.Geometry
OpenMC geometry
Returns
-------
opencg_geometry : opencg.Geometry
Equivalent OpenCG geometry
"""
if not isinstance(openmc_geometry, openmc.Geometry):
msg = 'Unable to get OpenCG geometry from {0} which is ' \
@ -822,6 +1022,19 @@ def get_opencg_geometry(openmc_geometry):
def get_openmc_geometry(opencg_geometry):
"""Return an OpenMC geometry corresponding to an OpenCG geometry.
Parameters
----------
opencg_geometry : opencg.Geometry
OpenCG geometry
Returns
-------
openmc_geometry : openmc.universe.Geometry
Equivalent OpenMC geometry
"""
if not isinstance(opencg_geometry, opencg.Geometry):
msg = 'Unable to get OpenMC geometry from {0} which is ' \
@ -849,8 +1062,8 @@ def get_openmc_geometry(opencg_geometry):
# Make the entire geometry "compatible" before assigning auto IDs
universes = opencg_geometry.getAllUniverses()
for universe_id, universe in universes.items():
if not isinstance(universe, opencg.Lattice):
make_opencg_cells_compatible(universe)
if not isinstance(universe, opencg.Lattice):
make_opencg_cells_compatible(universe)
opencg_geometry.assignAutoIds()

View file

@ -1,9 +1,44 @@
#!/usr/bin/env python
import struct
class Particle(object):
"""Information used to restart a specific particle that caused a simulation to
fail.
Parameters
----------
filename : str
Path to the particle restart file
Attributes
----------
filetype : int
Integer indicating the file type
revision : int
Revision of the particle restart format
current_batch : int
The batch containing the particle
gen_per_batch : int
Number of generations per batch
current_gen : int
The generation containing the particle
n_particles : int
Number of particles per generation
run_mode : int
Type of simulation (criticality or fixed source)
id : long
Identifier of the particle
weight : float
Weight of the particle
energy : float
Energy of the particle in MeV
xyz : list of float
Position of the particle
uvw : list of float
Directional cosines of the particle
"""
def __init__(self, filename):
if filename.endswith('.h5'):
import h5py

View file

@ -1,14 +1,21 @@
from collections import Iterable
from numbers import Real, Integral
from xml.etree import ElementTree as ET
import sys
import numpy as np
from openmc.checkvalue import *
from openmc.clean_xml import *
from openmc.checkvalue import (check_type, check_value, check_length,
check_greater_than, check_less_than)
if sys.version_info[0] >= 3:
basestring = str
# A static variable for auto-generated Plot IDs
AUTO_PLOT_ID = 10000
def reset_auto_plot_id():
global AUTO_PLOT_ID
AUTO_PLOT_ID = 10000
@ -18,9 +25,50 @@ BASES = ['xy', 'xz', 'yz']
class Plot(object):
"""Definition of a finite region of space to be plotted, either as a slice plot
in two dimensions or as a voxel plot in three dimensions.
Parameters
----------
plot_id : int
Unique identifier for the plot
name : str
Name of the plot
Attributes
----------
id : int
Unique identifier
name : str
Name of the plot
width : Iterable of float
Width of the plot in each basis direction
pixels : Iterable of int
Number of pixels to use in each basis direction
origin : tuple or list of ndarray
Origin (center) of the plot
filename :
Path to write the plot to
color : {'cell', 'mat'}
Indicate whether the plot should be colored by cell or by material
type : {'slice', 'voxel'}
The type of the plot
basis : {'xy', 'xz', 'yz'}
The basis directions for the plot
background : tuple or list of ndarray
Color of the background defined by RGB
mask_components : Iterable of int
Unique id numbers of the cells or materials to plot
mask_background : Iterable of int
Color to apply to all cells/materials not listed in mask_components
defined by RGB
col_spec : dict
Dictionary indicating that certain cells/materials (keys) should be
colored with a specific RGB (values)
"""
def __init__(self, plot_id=None, name=''):
# Initialize Plot class attributes
self.id = plot_id
self.name = name
@ -36,295 +84,139 @@ class Plot(object):
self._mask_background = None
self._col_spec = None
@property
def id(self):
return self._id
@property
def name(self):
return self._name
@property
def width(self):
return self._width
@property
def pixels(self):
return self._pixels
@property
def origin(self):
return self._origin
@property
def filename(self):
return self._filename
@property
def color(self):
return self._color
@property
def type(self):
return self._type
@property
def basis(self):
return self._basis
@property
def background(self):
return self._background
@property
def mask_componenets(self):
return self._mask_components
@property
def mask_background(self):
return self._mask_background
@property
def col_spec(self):
return self._col_spec
@id.setter
def id(self, plot_id):
if plot_id is None:
global AUTO_PLOT_ID
self._id = AUTO_PLOT_ID
AUTO_PLOT_ID += 1
# Check that the ID is an integer and wasn't already used
elif not is_integer(plot_id):
msg = 'Unable to set a non-integer Plot ID {0}'.format(plot_id)
raise ValueError(msg)
elif plot_id < 0:
msg = 'Unable to set Plot ID to {0} since it must be a ' \
'non-negative integer'.format(plot_id)
raise ValueError(msg)
else:
check_type('plot ID', plot_id, Integral)
check_greater_than('plot ID', plot_id, 0)
self._id = plot_id
@name.setter
def name(self, name):
if not is_string(name):
msg = 'Unable to set name for Plot ID={0} with a non-string ' \
'value {1}'.format(self._id, name)
raise ValueError(msg)
else:
self._name = name
check_type('plot name', name, basestring)
self._name = name
@width.setter
def width(self, width):
if not isinstance(width, (tuple, list, np.ndarray)):
msg = 'Unable to create Plot ID={0} with width {1} which is not ' \
'a Python tuple/list or NumPy array'.format(self._id, width)
raise ValueError(msg)
elif len(width) != 2 and len(width) != 3:
msg = 'Unable to create Plot ID={0} with width {1} since only 2D ' \
'and 3D plots are supported'.format(self._id, width)
raise ValueError(msg)
for dim in width:
if not is_integer(dim) and not is_float(dim):
msg = 'Unable to create Plot ID={0} with width {1} since ' \
'each element must be a floating point value or ' \
'integer'.format(self._id, width)
raise ValueError(msg)
check_type('plot width', width, Iterable, Real)
check_length('plot width', width, 2, 3)
self._width = width
@origin.setter
def origin(self, origin):
if not isinstance(origin, (tuple, list, np.ndarray)):
msg = 'Unable to create Plot ID={0} with origin {1} which is not ' \
'a Python tuple/list or NumPy array'.format(self._id, origin)
raise ValueError(msg)
elif len(origin) != 3:
msg = 'Unable to create Plot ID={0} with origin {1} since only ' \
'a 3D coordinate must be input'.format(self._id, origin)
raise ValueError(msg)
for dim in origin:
if not is_integer(dim) and not is_float(dim):
msg = 'Unable to create Plot ID={0} with origin {1} since ' \
'each element must be a floating point value or ' \
'integer'.format(self._id, origin)
raise ValueError(msg)
check_type('plot origin', origin, Iterable, Real)
check_length('plot origin', origin, 3)
self._origin = origin
@pixels.setter
def pixels(self, pixels):
if not isinstance(pixels, (tuple, list, np.ndarray)):
msg = 'Unable to create Plot ID={0} with pixels {1} which is not ' \
'a Python tuple/list or NumPy array'.format(self._id, pixels)
raise ValueError(msg)
elif len(pixels) != 2 and len(pixels) != 3:
msg = 'Unable to create Plot ID={0} with pixels {1} since ' \
'only 2D and 3D plots are supported'.format(self._id, pixels)
raise ValueError(msg)
check_type('plot pixels', pixels, Iterable, Integral)
check_length('plot pixels', pixels, 2, 3)
for dim in pixels:
if not is_integer(dim):
msg = 'Unable to create Plot ID={0} with pixel value {1} ' \
'which is not an integer'.format(self._id, dim)
raise ValueError(msg)
elif dim < 0:
msg = 'Unable to create Plot ID={0} with pixel value {1} ' \
'which is less than 0'.format(self._id, dim)
raise ValueError(msg)
check_greater_than('plot pixels', dim, 0)
self._pixels = pixels
@filename.setter
def filename(self, filename):
if not is_string(filename):
msg = 'Unable to create Plot ID={0} with filename {1} which is ' \
'not a string'.format(self._id, filename)
raise ValueError(msg)
check_type('filename', filename, basestring)
self._filename = filename
@color.setter
def color(self, color):
if not is_string(color):
msg = 'Unable to create Plot ID={0} with color {1} which is not ' \
'a string'.format(self._id, color)
raise ValueError(msg)
elif not color in ['cell', 'mat']:
msg = 'Unable to create Plot ID={0} with color {1} which is not ' \
'a cell or mat'.format(self._id, color)
raise ValueError(msg)
check_type('plot color', color, basestring)
check_value('plot color', color, ['cell', 'mat'])
self._color = color
@type.setter
def type(self, type):
if not is_string(type):
msg = 'Unable to create Plot ID={0} with type {1} which is not ' \
'a string'.format(self._id, type)
raise ValueError(msg)
elif not type in ['slice', 'voxel']:
msg = 'Unable to create Plot ID={0} with type {1} which is not ' \
'slice or voxel'.format(self._id, type)
raise ValueError(msg)
self._type = type
def type(self, plottype):
check_type('plot type', plottype, basestring)
check_value('plot type', plottype, ['slice', 'voxel'])
self._type = plottype
@basis.setter
def basis(self, basis):
if not is_string(basis):
msg = 'Unable to create Plot ID={0} with basis {1} which is not ' \
'a string'.format(self._id, basis)
raise ValueError(msg)
elif not basis in ['xy', 'xz', 'yz']:
msg = 'Unable to create Plot ID={0} with basis {1} which is not ' \
'xy, xz, or yz'.format(self._id, basis)
raise ValueError(msg)
check_type('plot basis', basis, basestring)
check_value('plot basis', basis, ['xy', 'xz', 'yz'])
self._basis = basis
@background.setter
def background(self, background):
if not isinstance(background, (tuple, list, np.ndarray)):
msg = 'Unable to create Plot ID={0} with background {1} ' \
'which is not a Python tuple/list or NumPy ' \
'array'.format(self._id, background)
raise ValueError(msg)
elif len(background) != 3:
msg = 'Unable to create Plot ID={0} with background {1} ' \
'which is not 3 integer RGB ' \
'values'.format(self._id, background)
raise ValueError(msg)
check_type('plot background', background, Iterable, Integral)
check_length('plot background', background, 3)
for rgb in background:
if not is_integer(rgb):
msg = 'Unable to create Plot ID={0} with background RGB ' \
'value {1} which is not an integer'.format(self._id, rgb)
raise ValueError(msg)
elif rgb < 0 or rgb > 255:
msg = 'Unable to create Plot ID={0} with background RGB value ' \
'{1} which is not between 0 and 255'.format(self._id, rgb)
raise ValueError(msg)
check_greater_than('plot background',rgb, 0, True)
check_less_than('plot background', rgb, 256)
self._background = background
@col_spec.setter
def col_spec(self, col_spec):
if not isinstance(col_spec, dict):
msg= 'Unable to create Plot ID={0} with col_spec parameter {1} ' \
'which is not a Python dictionary of IDs to ' \
'pixels'.format(self._id, col_spec)
raise ValueError(msg)
check_type('plot col_spec parameter', col_spec, dict, Integral)
for key in col_spec:
if not is_integer(key):
msg = 'Unable to create Plot ID={0} with col_spec ID {1} ' \
'which is not an integer'.format(self._id, key)
raise ValueError(msg)
elif key < 0:
if key < 0:
msg = 'Unable to create Plot ID={0} with col_spec ID {1} ' \
'which is less than 0'.format(self._id, key)
raise ValueError(msg)
elif not isinstance(col_spec[key], (tuple, list, np.ndarray)):
msg = 'Unable to create Plot ID={0} with col_spec RGB ' \
'values {1} which is not a Python tuple/list or NumPy ' \
'array'.format(self._id, col_spec[key])
elif not isinstance(col_spec[key], Iterable):
msg = 'Unable to create Plot ID={0} with col_spec RGB values' \
' {1} which is not iterable'.format(self._id, col_spec[key])
raise ValueError(msg)
elif len(col_spec[key]) != 3:
@ -335,63 +227,23 @@ class Plot(object):
self._col_spec = col_spec
@mask_componenets.setter
def mask_components(self, mask_components):
if not isinstance(mask_components, (list, tuple, np.ndarray)):
msg = 'Unable to create Plot ID={0} with mask components {1} ' \
'which is not a Python tuple/list or NumPy ' \
'array'.format(self._id, mask_components)
raise ValueError(msg)
check_type('plot mask_components', mask_components, Iterable, Integral)
for component in mask_components:
if not is_integer(component):
msg = 'Unable to create Plot ID={0} with mask component {1} ' \
'which is not an integer'.format(self._id, component)
raise ValueError(msg)
elif component < 0:
msg = 'Unable to create Plot ID={0} with mask component {1} ' \
'which is less than 0'.format(self._id, component)
raise ValueError(msg)
check_greater_than('plot mask_components', component, 0, True)
self._mask_components = mask_components
@mask_background.setter
def mask_background(self, mask_background):
if not isinstance(mask_background, (list, tuple, np.ndarray)):
msg = 'Unable to create Plot ID={0} with mask background {1} ' \
'which is not a Python tuple/list or NumPy ' \
'array'.format(self._id, mask_background)
raise ValueError(msg)
elif len(mask_background) != 3 and len(mask_background) != 0:
msg = 'Unable to create Plot ID={0} with mask background ' \
'{1} since 3 RGB values must be ' \
'input'.format(self._id, mask_background)
raise ValueError(msg)
check_type('plot mask background', mask_background, Iterable, Integral)
check_length('plot mask background', mask_background, 3)
for rgb in mask_background:
if not is_integer(rgb):
msg = 'Unable to create Plot ID={0} with mask background RGB ' \
'value {1} which is not an integer'.format(self._id, rgb)
raise ValueError(msg)
elif rgb < 0 or rgb > 255:
msg = 'Unable to create Plot ID={0} with mask bacground ' \
'RGB value {1} which is not between 0 and ' \
'255'.format(self._id, rgb)
raise ValueError(msg)
check_greater_than('plot mask background', rgb, 0, True)
check_less_than('plot mask background', rgb, 256)
self._mask_background = mask_background
def __repr__(self):
string = 'Plot\n'
string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id)
string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name)
@ -409,8 +261,15 @@ class Plot(object):
string += '{0: <16}{1}{2}\n'.format('\tCol Spec', '=\t', self._col_spec)
return string
def get_plot_xml(self):
"""Return XML representation of the plot
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing plot data
"""
element = ET.Element("plot")
element.set("id", str(self._id))
@ -422,64 +281,55 @@ class Plot(object):
element.set("basis", self._basis)
subelement = ET.SubElement(element, "origin")
text = ''
for coord in self._origin:
text += str(coord) + ' '
subelement.text = text.rstrip(' ')
subelement.text = ' '.join(map(str, self._origin))
subelement = ET.SubElement(element, "width")
text = ''
for dim in self._width:
text += str(dim) + ' '
subelement.text = text.rstrip(' ')
subelement.text = ' '.join(map(str, self._width))
subelement = ET.SubElement(element, "pixels")
text = ''
for dim in self._pixels:
text += str(dim) + ' '
subelement.text = text.rstrip(' ')
subelement.text = ' '.join(map(str, self._pixels))
if not self._mask_background is None:
if self._mask_background is not None:
subelement = ET.SubElement(element, "background")
text = ''
for rgb in self._background:
text += str(rgb) + ' '
subelement.text = text.rstrip(' ')
if not self._col_spec is None:
subelement.text = ' '.join(map(str, self._background))
if self._col_spec is not None:
for key in self._col_spec:
subelement = ET.SubElement(element, "col_spec")
subelement.set("id", '{0}'.format(key))
value = self._col_spec[key]
subelement.set("rgb",'{0} {1} {2}'.format(value[0],
value[1], value[2]))
subelement.set("id", str(key))
subelement.set("rgb", ' '.join(map(
str, self._col_spec[key])))
if not self._mask_components is None:
if self._mask_components is not None:
subelement = ET.SubElement(element, "mask")
text = ''
for id in self._mask_components:
text += str(id) + ' '
subelement.set("components", text.rstrip(' '))
rgb = self._mask_background
subelement.set("background", '{0} {1} {2}'.format(rgb[0],
rgb[1], rgb[2]))
subelement.set("components", ' '.join(map(
str, self._mask_components)))
subelement.set("background", ' '.join(map(
str, self._mask_background)))
return element
class PlotsFile(object):
"""Plots file used for an OpenMC simulation. Corresponds directly to the
plots.xml input file.
"""
def __init__(self):
# Initialize PlotsFile class attributes
self._plots = []
self._plots_file = ET.Element("plots")
def add_plot(self, plot):
"""Add a plot to the file.
Parameters
----------
plot : Plot
Plot to add
"""
if not isinstance(plot, Plot):
msg = 'Unable to add a non-Plot {0} to the PlotsFile'.format(plot)
@ -487,15 +337,20 @@ class PlotsFile(object):
self._plots.append(plot)
def remove_plot(self, plot):
"""Remove a plot from the file.
Parameters
----------
plot : Plot
Plot to remove
"""
self._plots.remove(plot)
def create_plot_subelements(self):
def _create_plot_subelements(self):
for plot in self._plots:
xml_element = plot.get_plot_xml()
if len(plot._name) > 0:
@ -503,10 +358,12 @@ class PlotsFile(object):
self._plots_file.append(xml_element)
def export_to_xml(self):
"""Create a plots.xml file that can be used by OpenMC.
self.create_plot_subelements()
"""
self._create_plot_subelements()
# Clean the indentation in the file to be user-readable
clean_xml_indentation(self._plots_file)

File diff suppressed because it is too large Load diff

View file

@ -13,17 +13,28 @@ if sys.version > '3':
class SourceSite(object):
"""A single source site produced from fission.
Attributes
----------
weight : float
Weight of the particle arising from the site
xyz : list of float
Cartesian coordinates of the site
uvw : list of float
Directional cosines for particles emerging from the site
E : float
Energy of the emerging particle in MeV
"""
def __init__(self):
self._weight = None
self._xyz = None
self._uvw = None
self._E = None
def __repr__(self):
string = 'SourceSite\n'
string += '{0: <16}{1}{2}\n'.format('\tweight', '=\t', self._weight)
string += '{0: <16}{1}{2}\n'.format('\tE', '=\t', self._E)
@ -31,32 +42,56 @@ class SourceSite(object):
string += '{0: <16}{1}{2}\n'.format('\t(u,v,w)', '=\t', self._uvw)
return string
@property
def weight(self):
return self._weight
@property
def xyz(self):
return self._xyz
@property
def uvw(self):
return self._uvw
@property
def E(self):
return self._E
class StatePoint(object):
"""State information on a simulation at a certain point in time (at the end of a
given batch). Statepoints can be used to analyze tally results as well as
restart a simulation.
Attributes
----------
k_combined : list
Combined estimator for k-effective and its uncertainty
n_particles : int
Number of particles per generation
n_batches : int
Number of batches
current_batch :
Number of batches simulated
results : bool
Indicate whether tally results have been read
source : ndarray of SourceSite
Array of source sites
with_summary : bool
Indicate whether statepoint data has been linked against a summary file
tallies : dict
Dictionary whose keys are tally IDs and whose values are Tally objects
tallies_present : bool
Indicate whether user-defined tallies are present
global_tallies : ndarray
Global tallies and their uncertainties
n_realizations : int
Number of tally realizations
"""
def __init__(self, filename):
if filename.endswith('.h5'):
import h5py
self._f = h5py.File(filename, 'r')
@ -79,7 +114,6 @@ class StatePoint(object):
# Read tally metadata
self._read_tallies()
def close(self):
self._f.close()
@ -128,7 +162,6 @@ class StatePoint(object):
return self._n_realizations
def _read_metadata(self):
# Read filetype
self._filetype = self._get_int(path='filetype')[0]
@ -169,9 +202,7 @@ class StatePoint(object):
if self._run_mode == 2:
self._read_criticality()
def _read_criticality(self):
# Read criticality information
if self._run_mode == 2:
@ -191,9 +222,7 @@ class StatePoint(object):
# Read CMFD information (if used)
self._read_cmfd()
def _read_cmfd(self):
base = 'cmfd'
# Read CMFD information
@ -217,9 +246,7 @@ class StatePoint(object):
self._cmfd_srccmp = self._get_double(self._current_batch,
path='{0}/cmfd_srccmp'.format(base))
def _read_meshes(self):
# Initialize dictionaries for the Meshes
# Keys - Mesh IDs
# Values - Mesh objects
@ -282,9 +309,7 @@ class StatePoint(object):
# Add mesh to the global dictionary of all Meshes
self._meshes[mesh_id] = mesh
def _read_tallies(self):
# Initialize dictionaries for the Tallies
# Keys - Tally IDs
# Values - Tally objects
@ -423,7 +448,6 @@ class StatePoint(object):
# Add the scores to the Tally
for j, score in enumerate(scores):
# If this is a scattering moment, insert the scattering order
if '-n' in score:
score = score.replace('-n', '-' + str(moments[j]))
@ -437,10 +461,13 @@ class StatePoint(object):
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_key] = tally
def read_results(self):
"""Read tally results and store them in the ``tallies`` attribute. No results
are read when the statepoint is instantiated.
# Number of realizations for global Tallies
"""
# Number of realizations for global Tallies
self._n_realizations = self._get_int(path='n_realizations')[0]
# Read global Tallies
@ -483,7 +510,8 @@ class StatePoint(object):
sum_sq = results[1::2]
# Define a routine to convert 0 to 1
nonzero = lambda val: 1 if not val else val
def nonzero(val):
return 1 if not val else val
# Reshape the results arrays
new_shape = (nonzero(tally.num_filter_bins),
@ -494,13 +522,17 @@ class StatePoint(object):
sum_sq = np.reshape(sum_sq, new_shape)
# Set the data for this Tally
tally.set_results(sum=sum, sum_sq=sum_sq)
tally.sum = sum
tally.sum_sq = sum_sq
# Indicate that Tally results have been read
self._results = True
def read_source(self):
"""Read and store source sites from the statepoint file. By default, source
sites are not loaded upon initialization.
"""
# Check whether Tally results have been read
if not self._results:
@ -520,7 +552,6 @@ class StatePoint(object):
# Initialize SourceSite object for each particle
for i in range(self._n_particles):
# Initialize new source site
site = SourceSite()
@ -536,9 +567,18 @@ class StatePoint(object):
# Store the source site in the NumPy array
self._source[i] = site
def compute_ci(self, confidence=0.95):
"""Computes confidence intervals for each Tally bin."""
"""Computes confidence intervals for each Tally bin.
This method is equivalent to calling compute_stdev(...) when the
confidence is known as opposed to its corresponding t value.
Parameters
----------
confidence : float, optional
Confidence level. Defaults to 0.95.
"""
# Determine significance level and percentile for two-sided CI
alpha = 1 - confidence
@ -548,11 +588,16 @@ class StatePoint(object):
t_value = scipy.stats.t.ppf(percentile, self._n_realizations - 1)
self.compute_stdev(t_value)
def compute_stdev(self, t_value=1.0):
"""
Computes the sample mean and the standard deviation of the mean
"""Computes the sample mean and the standard deviation of the mean
for each Tally bin.
Parameters
----------
t_value : float, optional
Student's t-value applied to the uncertainty. Defaults to 1.0,
meaning the reported value is the sample standard deviation.
"""
# Determine number of realizations
@ -572,49 +617,45 @@ class StatePoint(object):
if s != 0.0:
self._global_tallies[i, 1] = t_value * np.sqrt((s2 / n - s**2) / (n-1))
# Calculate sample mean and standard deviation for user-defined Tallies
for tally_id, tally in self.tallies.items():
tally.compute_std_dev(t_value)
def get_tally(self, scores=[], filters=[], nuclides=[],
name=None, id=None, estimator=None):
"""Finds and returns a Tally object with certain properties.
This routine searches the list of Tallies and returns the first Tally
found it finds which satisfieds all of the input parameters.
found it finds which satisfies all of the input parameters.
NOTE: The input parameters do not need to match the complete Tally
specification and may only represent a subset of the Tallies properties.
specification and may only represent a subset of the Tally's properties.
Parameters
----------
scores : list
A list of one or more score strings (default is []).
filters : list
A list of Filter objects (default is []).
nuclides : list
A list of Nuclide objects (default is []).
name : str
The name specified for the Tally (default is None).
id : int
The id specified for the Tally (default is None).
estimator: str
The type of estimator ('tracklength', 'analog'; default is None).
scores : list, optional
A list of one or more score strings (default is []).
filters : list, optional
A list of Filter objects (default is []).
nuclides : list, optional
A list of Nuclide objects (default is []).
name : str, optional
The name specified for the Tally (default is None).
id : int, optional
The id specified for the Tally (default is None).
estimator: str, optional
The type of estimator ('tracklength', 'analog'; default is None).
Returns
-------
A Tally object.
tally : Tally
A tally matching the specified criteria
Raises
------
LookupError : An error when a Tally meeting all of the input
parameters cannot be found in the statepoint.
LookupError
If a Tally meeting all of the input parameters cannot be found in
the statepoint.
"""
tally = None
@ -640,7 +681,7 @@ class StatePoint(object):
# Iterate over the scores requested by the user
for score in scores:
if not score in test_tally.scores:
if score not in test_tally.scores:
contains_scores = False
break
@ -653,7 +694,7 @@ class StatePoint(object):
# Iterate over the Filters requested by the user
for filter in filters:
if not filter in test_tally.filters:
if filter not in test_tally.filters:
contains_filters = False
break
@ -666,7 +707,7 @@ class StatePoint(object):
# Iterate over the Nuclides requested by the user
for nuclide in nuclides:
if not nuclide in test_tally.nuclides:
if nuclide not in test_tally.nuclides:
contains_nuclides = False
break
@ -683,14 +724,15 @@ class StatePoint(object):
return tally
def link_with_summary(self, summary):
"""Links Tallies and Filters with Summary model information.
This routine retrieves model information (materials, geometry) from a
Summary object populated with an HDF5 'summary.h5' file and inserts
it into the Tally objects. This can be helpful when viewing and
manipulating large scale Tally data.
Summary object populated with an HDF5 'summary.h5' file and inserts it
into the Tally objects. This can be helpful when viewing and
manipulating large scale Tally data. Note that it is necessary to link
against a summary to populate the Tallies with any user-specified "name"
XML tags.
Parameters
----------
@ -699,8 +741,10 @@ class StatePoint(object):
Raises
------
ValueError : An error when the argument passed to the 'summary'
parameter is not an openmc.Summary object.
ValueError
An error when the argument passed to the 'summary' parameter is not
an openmc.Summary object.
"""
if not isinstance(summary, openmc.summary.Summary):
@ -709,7 +753,6 @@ class StatePoint(object):
raise ValueError(msg)
for tally_id, tally in self.tallies.items():
# Get the Tally name from the summary file
tally.name = summary.tallies[tally_id].name
tally.with_summary = True
@ -717,7 +760,6 @@ class StatePoint(object):
nuclide_zaids = copy.deepcopy(tally.nuclides)
for nuclide_zaid in nuclide_zaids:
tally.remove_nuclide(nuclide_zaid)
if nuclide_zaid == -1:
tally.add_nuclide(openmc.Nuclide('total'))
@ -725,7 +767,6 @@ class StatePoint(object):
tally.add_nuclide(summary.nuclides[nuclide_zaid])
for filter in tally.filters:
if filter.type == 'surface':
surface_ids = []
for bin in filter.bins:
@ -749,12 +790,11 @@ class StatePoint(object):
for bin in filter.bins:
material_ids.append(summary.materials[bin].id)
filter.bins = material_ids
self._with_summary = True
def _get_data(self, n, typeCode, size):
return list(struct.unpack('={0}{1}'.format(n,typeCode),
return list(struct.unpack('={0}{1}'.format(n, typeCode),
self._f.read(n*size)))
def _get_int(self, n=1, path=None):

View file

@ -2,16 +2,18 @@ import numpy as np
import openmc
try:
import h5py
except ImportError:
msg = 'Unable to import h5py which is needed by openmc.summary'
raise ImportError(msg)
class Summary(object):
"""Information summarizing the geometry, materials, and tallies used in a
simulation.
"""
def __init__(self, filename):
# A user may not have h5py, but they can still use the rest of the
# Python API so we'll only try to import h5py if the user actually inits
# a Summary object.
import h5py
openmc.reset_auto_ids()
@ -27,9 +29,7 @@ class Summary(object):
self._read_geometry()
self._read_tallies()
def _read_metadata(self):
# Read OpenMC version
self.version = [self._f['version_major'][0],
self._f['version_minor'][0],
@ -44,9 +44,7 @@ class Summary(object):
self.gen_per_batch = self._f['gen_per_batch'][0]
self.n_procs = self._f['n_procs'][0]
def _read_geometry(self):
# Read in and initialize the Materials and Geometry
self._read_nuclides()
self._read_materials()
@ -56,9 +54,7 @@ class Summary(object):
self._read_lattices()
self._finalize_geometry()
def _read_nuclides(self):
self.n_nuclides = self._f['nuclides/n_nuclides'][0]
# Initialize dictionary for each Nuclide
@ -67,7 +63,6 @@ class Summary(object):
self.nuclides = {}
for key in self._f['nuclides'].keys():
if key == 'n_nuclides':
continue
@ -88,9 +83,7 @@ class Summary(object):
self.nuclides[zaid] = openmc.Nuclide(name=name, xs=xs)
self.nuclides[zaid].zaid = zaid
def _read_materials(self):
self.n_materials = self._f['materials/n_materials'][0]
# Initialize dictionary for each Material
@ -99,7 +92,6 @@ class Summary(object):
self.materials = {}
for key in self._f['materials'].keys():
if key == 'n_materials':
continue
@ -116,7 +108,6 @@ class Summary(object):
# Read the names of the S(a,b) tables for this Material
for i in range(1, n_sab+1):
sab_table = self._f['materials'][key]['sab_tables'][str(i)][0]
# Read the cross-section identifiers for each S(a,b) table
@ -148,9 +139,7 @@ class Summary(object):
# Add the Material to the global dictionary of all Materials
self.materials[index] = material
def _read_surfaces(self):
self.n_surfaces = self._f['geometry/n_surfaces'][0]
# Initialize dictionary for each Surface
@ -159,7 +148,6 @@ class Summary(object):
self.surfaces = {}
for key in self._f['geometry/surfaces'].keys():
if key == 'n_surfaces':
continue
@ -171,7 +159,6 @@ class Summary(object):
coeffs = self._f['geometry/surfaces'][key]['coefficients'][...]
# Create the Surface based on its type
if surf_type == 'X Plane':
x0 = coeffs[0]
surface = openmc.XPlane(surface_id, bc, x0, name)
@ -232,9 +219,7 @@ class Summary(object):
# Add Surface to global dictionary of all Surfaces
self.surfaces[index] = surface
def _read_cells(self):
self.n_cells = self._f['geometry/n_cells'][0]
# Initialize dictionary for each Cell
@ -251,7 +236,6 @@ class Summary(object):
self._cell_fills = {}
for key in self._f['geometry/cells'].keys():
if key == 'n_cells':
continue
@ -310,9 +294,7 @@ class Summary(object):
# Add the Cell to the global dictionary of all Cells
self.cells[index] = cell
def _read_universes(self):
self.n_universes = self._f['geometry/n_universes'][0]
# Initialize dictionary for each Universe
@ -321,7 +303,6 @@ class Summary(object):
self.universes = {}
for key in self._f['geometry/universes'].keys():
if key == 'n_universes':
continue
@ -340,9 +321,7 @@ class Summary(object):
# Add the Universe to the global list of Universes
self.universes[index] = universe
def _read_lattices(self):
self.n_lattices = self._f['geometry/n_lattices'][0]
# Initialize lattices for each Lattice
@ -351,7 +330,6 @@ class Summary(object):
self.lattices = {}
for key in self._f['geometry/lattices'].keys():
if key == 'n_lattices':
continue
@ -383,7 +361,6 @@ class Summary(object):
lattice.lower_left = lower_left
lattice.pitch = pitch
# If the Universe specified outer the Lattice is not void (-22)
if outer != -22:
lattice.outer = self.universes[outer]
@ -395,14 +372,14 @@ class Summary(object):
for x in range(universe_ids.shape[0]):
for y in range(universe_ids.shape[1]):
for z in range(universe_ids.shape[2]):
universes[x,y,z] = \
self.get_universe_by_id(universe_ids[x,y,z])
universes[x, y, z] = \
self.get_universe_by_id(universe_ids[x, y, z])
# Transpose, reverse y-dimension for appropriate ordering
shape = universes.shape
universes = np.transpose(universes, (1,0,2))
universes = np.transpose(universes, (1, 0, 2))
universes.shape = shape
universes = universes[:,::-1,:]
universes = universes[:, ::-1, :]
lattice.universes = universes
if offset_size > 0:
@ -420,8 +397,8 @@ class Summary(object):
pitch = self._f['geometry/lattices'][key]['pitch'][...]
outer = self._f['geometry/lattices'][key]['outer'][0]
universe_ids = \
self._f['geometry/lattices'][key]['universes'][...]
universe_ids = self._f[
'geometry/lattices'][key]['universes'][...]
# Create the Lattice
lattice = openmc.HexLattice(lattice_id=lattice_id, name=name)
@ -441,9 +418,9 @@ class Summary(object):
for i in range(universe_ids.shape[0]):
for j in range(universe_ids.shape[1]):
for k in range(universe_ids.shape[2]):
if universe_ids[i,j,k] != -1:
universes[i,j,k] = \
self.get_universe_by_id(universe_ids[i,j,k])
if universe_ids[i, j, k] != -1:
universes[i, j, k] = self.get_universe_by_id(
universe_ids[i, j, k])
if offset_size > 0:
lattice.offsets = offsets
@ -451,15 +428,12 @@ class Summary(object):
# Add the Lattice to the global dictionary of all Lattices
self.lattices[index] = lattice
def _finalize_geometry(self):
# Initialize Geometry object
self.openmc_geometry = openmc.Geometry()
# Iterate over all Cells and add fill Materials, Universes and Lattices
for cell_key in self._cell_fills.keys():
# Determine fill type ('normal', 'universe', or 'lattice') and ID
fill_type = self._cell_fills[cell_key][0]
fill_id = self._cell_fills[cell_key][1]
@ -482,16 +456,14 @@ class Summary(object):
root_universe = self.get_universe_by_id(0)
self.openmc_geometry.root_universe = root_universe
def _read_tallies(self):
# Initialize dictionaries for the Tallies
# Keys - Tally IDs
# Values - Tally objects
self.tallies = {}
# Read the number of tallies
if not 'tallies' in self._f.keys():
if 'tallies' not in self._f:
self.n_tallies = 0
return
@ -554,8 +526,11 @@ class Summary(object):
# Add Tally to the global dictionary of all Tallies
self.tallies[tally_id] = tally
def make_opencg_geometry(self):
"""Create OpenCG geometry based on the information contained in the summary
file. The geometry is stored as the 'opencg_geometry' attribute.
"""
try:
from openmc.opencg_compatible import get_opencg_geometry
@ -567,8 +542,22 @@ class Summary(object):
if self.opencg_geometry is None:
self.opencg_geometry = get_opencg_geometry(self.openmc_geometry)
def get_nuclide_by_zaid(self, zaid):
"""Return a Nuclide object given the 'zaid' identifier for the nuclide.
Parameters
----------
zaid : int
1000*Z + A, where Z is the atomic number of the nuclide and A is the
mass number. For example, the zaid for U-235 is 92235.
Returns
-------
nuclide : openmc.nuclide.Nuclide or None
Nuclide matching the specified zaid, or None if no matching object
is found.
"""
for index, nuclide in self.nuclides.items():
if nuclide._zaid == zaid:
@ -576,8 +565,20 @@ class Summary(object):
return None
def get_material_by_id(self, material_id):
"""Return a Material object given the material id
Parameters
----------
id : int
Unique identifier for the material
Returns
-------
material : openmc.material.Material
Material with given id
"""
for index, material in self.materials.items():
if material._id == material_id:
@ -585,8 +586,20 @@ class Summary(object):
return None
def get_surface_by_id(self, surface_id):
"""Return a Surface object given the surface id
Parameters
----------
id : int
Unique identifier for the surface
Returns
-------
surface : openmc.surface.Surface
Surface with given id
"""
for index, surface in self.surfaces.items():
if surface._id == surface_id:
@ -594,8 +607,20 @@ class Summary(object):
return None
def get_cell_by_id(self, cell_id):
"""Return a Cell object given the cell id
Parameters
----------
id : int
Unique identifier for the cell
Returns
-------
cell : openmc.universe.Cell
Cell with given id
"""
for index, cell in self.cells.items():
if cell._id == cell_id:
@ -603,8 +628,20 @@ class Summary(object):
return None
def get_universe_by_id(self, universe_id):
"""Return a Universe object given the universe id
Parameters
----------
id : int
Unique identifier for the universe
Returns
-------
universe : openmc.universe.Universe
Universe with given id
"""
for index, universe in self.universes.items():
if universe._id == universe_id:
@ -612,8 +649,20 @@ class Summary(object):
return None
def get_lattice_by_id(self, lattice_id):
"""Return a Lattice object given the lattice id
Parameters
----------
id : int
Unique identifier for the lattice
Returns
-------
lattice : openmc.universe.Lattice
Lattice with given id
"""
for index, lattice in self.lattices.items():
if lattice._id == lattice_id:

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,47 @@
from numbers import Real
from xml.etree import ElementTree as ET
import sys
from openmc.checkvalue import *
from openmc.checkvalue import check_type, check_value
if sys.version_info[0] >= 3:
basestring = str
class Trigger(object):
"""A criterion for when to finish a simulation based on tally uncertainties.
Parameters
----------
trigger_type : {'variance', 'std_dev', 'rel_err'}
Determine whether to trigger on the variance, standard deviation, or
relative error of scores.
threshold : float
The threshold for the trigger type.
Attributes
----------
trigger_type : {'variance', 'std_dev', 'rel_err'}
Determine whether to trigger on the variance, standard deviation, or
relative error of scores.
threshold : float
The threshold for the trigger type.
scores : list of str
Scores which should be checked against the trigger
"""
def __init__(self, trigger_type, threshold):
# Initialize Mesh class attributes
self.trigger_type = trigger_type
self.threshold = threshold
self._scores = []
def __deepcopy__(self, memo):
existing = memo.get(id(self))
# If this is first time we have tried to copy this object, create a copy
if existing is None:
clone = type(self).__new__(type(self))
clone._trigger_type = self._trigger_type
clone._threshold = self._threshold
@ -36,47 +58,40 @@ class Trigger(object):
else:
return existing
@property
def trigger_type(self):
return self._trigger_type
@property
def threshold(self):
return self._threshold
@property
def scores(self):
return self._scores
@trigger_type.setter
def trigger_type(self, trigger_type):
if not trigger_type in ['variance', 'std_dev', 'rel_err']:
msg = 'Unable to create a tally trigger with ' \
'type "{0}"'.format(trigger_type)
raise ValueError(msg)
check_value('tally trigger type', trigger_type,
['variance', 'std_dev', 'rel_err'])
self._trigger_type = trigger_type
@threshold.setter
def threshold(self, threshold):
if not is_float(threshold):
msg = 'Unable to set a tally trigger threshold with ' \
'threshold "{0}"'.format(threshold)
raise ValueError(msg)
check_type('tally trigger threshold', threshold, Real)
self._threshold = threshold
def add_score(self, score):
"""Add a score to the list of scores to be checked against the trigger.
if not is_string(score):
Parameters
----------
score : str
Score to append
"""
if not isinstance(score, basestring):
msg = 'Unable to add score "{0}" to tally trigger since ' \
'it is not a string'.format(score)
raise ValueError(msg)
@ -87,28 +102,25 @@ class Trigger(object):
else:
self._scores.append(score)
def __repr__(self):
string = 'Trigger\n'
string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._trigger_type)
string += '{0: <16}{1}{2}\n'.format('\tThreshold', '=\t', self._threshold)
string += '{0: <16}{1}{2}\n'.format('\tScores', '=\t', self._scores)
return string
def get_trigger_xml(self, element):
"""Return XML representation of the trigger
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing trigger data
"""
subelement = ET.SubElement(element, "trigger")
subelement.set("type", self._trigger_type)
subelement.set("threshold", str(self._threshold))
# Scores
if len(self._scores) != 0:
scores = ''
for score in self._scores:
scores += '{0} '.format(score)
scores.rstrip(' ')
subelement.set("scores", scores)
subelement.set("scores", ' '.join(map(str, self._scores)))

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
module ace_header
use constants, only: MAX_FILE_LEN
use constants, only: MAX_FILE_LEN, ZERO
use endf_header, only: Tab1
use list_header, only: ListInt
@ -167,12 +167,12 @@ module ace_header
type Nuclide0K
character(10) :: nuclide ! name of nuclide, e.g. U-238
character(16) :: scheme = 'ares' ! target velocity sampling scheme
character(10) :: name ! name of nuclide, e.g. 92235.03c
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
real(8) :: E_min = 0.01e-6 ! lower cutoff energy for res scattering
real(8) :: E_max = 1000.0e-6 ! upper cutoff energy for res scattering
character(10) :: nuclide ! name of nuclide, e.g. U-238
character(16) :: scheme = 'ares' ! target velocity sampling scheme
character(10) :: name ! name of nuclide, e.g. 92235.03c
character(10) :: name_0K ! name of 0K nuclide, e.g. 92235.00c
real(8) :: E_min = 0.01e-6_8 ! lower cutoff energy for res scattering
real(8) :: E_max = 1000.0e-6_8 ! upper cutoff energy for res scattering
end type Nuclide0K
@ -204,7 +204,7 @@ module ace_header
! threshold for S(a,b) treatment (usually ~4 eV)
real(8) :: threshold_inelastic
real(8) :: threshold_elastic = 0.0
real(8) :: threshold_elastic = ZERO
! Inelastic scattering data
integer :: n_inelastic_e_in ! # of incoming E for inelastic
@ -258,7 +258,7 @@ module ace_header
type NuclideMicroXS
integer :: index_grid ! index on nuclide energy grid
integer :: index_temp ! temperature index for nuclide
real(8) :: last_E = 0.0 ! last evaluated energy
real(8) :: last_E = ZERO ! last evaluated energy
real(8) :: interp_factor ! interpolation factor on nuc. energy grid
real(8) :: total ! microscropic total xs
real(8) :: elastic ! microscopic elastic scattering xs

View file

@ -95,7 +95,7 @@ contains
#ifdef MPI
use global, only: mpi_err
use mpi
use message_passing
#endif
integer :: nx ! maximum number of cells in x direction
@ -224,7 +224,7 @@ contains
#ifdef MPI
use global, only: mpi_err
use mpi
use message_passing
#endif
logical, intent(in) :: new_weights ! calcualte new weights

View file

@ -44,6 +44,7 @@ contains
subroutine read_cmfd_xml()
use constants, only: ZERO, ONE
use error, only: fatal_error, warning
use global
use output, only: write_message
@ -103,7 +104,7 @@ contains
cmfd % indices(4) = ng - 1 ! sets energy group dimension
else
if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2))
cmfd % egrid = (/0.0_8,20.0_8/)
cmfd % egrid = [ ZERO, 20.0_8 ]
cmfd % indices(4) = 1 ! one energy group
end if
@ -111,7 +112,7 @@ contains
if (check_for_node(node_mesh, "albedo")) then
call get_node_array(node_mesh, "albedo", cmfd % albedo)
else
cmfd % albedo = (/1.0, 1.0, 1.0, 1.0, 1.0, 1.0/)
cmfd % albedo = [ ONE, ONE, ONE, ONE, ONE, ONE ]
end if
! Get acceleration map

View file

@ -27,7 +27,7 @@ module constants
! adjusted. Modifying constants in other sections may cause the code to fail.
! Monoatomic ideal-gas scattering treatment threshold
real(8), parameter :: FREE_GAS_THRESHOLD = 400.0
real(8), parameter :: FREE_GAS_THRESHOLD = 400.0_8
! Significance level for confidence intervals
real(8), parameter :: CONFIDENCE_LEVEL = 0.95_8
@ -63,15 +63,18 @@ module constants
real(8), parameter :: &
PI = 3.1415926535898_8, & ! pi
MASS_NEUTRON = 1.008664916, & ! mass of a neutron in amu
MASS_PROTON = 1.007276466812, & ! mass of a proton in amu
AMU = 1.660538921e-27, & ! 1 amu in kg
N_AVOGADRO = 0.602214129, & ! Avogadro's number in 10^24/mol
K_BOLTZMANN = 8.6173324e-11, & ! Boltzmann constant in MeV/K
MASS_NEUTRON = 1.008664916_8, & ! mass of a neutron in amu
MASS_PROTON = 1.007276466812_8, & ! mass of a proton in amu
AMU = 1.660538921e-27_8, & ! 1 amu in kg
N_AVOGADRO = 0.602214129_8, & ! Avogadro's number in 10^24/mol
K_BOLTZMANN = 8.6173324e-11_8, & ! Boltzmann constant in MeV/K
INFINITY = huge(0.0_8), & ! positive infinity
ZERO = 0.0_8, &
HALF = 0.5_8, &
ONE = 1.0_8, &
TWO = 2.0_8
TWO = 2.0_8, &
THREE = 3.0_8, &
FOUR = 4.0_8
! ============================================================================
! GEOMETRY-RELATED CONSTANTS
@ -317,13 +320,13 @@ module constants
OUT_FRONT = 4, &
IN_TOP = 5, &
OUT_TOP = 6
! Tally trigger types and threshold
integer, parameter :: &
VARIANCE = 1, &
RELATIVE_ERROR = 2, &
STANDARD_DEVIATION = 3
STANDARD_DEVIATION = 3
! Global tallY parameters
integer, parameter :: N_GLOBAL_TALLIES = 4
integer, parameter :: &

View file

@ -1,7 +1,7 @@
module eigenvalue
#ifdef MPI
use mpi
use message_passing
#endif
use cmfd_execute, only: cmfd_init_batch, execute_cmfd
@ -96,7 +96,7 @@ contains
end do GENERATION_LOOP
call finalize_batch()
if (satisfy_triggers) exit BATCH_LOOP
end do BATCH_LOOP
@ -171,6 +171,26 @@ contains
subroutine finalize_generation()
! Update global tallies with the omp private accumulation variables
!$omp parallel
!$omp critical
global_tallies(K_TRACKLENGTH) % value = &
global_tallies(K_TRACKLENGTH) % value + global_tally_tracklength
global_tallies(K_COLLISION) % value = &
global_tallies(K_COLLISION) % value + global_tally_collision
global_tallies(LEAKAGE) % value = &
global_tallies(LEAKAGE) % value + global_tally_leakage
global_tallies(K_ABSORPTION) % value = &
global_tallies(K_ABSORPTION) % value + global_tally_absorption
!$omp end critical
! reset private tallies
global_tally_tracklength = 0
global_tally_collision = 0
global_tally_leakage = 0
global_tally_absorption = 0
!$omp end parallel
#ifdef _OPENMP
! Join the fission bank from each thread into one global fission bank
call join_bank_from_threads()
@ -220,12 +240,12 @@ contains
! Calculate combined estimate of k-effective
if (master) call calculate_combined_keff()
! Check_triggers
if (master) call check_triggers()
#ifdef MPI
call MPI_BCAST(satisfy_triggers, 1, MPI_LOGICAL, 0, &
MPI_COMM_WORLD, mpi_err)
MPI_COMM_WORLD, mpi_err)
#endif
if (satisfy_triggers .or. &
(trigger_on .and. current_batch == n_max_batches)) then
@ -273,7 +293,11 @@ contains
#ifdef MPI
integer(8) :: n ! number of sites to send/recv
integer :: neighbor ! processor to send/recv data from
#ifdef MPIF08
type(MPI_Request) :: request(20)
#else
integer :: request(20) ! communication request for send/recving sites
#endif
integer :: n_request ! number of communication requests
integer(8) :: index_local ! index in local source bank
integer(8), save, allocatable :: &
@ -544,7 +568,7 @@ contains
! 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)**(1.0/3.0))
n = ceiling((n_particles/20)**(ONE/THREE))
! copy dimensions
m % n_dimension = 3

View file

@ -92,15 +92,17 @@ contains
! Determine corresponding indices in nuclide grid to energies on
! equal-logarithmic grid
j = 1
do k = 0, M - 1
do k = 0, M
do while (log(nuc%energy(j + 1)/E_min) <= umesh(k))
! Ensure that for isotopes where maxval(nuc % energy) << E_max
! that there are no out-of-bounds issues.
if (j + 1 == nuc % n_grid) then
exit
end if
j = j + 1
end do
nuc % grid_index(k) = j
end do
! Set the last point explicitly so that we don't have out-of-bounds issues
nuc % grid_index(M) = size(nuc % energy) - 1
end do
deallocate(umesh)

View file

@ -6,7 +6,7 @@ module error
use global
#ifdef MPI
use mpi
use message_passing
#endif
implicit none
@ -155,7 +155,7 @@ contains
#ifdef NO_F2008
stop
#else
error stop
error stop
#endif
end subroutine fatal_error

View file

@ -6,7 +6,7 @@ module finalize
use tally, only: tally_statistics
#ifdef MPI
use mpi
use message_passing
#endif
#ifdef HDF5

View file

@ -1,7 +1,7 @@
module fixed_source
#ifdef MPI
use mpi
use message_passing
#endif
use constants, only: ZERO, MAX_LINE_LEN

View file

@ -6,7 +6,7 @@ module geometry
&RectLattice, HexLattice
use global
use output, only: write_message
use particle_header, only: LocalCoord, deallocate_coord, Particle
use particle_header, only: LocalCoord, Particle
use particle_restart_write, only: write_particle_restart
use string, only: to_str
use tally, only: score_surface_current
@ -76,20 +76,18 @@ contains
type(Particle), intent(inout) :: p
integer :: i ! cell loop index on a level
integer :: j ! coordinate level index
integer :: n_coord ! saved number of coordinate levels
integer :: n ! number of cells to search on a level
integer :: index_cell ! index in cells array
type(Cell), pointer :: c ! pointer to cell
type(Universe), pointer :: univ ! universe to search in
type(LocalCoord), pointer :: coord ! particle coordinate to search on
coord => p % coord0
! loop through each coordinate level
do while (associated(coord))
p % coord => coord
univ => universes(coord % universe)
n_coord = p % n_coord
do j = 1, n_coord
p % n_coord = j
univ => universes(p % coord(j) % universe)
n = univ % n_cells
! loop through each cell on this level
@ -99,10 +97,10 @@ contains
if (simple_cell_contains(c, p)) then
! the particle should only be contained in one cell per level
if (index_cell /= coord % cell) then
if (index_cell /= p % coord(j) % cell) then
call fatal_error("Overlapping cells detected: " &
&// trim(to_str(cells(index_cell) % id)) // ", " &
&// trim(to_str(cells(coord % cell) % id)) &
&// trim(to_str(cells(p % coord(j) % cell) % id)) &
&// " on universe " // trim(to_str(univ % id)))
end if
@ -111,9 +109,6 @@ contains
end if
end do
coord => coord % next
end do
end subroutine check_cell_overlap
@ -130,6 +125,7 @@ contains
logical, intent(inout) :: found
integer, optional :: search_cells(:)
integer :: i ! index over cells
integer :: j ! coordinate level index
integer :: i_xyz(3) ! indices in lattice
integer :: n ! number of cells to search
integer :: index_cell ! index in cells array
@ -138,8 +134,10 @@ contains
class(Lattice), pointer :: lat ! pointer to lattice
type(Universe), pointer :: univ ! universe to search in
! Remove coordinates for any lower levels
call deallocate_coord(p % coord % next)
do j = p % n_coord + 1, MAX_COORD
call p % coord(j) % reset()
end do
j = p % n_coord
! set size of list to search
if (present(search_cells)) then
@ -147,7 +145,7 @@ contains
n = size(search_cells)
else
use_search_cells = .false.
univ => universes(p % coord % universe)
univ => universes(p % coord(j) % universe)
n = univ % n_cells
end if
@ -157,7 +155,7 @@ contains
if (use_search_cells) then
index_cell = search_cells(i)
! check to make sure search cell is in same universe
if (cells(index_cell) % universe /= p % coord % universe) cycle
if (cells(index_cell) % universe /= p % coord(j) % universe) cycle
else
index_cell = univ % cells(i)
end if
@ -169,7 +167,7 @@ contains
if (.not. simple_cell_contains(c, p)) cycle
! Set cell on this level
p % coord % cell = index_cell
p % coord(j) % cell = index_cell
! Show cell information on trace
if (verbosity >= 10 .or. trace) then
@ -188,28 +186,29 @@ contains
! ======================================================================
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
! Create new level of coordinates
allocate(p % coord % next)
p % coord % next % xyz = p % coord % xyz
p % coord % next % uvw = p % coord % uvw
! Store lower level coordinates
p % coord(j + 1) % xyz = p % coord(j) % xyz
p % coord(j + 1) % uvw = p % coord(j) % uvw
! Move particle to next level and set universe
p % coord => p % coord % next
p % coord % universe = c % fill
j = j + 1
p % n_coord = j
p % coord(j) % universe = c % fill
! Apply translation
if (allocated(c % translation)) then
p % coord % xyz = p % coord % xyz - c % translation
p % coord(j) % xyz = p % coord(j) % xyz - c % translation
end if
! Apply rotation
if (allocated(c % rotation_matrix)) then
p % coord % xyz = matmul(c % rotation_matrix, p % coord % xyz)
p % coord % uvw = matmul(c % rotation_matrix, p % coord % uvw)
p % coord % rotated = .true.
p % coord(j) % xyz = matmul(c % rotation_matrix, p % coord(j) % xyz)
p % coord(j) % uvw = matmul(c % rotation_matrix, p % coord(j) % uvw)
p % coord(j) % rotated = .true.
end if
call find_cell(p, found)
j = p % n_coord
if (.not. found) exit
elseif (c % type == CELL_LATTICE) then CELL_TYPE
@ -220,40 +219,41 @@ contains
lat => lattices(c % fill) % obj
! Determine lattice indices
i_xyz = lat % get_indices(p % coord % xyz + TINY_BIT * p % coord % uvw)
i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw)
! Create new level of coordinates
allocate(p % coord % next)
p % coord % next % xyz = lat % get_local_xyz(p % coord % xyz, i_xyz)
p % coord % next % uvw = p % coord % uvw
! Store lower level coordinates
p % coord(j + 1) % xyz = lat % get_local_xyz(p % coord(j) % xyz, i_xyz)
p % coord(j + 1) % uvw = p % coord(j) % uvw
! set particle lattice indices
p % coord % next% lattice = c % fill
p % coord % next% lattice_x = i_xyz(1)
p % coord % next% lattice_y = i_xyz(2)
p % coord % next% lattice_z = i_xyz(3)
p % coord(j + 1) % lattice = c % fill
p % coord(j + 1) % lattice_x = i_xyz(1)
p % coord(j + 1) % lattice_y = i_xyz(2)
p % coord(j + 1) % lattice_z = i_xyz(3)
! Set the next lowest coordinate level.
if (lat % are_valid_indices(i_xyz)) then
! Particle is inside the lattice.
p % coord % next % universe = &
&lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
p % coord(j + 1) % universe = &
lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
else
! Particle is outside the lattice.
if (lat % outer == NO_OUTER_UNIVERSE) then
call fatal_error("A particle is outside latttice " &
&// trim(to_str(lat % id)) // " but the lattice has no &
// trim(to_str(lat % id)) // " but the lattice has no &
&defined outer universe.")
else
p % coord % next % universe = lat % outer
p % coord(j + 1) % universe = lat % outer
end if
end if
! Move particle to next level and search for the lower cells.
p % coord => p % coord % next
j = j + 1
p % n_coord = j
call find_cell(p, found)
j = p % n_coord
if (.not. found) exit
end if CELL_TYPE
@ -314,15 +314,13 @@ contains
! TODO: Find a better solution to score surface currents than
! physically moving the particle forward slightly
p % coord0 % xyz = p % coord0 % xyz + TINY_BIT * p % coord0 % uvw
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
end if
! Score to global leakage tally
if (tallies_on) then
!$omp atomic
global_tallies(LEAKAGE) % value = &
global_tallies(LEAKAGE) % value + p % wgt
global_tally_leakage = global_tally_leakage + p % wgt
end if
! Display message
@ -337,7 +335,7 @@ contains
! PARTICLE REFLECTS FROM SURFACE
! Do not handle reflective boundary conditions on lower universes
if (.not. associated(p % coord, p % coord0)) then
if (p % n_coord /= 1) then
call handle_lost_particle(p, "Cannot reflect particle " &
&// trim(to_str(p % id)) // " off surface in a lower universe.")
return
@ -348,15 +346,15 @@ contains
! case the surface crossing in coincident with a mesh boundary
if (active_current_tallies % size() > 0) then
p % coord0 % xyz = p % coord0 % xyz - TINY_BIT * p % coord0 % uvw
p % coord(1) % xyz = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
call score_surface_current(p)
p % coord0 % xyz = p % coord0 % xyz + TINY_BIT * p % coord0 % uvw
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
end if
! Copy particle's direction cosines
u = p % coord0 % uvw(1)
v = p % coord0 % uvw(2)
w = p % coord0 % uvw(3)
u = p % coord(1) % uvw(1)
v = p % coord(1) % uvw(2)
w = p % coord(1) % uvw(3)
select case (surf%type)
case (SURF_PX)
@ -383,8 +381,8 @@ contains
case (SURF_CYL_X)
! Find y-y0, z-z0 and dot product of direction and surface normal
y = p % coord0 % xyz(2) - surf % coeffs(1)
z = p % coord0 % xyz(3) - surf % coeffs(2)
y = p % coord(1) % xyz(2) - surf % coeffs(1)
z = p % coord(1) % xyz(3) - surf % coeffs(2)
R = surf % coeffs(3)
dot_prod = v*y + w*z
@ -394,8 +392,8 @@ contains
case (SURF_CYL_Y)
! Find x-x0, z-z0 and dot product of direction and surface normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
z = p % coord0 % xyz(3) - surf % coeffs(2)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
z = p % coord(1) % xyz(3) - surf % coeffs(2)
R = surf % coeffs(3)
dot_prod = u*x + w*z
@ -405,8 +403,8 @@ contains
case (SURF_CYL_Z)
! Find x-x0, y-y0 and dot product of direction and surface normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
y = p % coord0 % xyz(2) - surf % coeffs(2)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
y = p % coord(1) % xyz(2) - surf % coeffs(2)
R = surf % coeffs(3)
dot_prod = u*x + v*y
@ -417,9 +415,9 @@ contains
case (SURF_SPHERE)
! Find x-x0, y-y0, z-z0 and dot product of direction and surface
! normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
y = p % coord0 % xyz(2) - surf % coeffs(2)
z = p % coord0 % xyz(3) - surf % coeffs(3)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
y = p % coord(1) % xyz(2) - surf % coeffs(2)
z = p % coord(1) % xyz(3) - surf % coeffs(3)
R = surf % coeffs(4)
dot_prod = u*x + v*y + w*z
@ -431,9 +429,9 @@ contains
case (SURF_CONE_X)
! Find x-x0, y-y0, z-z0 and dot product of direction and surface
! normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
y = p % coord0 % xyz(2) - surf % coeffs(2)
z = p % coord0 % xyz(3) - surf % coeffs(3)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
y = p % coord(1) % xyz(2) - surf % coeffs(2)
z = p % coord(1) % xyz(3) - surf % coeffs(3)
R = surf % coeffs(4)
dot_prod = (v*y + w*z - R*u*x)/((R + ONE)*R*x*x)
@ -445,9 +443,9 @@ contains
case (SURF_CONE_Y)
! Find x-x0, y-y0, z-z0 and dot product of direction and surface
! normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
y = p % coord0 % xyz(2) - surf % coeffs(2)
z = p % coord0 % xyz(3) - surf % coeffs(3)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
y = p % coord(1) % xyz(2) - surf % coeffs(2)
z = p % coord(1) % xyz(3) - surf % coeffs(3)
R = surf % coeffs(4)
dot_prod = (u*x + w*z - R*v*y)/((R + ONE)*R*y*y)
@ -459,9 +457,9 @@ contains
case (SURF_CONE_Z)
! Find x-x0, y-y0, z-z0 and dot product of direction and surface
! normal
x = p % coord0 % xyz(1) - surf % coeffs(1)
y = p % coord0 % xyz(2) - surf % coeffs(2)
z = p % coord0 % xyz(3) - surf % coeffs(3)
x = p % coord(1) % xyz(1) - surf % coeffs(1)
y = p % coord(1) % xyz(2) - surf % coeffs(2)
z = p % coord(1) % xyz(3) - surf % coeffs(3)
R = surf % coeffs(4)
dot_prod = (u*x + v*y - R*w*z)/((R + ONE)*R*z*z)
@ -477,28 +475,26 @@ contains
! Set new particle direction
norm = sqrt(u*u + v*v + w*w)
p % coord0 % uvw = [u, v, w] / norm
p % coord(1) % uvw = [u, v, w] / norm
! Reassign particle's cell and surface
p % coord0 % cell = last_cell
p % coord(1) % cell = last_cell
p % surface = -p % surface
! If a reflective surface is coincident with a lattice or universe
! boundary, it is necessary to redetermine the particle's coordinates in
! the lower universes.
if (associated(p % coord0 % next)) then
call deallocate_coord(p % coord0 % next)
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Couldn't find particle after reflecting&
& from surface.")
return
end if
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Couldn't find particle after reflecting&
& from surface.")
return
end if
! Set previous coordinate going slightly past surface crossing
p % last_xyz = p % coord0 % xyz + TINY_BIT * p % coord0 % uvw
p % last_xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
! Diagnostic message
if (verbosity >= 10 .or. trace) then
@ -532,8 +528,7 @@ contains
! Remove lower coordinate levels and assignment of surface
p % surface = NONE
p % coord => p % coord0
call deallocate_coord(p % coord % next)
p % n_coord = 1
call find_cell(p, found)
if (run_mode /= MODE_PLOTTING .and. (.not. found)) then
@ -542,9 +537,8 @@ contains
! the particle is really traveling tangent to a surface, if we move it
! forward a tiny bit it should fix the problem.
p % coord => p % coord0
call deallocate_coord(p % coord % next)
p % coord % xyz = p % coord % xyz + TINY_BIT * p % coord % uvw
p % n_coord = 1
p % coord(1) % xyz = p % coord(1) % xyz + TINY_BIT * p % coord(1) % uvw
call find_cell(p, found)
! Couldn't find next cell anywhere! This probably means there is an actual
@ -568,52 +562,46 @@ contains
type(Particle), intent(inout) :: p
integer, intent(in) :: lattice_translation(3)
integer :: j
integer :: i_xyz(3) ! indices in lattice
logical :: found ! particle found in cell?
class(Lattice), pointer :: lat
type(LocalCoord), pointer :: parent_coord
lat => lattices(p % coord % lattice) % obj
j = p % n_coord
lat => lattices(p % coord(j) % lattice) % obj
if (verbosity >= 10 .or. trace) then
call write_message(" Crossing lattice " // trim(to_str(lat % id)) &
&// ". Current position (" // trim(to_str(p % coord % lattice_x)) &
&// "," // trim(to_str(p % coord % lattice_y)) // "," &
&// trim(to_str(p % coord % lattice_z)) // ")")
&// ". Current position (" // trim(to_str(p % coord(j) % lattice_x)) &
&// "," // trim(to_str(p % coord(j) % lattice_y)) // "," &
&// trim(to_str(p % coord(j) % lattice_z)) // ")")
end if
! Find the coordiante level just above the current one.
parent_coord => p % coord0
do while(.not. associated(parent_coord % next, p % coord))
parent_coord => parent_coord % next
end do
! Set the lattice indices.
p % coord % lattice_x = p % coord % lattice_x + lattice_translation(1)
p % coord % lattice_y = p % coord % lattice_y + lattice_translation(2)
p % coord % lattice_z = p % coord % lattice_z + lattice_translation(3)
i_xyz(1) = p % coord % lattice_x
i_xyz(2) = p % coord % lattice_y
i_xyz(3) = p % coord % lattice_z
p % coord(j) % lattice_x = p % coord(j) % lattice_x + lattice_translation(1)
p % coord(j) % lattice_y = p % coord(j) % lattice_y + lattice_translation(2)
p % coord(j) % lattice_z = p % coord(j) % lattice_z + lattice_translation(3)
i_xyz(1) = p % coord(j) % lattice_x
i_xyz(2) = p % coord(j) % lattice_y
i_xyz(3) = p % coord(j) % lattice_z
! Set the new coordinate position.
p % coord % xyz = lat % get_local_xyz(parent_coord % xyz, i_xyz)
p % coord(j) % xyz = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz)
OUTSIDE_LAT: if (.not. lat % are_valid_indices(i_xyz)) then
! The particle is outside the lattice. Search for it from coord0.
call deallocate_coord(p % coord0 % next)
p % coord => p % coord0
! The particle is outside the lattice. Search for it from base coord
p % n_coord = 1
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Could not locate particle " &
&// trim(to_str(p % id)) // " after crossing a lattice boundary.")
// trim(to_str(p % id)) // " after crossing a lattice boundary.")
return
end if
else OUTSIDE_LAT
! Find cell in next lattice element
p % coord % universe = lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
p % coord(j) % universe = lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
call find_cell(p, found)
if (.not. found) then
@ -622,15 +610,14 @@ contains
! off all lower-level coordinates and search from universe zero
! Remove lower coordinates
call deallocate_coord(p % coord0 % next)
p % coord => p % coord0
p % n_coord = 1
! Search for particle
call find_cell(p, found)
if (.not. found) then
call handle_lost_particle(p, "Could not locate particle " &
&// trim(to_str(p % id)) &
&// " after crossing a lattice boundary.")
// trim(to_str(p % id)) &
// " after crossing a lattice boundary.")
return
end if
end if
@ -644,14 +631,17 @@ contains
! that has a parent cell, also include the surfaces of the edge of the universe.
!===============================================================================
subroutine distance_to_boundary(p, dist, surface_crossed, lattice_translation)
subroutine distance_to_boundary(p, dist, surface_crossed, lattice_translation, &
next_level)
type(Particle), intent(inout) :: p
real(8), intent(out) :: dist
integer, intent(out) :: surface_crossed
integer, intent(out) :: lattice_translation(3)
integer, intent(out) :: next_level
integer :: i ! index for surface in cell
integer :: j
integer :: index_surf ! index in surfaces array (with sign)
integer :: i_xyz(3) ! lattice indices
integer :: level_surf_cross ! surface crossed on current level
@ -675,30 +665,25 @@ contains
type(Cell), pointer :: cl
type(Surface), pointer :: surf
class(Lattice), pointer :: lat
type(LocalCoord), pointer :: coord
type(LocalCoord), pointer :: final_coord
type(LocalCoord), pointer :: parent_coord
! inialize distance to infinity (huge)
dist = INFINITY
d_lat = INFINITY
d_surf = INFINITY
lattice_translation(:) = [0, 0, 0]
nullify(final_coord)
! Get pointer to top-level coordinates
coord => p % coord0
next_level = 0
! Loop over each universe level
LEVEL_LOOP: do while(associated(coord))
LEVEL_LOOP: do j = 1, p % n_coord
! get pointer to cell on this level
cl => cells(coord % cell)
cl => cells(p % coord(j) % cell)
! copy directional cosines
u = coord % uvw(1)
v = coord % uvw(2)
w = coord % uvw(3)
u = p % coord(j) % uvw(1)
v = p % coord(j) % uvw(2)
w = p % coord(j) % uvw(3)
! =======================================================================
! FIND MINIMUM DISTANCE TO SURFACE IN THIS CELL
@ -706,9 +691,9 @@ contains
SURFACE_LOOP: do i = 1, cl % n_surfaces
! copy local coordinates of particle
x = coord % xyz(1)
y = coord % xyz(2)
z = coord % xyz(3)
x = p % coord(j) % xyz(1)
y = p % coord(j) % xyz(2)
z = p % coord(j) % xyz(3)
! check for coincident surface -- note that we can't skip the
! calculation in general because a particle could be on one side of a
@ -1129,20 +1114,20 @@ contains
! =======================================================================
! FIND MINIMUM DISTANCE TO LATTICE SURFACES
LAT_COORD: if (coord % lattice /= NONE) then
lat => lattices(coord % lattice) % obj
LAT_COORD: if (p % coord(j) % lattice /= NONE) then
lat => lattices(p % coord(j) % lattice) % obj
LAT_TYPE: select type(lat)
type is (RectLattice)
! copy local coordinates
x = coord % xyz(1)
y = coord % xyz(2)
z = coord % xyz(3)
x = p % coord(j) % xyz(1)
y = p % coord(j) % xyz(2)
z = p % coord(j) % xyz(3)
! determine oncoming edge
x0 = sign(lat % pitch(1) * 0.5_8, u)
y0 = sign(lat % pitch(2) * 0.5_8, v)
x0 = sign(lat % pitch(1) * HALF, u)
y0 = sign(lat % pitch(2) * HALF, v)
! left and right sides
if (abs(x - x0) < FP_PRECISION) then
@ -1179,7 +1164,7 @@ contains
end if
if (lat % is_3d) then
z0 = sign(lat % pitch(3) * 0.5_8, w)
z0 = sign(lat % pitch(3) * HALF, w)
! top and bottom sides
if (abs(z - z0) < FP_PRECISION) then
@ -1202,18 +1187,14 @@ contains
type is (HexLattice) LAT_TYPE
! Copy local coordinates.
z = coord % xyz(3)
i_xyz(1) = coord % lattice_x
i_xyz(2) = coord % lattice_y
i_xyz(3) = coord % lattice_z
parent_coord => p % coord0
do while(.not. associated(parent_coord % next, coord))
parent_coord => parent_coord % next
end do
z = p % coord(j) % xyz(3)
i_xyz(1) = p % coord(j) % lattice_x
i_xyz(2) = p % coord(j) % lattice_y
i_xyz(3) = p % coord(j) % lattice_z
! Compute velocities along the hexagonal axes.
beta_dir = u*sqrt(3.0_8)/2.0_8 + v/2.0_8
gama_dir = u*sqrt(3.0_8)/2.0_8 - v/2.0_8
beta_dir = u*sqrt(THREE)/TWO + v/TWO
gama_dir = u*sqrt(THREE)/TWO - v/TWO
! Note that hexagonal lattice distance calculations are performed
! using the particle's coordinates relative to the neighbor lattice
@ -1223,13 +1204,13 @@ contains
! of hex lattices.
! Upper right and lower left sides.
edge = -sign(lat % pitch(1)/2.0_8, beta_dir) ! Oncoming edge
if (beta_dir > 0.0) then
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[1, 0, 0])
edge = -sign(lat % pitch(1)/TWO, beta_dir) ! Oncoming edge
if (beta_dir > ZERO) then
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, 0, 0])
else
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[-1, 0, 0])
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 0, 0])
end if
beta = xyz_t(1)*sqrt(3.0_8)/2.0_8 + xyz_t(2)/2.0_8
beta = xyz_t(1)*sqrt(THREE)/TWO + xyz_t(2)/TWO
if (abs(beta - edge) < FP_PRECISION) then
d = INFINITY
else if (beta_dir == ZERO) then
@ -1246,13 +1227,13 @@ contains
end if
! Lower right and upper left sides.
edge = -sign(lat % pitch(1)/2.0_8, gama_dir) ! Oncoming edge
if (gama_dir > 0.0) then
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[1, -1, 0])
edge = -sign(lat % pitch(1)/TWO, gama_dir) ! Oncoming edge
if (gama_dir > ZERO) then
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, -1, 0])
else
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[-1, 1, 0])
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 1, 0])
end if
gama = xyz_t(1)*sqrt(3.0_8)/2.0_8 - xyz_t(2)/2.0_8
gama = xyz_t(1)*sqrt(THREE)/TWO - xyz_t(2)/TWO
if (abs(gama - edge) < FP_PRECISION) then
d = INFINITY
else if (gama_dir == ZERO) then
@ -1271,11 +1252,11 @@ contains
end if
! Upper and lower sides.
edge = -sign(lat % pitch(1)/2.0_8, v) ! Oncoming edge
if (v > 0.0) then
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[0, 1, 0])
edge = -sign(lat % pitch(1)/TWO, v) ! Oncoming edge
if (v > ZERO) then
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, 1, 0])
else
xyz_t = lat % get_local_xyz(parent_coord % xyz, i_xyz+[0, -1, 0])
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, -1, 0])
end if
if (abs(xyz_t(2) - edge) < FP_PRECISION) then
d = INFINITY
@ -1296,7 +1277,7 @@ contains
! Top and bottom sides.
if (lat % is_3d) then
z0 = sign(lat % pitch(2) * 0.5_8, w)
z0 = sign(lat % pitch(2) * HALF, w)
if (abs(z - z0) < FP_PRECISION) then
d = INFINITY
@ -1317,10 +1298,10 @@ contains
end if
end select LAT_TYPE
if (d_lat < 0.0) then
if (d_lat < ZERO) then
call handle_lost_particle(p, "Particle " // trim(to_str(p % id)) &
&//" had a negative distance to a lattice boundary. d = " &
&//trim(to_str(d_lat)))
//" had a negative distance to a lattice boundary. d = " &
//trim(to_str(d_lat)))
end if
end if LAT_COORD
@ -1333,24 +1314,19 @@ contains
dist = d_surf
surface_crossed = level_surf_cross
lattice_translation(:) = [0, 0, 0]
final_coord => coord
next_level = j
end if
else
if ((dist - d_lat)/dist >= FP_REL_PRECISION) then
dist = d_lat
surface_crossed = None
lattice_translation(:) = level_lat_trans
final_coord => coord
next_level = j
end if
end if
coord => coord % next
end do LEVEL_LOOP
! Move particle to appropriate coordinate level
if (associated(final_coord)) p % coord => final_coord
end subroutine distance_to_boundary
!===============================================================================
@ -1365,6 +1341,7 @@ contains
type(Surface), pointer :: surf ! surface
logical :: s ! sense of particle
integer :: j
real(8) :: x,y,z ! coordinates of particle
real(8) :: func ! surface function evaluated at point
real(8) :: A ! coefficient on x for plane
@ -1374,9 +1351,10 @@ contains
real(8) :: x0,y0,z0 ! coefficients for quadratic surfaces / box
real(8) :: r ! radius for quadratic surfaces
x = p % coord % xyz(1)
y = p % coord % xyz(2)
z = p % coord % xyz(3)
j = p % n_coord
x = p % coord(j) % xyz(1)
y = p % coord(j) % xyz(2)
z = p % coord(j) % xyz(3)
select case (surf % type)
case (SURF_PX)
@ -1468,7 +1446,7 @@ contains
if (abs(func) < FP_COINCIDENT) then
! Particle may be coincident with this surface. Artifically move the
! particle forward a tiny bit.
p % coord % xyz = p % coord % xyz + TINY_BIT * p % coord % uvw
p % coord(j) % xyz = p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw
s = sense(p, surf)
elseif (func > 0) then
s = .true.
@ -1591,8 +1569,8 @@ contains
subroutine calc_offsets(goal, map, univ, counts, found)
integer, intent(inout) :: goal ! target universe ID
integer, intent(inout) :: map ! map index in vector of maps
integer, intent(in) :: goal ! target universe ID
integer, intent(in) :: map ! map index in vector of maps
type(Universe), intent(in) :: univ ! universe searching in
integer, intent(inout) :: counts(:,:) ! target count
logical, intent(inout) :: found(:,:) ! target found
@ -1692,11 +1670,11 @@ contains
recursive function count_target(univ, counts, found, goal, map) result(count)
type(Universe), intent(inout) :: univ ! universe to search through
integer, intent(inout) :: counts(:,:) ! target count
logical, intent(inout) :: found(:,:) ! target found
integer, intent(inout) :: goal ! target universe ID
integer, intent(inout) :: map ! current map
type(Universe), intent(in) :: univ ! universe to search through
integer, intent(inout) :: counts(:,:) ! target count
logical, intent(inout) :: found(:,:) ! target found
integer, intent(in) :: goal ! target universe ID
integer, intent(in) :: map ! current map
integer :: i ! index over cells
integer :: j, k, m ! indices in lattice
@ -1907,5 +1885,82 @@ contains
end subroutine count_instance
!===============================================================================
! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in
! the geometry
!===============================================================================
recursive function maximum_levels(univ) result(levels)
type(Universe), intent(in) :: univ ! universe to search through
integer :: levels ! maximum number of levels for this universe
integer :: i ! index over cells
integer :: j, k, m ! indices in lattice
integer :: levels_below ! max levels below this universe
type(Cell), pointer :: c ! pointer to current cell
type(Universe), pointer :: next_univ ! next universe to loop through
class(Lattice), pointer :: lat ! pointer to current lattice
levels_below = 0
do i = 1, univ % n_cells
c => cells(univ % cells(i))
! ====================================================================
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
if (c % type == CELL_FILL) then
next_univ => universes(c % fill)
levels_below = max(levels_below, maximum_levels(next_univ))
! ====================================================================
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
elseif (c % type == CELL_LATTICE) then
! Set current lattice
lat => lattices(c % fill) % obj
select type (lat)
type is (RectLattice)
! Loop over lattice coordinates
do j = 1, lat % n_cells(1)
do k = 1, lat % n_cells(2)
do m = 1, lat % n_cells(3)
next_univ => universes(lat % universes(j, k, m))
levels_below = max(levels_below, maximum_levels(next_univ))
end do
end do
end do
type is (HexLattice)
! Loop over lattice coordinates
do m = 1, lat % n_axial
do k = 1, 2*lat % n_rings - 1
do j = 1, 2*lat % n_rings - 1
! This array location is never used
if (j + k < lat % n_rings + 1) then
cycle
! This array location is never used
else if (j + k > 3*lat % n_rings - 1) then
cycle
else
next_univ => universes(lat % universes(j, k, m))
levels_below = max(levels_below, maximum_levels(next_univ))
end if
end do
end do
end do
end select
end if
end do
levels = 1 + levels_below
end function maximum_levels
end module geometry

View file

@ -1,5 +1,7 @@
module geometry_header
use constants, only: HALF, TWO, THREE
implicit none
!===============================================================================
@ -29,7 +31,7 @@ module geometry_header
integer :: outer ! universe to tile outside the lat
logical :: is_3d ! Lattice has cells on z axis
integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets
contains
procedure(are_valid_indices_), deferred :: are_valid_indices
@ -121,7 +123,7 @@ module geometry_header
character(len=52) :: name = "" ! User-defined name
integer :: type ! Type of surface
real(8), allocatable :: coeffs(:) ! Definition of surface
integer, allocatable :: &
integer, allocatable :: &
neighbor_pos(:), & ! List of cells on positive side
neighbor_neg(:) ! List of cells on negative side
integer :: bc ! Boundary condition
@ -141,7 +143,7 @@ module geometry_header
integer :: material ! Material within cell (0 for universe)
integer :: n_surfaces ! Number of surfaces within
integer, allocatable :: offset (:) ! Distribcell offset for tally counter
integer, allocatable :: &
integer, allocatable :: &
& surfaces(:) ! List of surfaces bounding cell -- note that
! parentheses, union, etc operators will be listed
! here too
@ -189,7 +191,7 @@ contains
real(8), intent(in) :: global_xyz(3)
integer :: i_xyz(3)
real(8) :: xyz(3) ! global_xyz alias
real(8) :: xyz(3) ! global_xyz alias
xyz = global_xyz
@ -209,7 +211,7 @@ contains
real(8), intent(in) :: global_xyz(3)
integer :: i_xyz(3)
real(8) :: xyz(3) ! global_xyz alias
real(8) :: xyz(3) ! global_xyz alias
real(8) :: alpha ! Skewed coord axis
real(8) :: xyz_t(3) ! Local xyz
real(8) :: dists(4) ! Squared distances from cell centers
@ -220,16 +222,16 @@ contains
! Index z direction.
if (this % is_3d) then
i_xyz(3) = ceiling((xyz(3) - this % center(3))/this % pitch(2) + 0.5_8)&
&+ this % n_axial/2
i_xyz(3) = ceiling((xyz(3) - this % center(3))/this % pitch(2) + HALF)&
+ this % n_axial/2
else
i_xyz(3) = 1
end if
! Convert coordinates into skewed bases. The (x, alpha) basis is used to
! find the index of the global coordinates to within 4 cells.
alpha = xyz(2) - xyz(1) / sqrt(3.0_8)
i_xyz(1) = floor(xyz(1) / (sqrt(3.0_8) / 2.0_8 * this % pitch(1)))
alpha = xyz(2) - xyz(1) / sqrt(THREE)
i_xyz(1) = floor(xyz(1) / (sqrt(THREE) / TWO * this % pitch(1)))
i_xyz(2) = floor(alpha / this % pitch(1))
! Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but
@ -279,12 +281,12 @@ contains
xyz = global_xyz
local_xyz(1) = xyz(1) - (this % lower_left(1) + &
&(i_xyz(1) - 0.5_8)*this % pitch(1))
(i_xyz(1) - HALF)*this % pitch(1))
local_xyz(2) = xyz(2) - (this % lower_left(2) + &
&(i_xyz(2) - 0.5_8)*this % pitch(2))
(i_xyz(2) - HALF)*this % pitch(2))
if (this % is_3d) then
local_xyz(3) = xyz(3) - (this % lower_left(3) + &
&(i_xyz(3) - 0.5_8)*this % pitch(3))
(i_xyz(3) - HALF)*this % pitch(3))
else
local_xyz(3) = xyz(3)
end if
@ -304,14 +306,14 @@ contains
! x_l = x_g - (center + pitch_x*cos(30)*index_x)
local_xyz(1) = xyz(1) - (this % center(1) + &
&sqrt(3.0_8) / 2.0_8 * (i_xyz(1) - this % n_rings) * this % pitch(1))
sqrt(THREE) / TWO * (i_xyz(1) - this % n_rings) * this % pitch(1))
! y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y)
local_xyz(2) = xyz(2) - (this % center(2) + &
&(i_xyz(2) - this % n_rings) * this % pitch(1) + &
&(i_xyz(1) - this % n_rings) * this % pitch(1) / 2.0_8)
(i_xyz(2) - this % n_rings) * this % pitch(1) + &
(i_xyz(1) - this % n_rings) * this % pitch(1) / TWO)
if (this % is_3d) then
local_xyz(3) = xyz(3) - this % center(3) &
&+ (this % n_axial/2 - i_xyz(3) + 1) * this % pitch(2)
+ (this % n_axial/2 - i_xyz(3) + 1) * this % pitch(2)
else
local_xyz(3) = xyz(3)
end if

View file

@ -19,6 +19,9 @@ module global
#ifdef HDF5
use hdf5_interface, only: HID_T
#endif
#ifdef MPIF08
use mpi_f08
#endif
implicit none
save
@ -112,11 +115,24 @@ module global
! Global tallies
! 1) collision estimate of k-eff
! 2) track-length estimate of k-eff
! 3) leakage fraction
! 2) absorption estimate of k-eff
! 3) track-length estimate of k-eff
! 4) leakage fraction
type(TallyResult), 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)
! Tally map structure
type(TallyMap), allocatable :: tally_maps(:)
@ -212,8 +228,13 @@ module global
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
type(MPI_Datatype) :: MPI_TALLYRESULT
#else
integer :: MPI_BANK ! MPI datatype for fission bank
integer :: MPI_TALLYRESULT ! MPI datatype for TallyResult
#endif
#ifdef _OPENMP
integer :: n_threads = NONE ! number of OpenMP threads
@ -243,8 +264,8 @@ module global
! VARIANCE REDUCTION VARIABLES
logical :: survival_biasing = .false.
real(8) :: weight_cutoff = 0.25
real(8) :: weight_survive = 1.0
real(8) :: weight_cutoff = 0.25_8
real(8) :: weight_survive = ONE
! ============================================================================
! HDF5 VARIABLES

View file

@ -7,7 +7,7 @@ module hdf5_interface
use, intrinsic :: ISO_C_BINDING
#ifdef MPI
use mpi, only: MPI_COMM_WORLD, MPI_INFO_NULL
use message_passing, only: MPI_COMM_WORLD, MPI_INFO_NULL
#endif
implicit none
@ -149,7 +149,12 @@ contains
! Setup file access property list with parallel I/O access
call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err)
#ifdef MPIF08
call h5pset_fapl_mpio_f(plist, MPI_COMM_WORLD%MPI_VAL, &
MPI_INFO_NULL%MPI_VAL, hdf5_err)
#else
call h5pset_fapl_mpio_f(plist, MPI_COMM_WORLD, MPI_INFO_NULL, hdf5_err)
#endif
! Create the file collectively
call h5fcreate_f(trim(filename), H5F_ACC_TRUNC_F, file_id, hdf5_err, &
@ -174,7 +179,12 @@ contains
! Setup file access property list with parallel I/O access
call h5pcreate_f(H5P_FILE_ACCESS_F, plist, hdf5_err)
#ifdef MPIF08
call h5pset_fapl_mpio_f(plist, MPI_COMM_WORLD%MPI_VAL, &
MPI_INFO_NULL%MPI_VAL, hdf5_err)
#else
call h5pset_fapl_mpio_f(plist, MPI_COMM_WORLD, MPI_INFO_NULL, hdf5_err)
#endif
! Determine access type
open_mode = H5F_ACC_RDONLY_F

View file

@ -7,7 +7,8 @@ module initialize
use set_header, only: SetInt
use energy_grid, only: logarithmic_grid, grid_method, unionized_grid
use error, only: fatal_error, warning
use geometry, only: neighbor_lists, count_instance, calc_offsets
use geometry, only: neighbor_lists, count_instance, calc_offsets, &
maximum_levels
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice,&
&BASE_UNIVERSE
use global
@ -26,7 +27,7 @@ module initialize
use tally_initialize, only: configure_tallies
#ifdef MPI
use mpi
use message_passing
#endif
#ifdef _OPENMP
@ -95,11 +96,20 @@ contains
! 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(BASE_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
! With the AWRs from the xs_listings, change all material specifications
! so that they contain atom percents summing to 1
@ -185,11 +195,17 @@ contains
subroutine initialize_mpi()
integer :: bank_blocks(4) ! Count for each datatype
#ifdef MPIF08
type(MPI_Datatype) :: bank_types(4)
type(MPI_Datatype) :: result_types(1)
type(MPI_Datatype) :: temp_type
#else
integer :: bank_types(4) ! Datatypes
integer(MPI_ADDRESS_KIND) :: bank_disp(4) ! Displacements
integer :: temp_type ! temporary derived type
integer :: result_blocks(1) ! Count for each datatype
integer :: result_types(1) ! Datatypes
integer :: temp_type ! temporary derived type
#endif
integer(MPI_ADDRESS_KIND) :: bank_disp(4) ! Displacements
integer :: result_blocks(1) ! Count for each datatype
integer(MPI_ADDRESS_KIND) :: result_disp(1) ! Displacements
integer(MPI_ADDRESS_KIND) :: result_base_disp ! Base displacement
integer(MPI_ADDRESS_KIND) :: lower_bound ! Lower bound for TallyResult
@ -936,7 +952,7 @@ contains
count_all = .false.
! Loop over tallies
! Loop over tallies
do i = 1, n_tallies
! Get pointer to tally
@ -954,25 +970,25 @@ contains
if (size(tally % filters(j) % int_bins) > 1) then
call fatal_error("A distribcell filter was specified with &
&multiple bins. This feature is not supported.")
end if
end if
end if
end do
end do
if (count_all) then
univ => universes(BASE_UNIVERSE)
! sum the number of occurrences of all cells
call count_instance(univ)
! Loop over tallies
do i = 1, n_tallies
! Loop over tallies
do i = 1, n_tallies
! Get pointer to tally
tally => tallies(i)
tally => tallies(i)
! Initialize the filters
do j = 1, tally % n_filters
@ -993,7 +1009,7 @@ contains
! Calculate offsets for each target distribcell
do i = 1, n_maps
do j = 1, n_universes
do j = 1, n_universes
univ => universes(j)
call calc_offsets(univ_list(i), i, univ, counts, found)
end do
@ -1003,7 +1019,7 @@ contains
deallocate(counts)
deallocate(found)
deallocate(univ_list)
end subroutine prepare_distribcell
!===============================================================================
@ -1018,31 +1034,31 @@ contains
logical, intent(out), allocatable :: found(:,:) ! Target found
integer :: i, j, k, l, m ! Loop counters
type(SetInt) :: cell_list ! distribells to track
type(SetInt) :: cell_list ! distribells to track
type(Universe), pointer :: univ ! pointer to universe
class(Lattice), pointer :: lat ! pointer to lattice
type(TallyObject), pointer :: tally ! pointer to tally
type(TallyFilter), pointer :: filter ! pointer to filter
! Begin gathering list of cells in distribcell tallies
n_maps = 0
! Populate list of distribcells to track
do i = 1, n_tallies
tally => tallies(i)
do j = 1, tally % n_filters
filter => tally % filters(j)
filter => tally % filters(j)
if (filter % type == FILTER_DISTRIBCELL) then
if (.not. cell_list % contains(filter % int_bins(1))) then
call cell_list % add(filter % int_bins(1))
end if
end if
end if
end do
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
@ -1053,7 +1069,7 @@ contains
end if
end do
end do
! Allocate the list of offset tables for each unique universe
allocate(univ_list(n_maps))
@ -1071,34 +1087,34 @@ contains
univ => universes(i)
do j = 1, univ % n_cells
if (cell_list % contains(univ % cells(j))) then
! Loop over all tallies
! Loop over all tallies
do l = 1, n_tallies
tally => tallies(l)
do m = 1, tally % n_filters
filter => tally % filters(m)
! Loop over only distribcell filters
! If filter points to cell we just found, set offset index
if (filter % type == FILTER_DISTRIBCELL) then
if (filter % type == FILTER_DISTRIBCELL) then
if (filter % int_bins(1) == univ % cells(j)) then
filter % offset = k
end if
end if
end do
end do
end do
univ_list(k) = univ % id
k = k + 1
end if
end do
end do
! Allocate the offset tables for lattices
! Allocate the offset tables for lattices
do i = 1, n_lattices
lat => lattices(i) % obj

View file

@ -1797,11 +1797,11 @@ contains
case ('g/cc', 'g/cm3')
mat % density = -val
case ('kg/m3')
mat % density = -0.001 * val
mat % density = -0.001_8 * val
case ('atom/b-cm')
mat % density = val
case ('atom/cm3', 'atom/cc')
mat % density = 1.0e-24 * val
mat % density = 1.0e-24_8 * val
case default
call fatal_error("Unkwown units '" // trim(units) &
&// "' specified on material " // trim(to_str(mat % id)))

View file

@ -1,6 +1,6 @@
module math
use constants, only: PI, ONE, TWO, ZERO
use constants
use random_lcg, only: prn
implicit none
@ -41,27 +41,27 @@ contains
q = sqrt(-TWO*log(p))
z = (((((c(1)*q + c(2))*q + c(3))*q + c(4))*q + c(5))*q + c(6)) / &
((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + 1.)
((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + ONE)
elseif (p <= 1. - p_low) then
elseif (p <= ONE - p_low) then
! Rational approximation for central region
q = p - 0.5
q = p - HALF
r = q*q
z = (((((a(1)*r + a(2))*r + a(3))*r + a(4))*r + a(5))*r + a(6))*q / &
(((((b(1)*r + b(2))*r + b(3))*r + b(4))*r + b(5))*r + 1.)
(((((b(1)*r + b(2))*r + b(3))*r + b(4))*r + b(5))*r + ONE)
else
! Rational approximation for upper region
q = sqrt(-2*log(1. - p))
q = sqrt(-TWO*log(ONE - p))
z = -(((((c(1)*q + c(2))*q + c(3))*q + c(4))*q + c(5))*q + c(6)) / &
((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + 1.)
((((d(1)*q + d(2))*q + d(3))*q + d(4))*q + ONE)
endif
! Refinement based on Newton's method
#ifndef NO_F2008
z = z - (0.5 * erfc(-z/sqrt(TWO)) - p) * sqrt(TWO*PI) * exp(0.5*z*z)
z = z - (HALF * erfc(-z/sqrt(TWO)) - p) * sqrt(TWO*PI) * exp(HALF*z*z)
#endif
end function normal_percentile
@ -86,13 +86,13 @@ contains
! For one degree of freedom, the t-distribution becomes a Cauchy
! distribution whose cdf we can invert directly
t = tan(PI*(p - 0.5))
t = tan(PI*(p - HALF))
elseif (df == 2) then
! For two degrees of freedom, the cdf is given by 1/2 + x/(2*sqrt(x^2 +
! 2)). This can be directly inverted to yield the solution below
t = TWO*sqrt(TWO)*(p - 0.5)/sqrt(ONE - 4.*(p - 0.5)**2)
t = TWO*sqrt(TWO)*(p - HALF)/sqrt(ONE - FOUR*(p - HALF)**2)
else
@ -102,12 +102,12 @@ contains
! 16 (4), pp. 1123-1132 (1987).
n = real(df,8)
k = 1./(n - 2.)
k = ONE/(n - TWO)
z = normal_percentile(p)
z2 = z * z
t = sqrt(n*k) * (z + (z2 - 3.)*z*k/4. + ((5.*z2 - 56.)*z2 + &
75.)*z*k*k/96. + (((z2 - 27.)*3.*z2 + 417.)*z2 - 315.) &
*z*k*k*k/384.)
t = sqrt(n*k) * (z + (z2 - THREE)*z*k/FOUR + ((5._8*z2 - 56._8)*z2 + &
75._8)*z*k*k/96._8 + (((z2 - 27._8)*THREE*z2 + 417._8)*z2 - 315._8) &
*z*k*k*k/384._8)
end if
@ -134,7 +134,7 @@ contains
case(1)
pnx = x
case(2)
pnx = 1.5_8 * x * x - 0.5_8
pnx = 1.5_8 * x * x - HALF
case(3)
pnx = 2.5_8 * x * x * x - 1.5_8 * x
case(4)
@ -199,37 +199,37 @@ contains
rn(3) = ONE*sqrt(w2m1) * cos(phi)
case (2)
! l = 2, m = -2
rn(1) = 0.288675134594813_8 * (-3.0_8 * w**2 + 3.0_8) * sin(TWO*phi)
rn(1) = 0.288675134594813_8 * (-THREE * w**2 + THREE) * sin(TWO*phi)
! l = 2, m = -1
rn(2) = 1.73205080756888_8 * w*sqrt(w2m1) * sin(phi)
! l = 2, m = 0
rn(3) = 1.5_8 * w**2 - 0.5_8
rn(3) = 1.5_8 * w**2 - HALF
! l = 2, m = 1
rn(4) = 1.73205080756888_8 * w*sqrt(w2m1) * cos(phi)
! l = 2, m = 2
rn(5) = 0.288675134594813_8 * (-3.0_8 * w**2 + 3.0_8) * cos(TWO*phi)
rn(5) = 0.288675134594813_8 * (-THREE * w**2 + THREE) * cos(TWO*phi)
case (3)
! l = 3, m = -3
rn(1) = 0.790569415042095_8 * (w2m1)**(3.0_8/TWO) * sin(3.0_8 * phi)
rn(1) = 0.790569415042095_8 * (w2m1)**(THREE/TWO) * sin(THREE * phi)
! l = 3, m = -2
rn(2) = 1.93649167310371_8 * w*(w2m1) * sin(TWO*phi)
! l = 3, m = -1
rn(3) = 0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - 3.0_8/TWO) * &
rn(3) = 0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - THREE/TWO) * &
sin(phi)
! l = 3, m = 0
rn(4) = 2.5_8 * w**3 - 1.5_8 * w
! l = 3, m = 1
rn(5) = 0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - 3.0_8/TWO) * &
rn(5) = 0.408248290463863_8*sqrt(w2m1)*((15.0_8/TWO)*w**2 - THREE/TWO) * &
cos(phi)
! l = 3, m = 2
rn(6) = 1.93649167310371_8 * w*(w2m1) * cos(TWO*phi)
! l = 3, m = 3
rn(7) = 0.790569415042095_8 * (w2m1)**(3.0_8/TWO) * cos(3.0_8* phi)
rn(7) = 0.790569415042095_8 * (w2m1)**(THREE/TWO) * cos(THREE* phi)
case (4)
! l = 4, m = -4
rn(1) = 0.739509972887452_8 * (w2m1)**2 * sin(4.0_8*phi)
! l = 4, m = -3
rn(2) = 2.09165006633519_8 * w*(w2m1)**(3.0_8/TWO) * sin(3.0_8* phi)
rn(2) = 2.09165006633519_8 * w*(w2m1)**(THREE/TWO) * sin(THREE* phi)
! l = 4, m = -2
rn(3) = 0.074535599249993_8 * (w2m1)*((105.0_8/TWO)*w**2 - 15.0_8/TWO) * &
sin(TWO*phi)
@ -245,7 +245,7 @@ contains
rn(7) = 0.074535599249993_8 * (w2m1)*((105.0_8/TWO)*w**2 - 15.0_8/TWO) * &
cos(TWO*phi)
! l = 4, m = 3
rn(8) = 2.09165006633519_8 * w*(w2m1)**(3.0_8/TWO) * cos(3.0_8* phi)
rn(8) = 2.09165006633519_8 * w*(w2m1)**(THREE/TWO) * cos(THREE* phi)
! l = 4, m = 4
rn(9) = 0.739509972887452_8 * (w2m1)**2 * cos(4.0_8*phi)
case (5)
@ -254,8 +254,8 @@ contains
! l = 5, m = -4
rn(2) = 2.21852991866236_8 * w*(w2m1)**2 * sin(4.0_8*phi)
! l = 5, m = -3
rn(3) = 0.00996023841111995_8 * (w2m1)**(3.0_8/TWO)* &
((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * sin(3.0_8*phi)
rn(3) = 0.00996023841111995_8 * (w2m1)**(THREE/TWO)* &
((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * sin(THREE*phi)
! l = 5, m = -2
rn(4) = 0.0487950036474267_8 * (w2m1)*((315.0_8/TWO)*w**3 - 105.0_8/TWO*w) * &
sin(TWO*phi)
@ -271,8 +271,8 @@ contains
rn(8) = 0.0487950036474267_8 * (w2m1)* &
((315.0_8/TWO)*w**3 - 105.0_8/TWO*w) * cos(TWO*phi)
! l = 5, m = 3
rn(9) = 0.00996023841111995_8 * (w2m1)**(3.0_8/TWO)* &
((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * cos(3.0_8*phi)
rn(9) = 0.00996023841111995_8 * (w2m1)**(THREE/TWO)* &
((945.0_8 /TWO)*w**2 - 105.0_8/TWO) * cos(THREE*phi)
! l = 5, m = 4
rn(10) = 2.21852991866236_8 * w*(w2m1)**2 * cos(4.0_8*phi)
! l = 5, m = 5
@ -286,8 +286,8 @@ contains
rn(3) = 0.00104990131391452_8 * (w2m1)**2 * &
((10395.0_8/TWO)*w**2 - 945.0_8/TWO) * sin(4.0_8*phi)
! l = 6, m = -3
rn(4) = 0.00575054632785295_8 * (w2m1)**(3.0_8/TWO) * &
((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * sin(3.0_8*phi)
rn(4) = 0.00575054632785295_8 * (w2m1)**(THREE/TWO) * &
((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * sin(THREE*phi)
! l = 6, m = -2
rn(5) = 0.0345032779671177_8 * (w2m1) * &
((3465.0_8/8.0_8)*w**4 - 945.0_8/4.0_8 * w**2 + 105.0_8/8.0_8) * sin(TWO*phi)
@ -303,8 +303,8 @@ contains
rn(9) = 0.0345032779671177_8 * (w2m1) * &
((3465.0_8/8.0_8)*w**4 -945.0_8/4.0_8 * w**2 + 105.0_8/8.0_8) * cos(TWO*phi)
! l = 6, m = 3
rn(10) = 0.00575054632785295_8 * (w2m1)**(3.0_8/TWO) * &
((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * cos(3.0_8*phi)
rn(10) = 0.00575054632785295_8 * (w2m1)**(THREE/TWO) * &
((3465.0_8/TWO)*w**3 - 945.0_8/TWO*w) * cos(THREE*phi)
! l = 6, m = 4
rn(11) = 0.00104990131391452_8 * (w2m1)**2 * &
((10395.0_8/TWO)*w**2 - 945.0_8/TWO) * cos(4.0_8*phi)
@ -324,9 +324,9 @@ contains
rn(4) = 0.000548293079133141_8 * (w2m1)**2* &
((45045.0_8/TWO)*w**3 - 10395.0_8/TWO*w) * sin(4.0_8*phi)
! l = 7, m = -3
rn(5) = 0.00363696483726654_8 * (w2m1)**(3.0_8/TWO)* &
rn(5) = 0.00363696483726654_8 * (w2m1)**(THREE/TWO)* &
((45045.0_8/8.0_8)*w**4 - 10395.0_8/4.0_8 * w**2 + 945.0_8/8.0_8)* &
sin(3.0_8*phi)
sin(THREE*phi)
! l = 7, m = -2
rn(6) = 0.025717224993682_8 * (w2m1)* &
((9009.0_8/8.0_8)*w**5 -3465.0_8/4.0_8 * w**3 + (945.0_8/8.0_8)*w)* &
@ -346,9 +346,9 @@ contains
((9009.0_8/8.0_8)*w**5 -3465.0_8/4.0_8 * w**3 + (945.0_8/8.0_8)*w)* &
cos(TWO*phi)
! l = 7, m = 3
rn(11) = 0.00363696483726654_8 * (w2m1)**(3.0_8/TWO)* &
rn(11) = 0.00363696483726654_8 * (w2m1)**(THREE/TWO)* &
((45045.0_8/8.0_8)*w**4 - 10395.0_8/4.0_8 * w**2 + 945.0_8/8.0_8)* &
cos(3.0_8*phi)
cos(THREE*phi)
! l = 7, m = 4
rn(12) = 0.000548293079133141_8 * (w2m1)**2 * &
((45045.0_8/TWO)*w**3 - 10395.0_8/TWO*w) * cos(4.0_8*phi)
@ -374,8 +374,8 @@ contains
rn(5) = 0.000316557156832328_8 * (w2m1)**2* &
((675675.0_8/8.0_8)*w**4 - 135135.0_8/4.0_8 * w**2 + 10395.0_8/8.0_8) * sin(4.0_8*phi)
! l = 8, m = -3
rn(6) = 0.00245204119306875_8 * (w2m1)**(3.0_8/TWO)* &
((135135.0_8/8.0_8)*w**5 - 45045.0_8/4.0_8 * w**3 + (10395.0_8/8.0_8)*w) * sin(3.0_8*phi)
rn(6) = 0.00245204119306875_8 * (w2m1)**(THREE/TWO)* &
((135135.0_8/8.0_8)*w**5 - 45045.0_8/4.0_8 * w**3 + (10395.0_8/8.0_8)*w) * sin(THREE*phi)
! l = 8, m = -2
rn(7) = 0.0199204768222399_8 * (w2m1)* &
((45045.0_8/16.0_8)*w**6- 45045.0_8/16.0_8 * w**4 + &
@ -396,9 +396,9 @@ contains
45045.0_8/16.0_8 * w**4 + (10395.0_8/16.0_8)*w**2 - &
315.0_8/16.0_8) * cos(TWO*phi)
! l = 8, m = 3
rn(12) = 0.00245204119306875_8 * (w2m1)**(3.0_8/TWO)* &
rn(12) = 0.00245204119306875_8 * (w2m1)**(THREE/TWO)* &
((135135.0_8/8.0_8)*w**5 - 45045.0_8/4.0_8 * w**3 + &
(10395.0_8/8.0_8)*w) * cos(3.0_8*phi)
(10395.0_8/8.0_8)*w) * cos(THREE*phi)
! l = 8, m = 4
rn(13) = 0.000316557156832328_8 * (w2m1)**2*((675675.0_8/8.0_8)*w**4 - &
135135.0_8/4.0_8 * w**2 + 10395.0_8/8.0_8) * cos(4.0_8*phi)
@ -431,9 +431,9 @@ contains
rn(6) = 0.000196320414650061_8 * (w2m1)**2*((2297295.0_8/8.0_8)*w**5 - &
675675.0_8/4.0_8 * w**3 + (135135.0_8/8.0_8)*w) * sin(4.0_8*phi)
! l = 9, m = -3
rn(7) = 0.00173385495536766_8 * (w2m1)**(3.0_8/TWO)* &
rn(7) = 0.00173385495536766_8 * (w2m1)**(THREE/TWO)* &
((765765.0_8/16.0_8)*w**6 - 675675.0_8/16.0_8 * w**4 + &
(135135.0_8/16.0_8)*w**2 - 3465.0_8/16.0_8) * sin(3.0_8*phi)
(135135.0_8/16.0_8)*w**2 - 3465.0_8/16.0_8) * sin(THREE*phi)
! l = 9, m = -2
rn(8) = 0.0158910431540932_8 * (w2m1)*((109395.0_8/16.0_8)*w**7- &
135135.0_8/16.0_8 * w**5 + (45045.0_8/16.0_8)*w**3 - 3465.0_8/16.0_8 * w)* &
@ -452,9 +452,9 @@ contains
135135.0_8/16.0_8 * w**5 + (45045.0_8/16.0_8)*w**3 - 3465.0_8/ 16.0_8 * w) * &
cos(TWO*phi)
! l = 9, m = 3
rn(13) = 0.00173385495536766_8 * (w2m1)**(3.0_8/TWO)*((765765.0_8/16.0_8)*w**6 - &
rn(13) = 0.00173385495536766_8 * (w2m1)**(THREE/TWO)*((765765.0_8/16.0_8)*w**6 - &
675675.0_8/16.0_8 * w**4 + (135135.0_8/16.0_8)*w**2 - 3465.0_8/16.0_8)* &
cos(3.0_8*phi)
cos(THREE*phi)
! l = 9, m = 4
rn(14) = 0.000196320414650061_8 * (w2m1)**2*((2297295.0_8/8.0_8)*w**5 - &
675675.0_8/4.0_8 * w**3 + (135135.0_8/8.0_8)*w) * cos(4.0_8*phi)
@ -494,9 +494,9 @@ contains
11486475.0_8/16.0_8 * w**4 + (2027025.0_8/16.0_8)*w**2 - &
45045.0_8/16.0_8) * sin(4.0_8*phi)
! l = 10, m = -3
rn(8) = 0.00127230170115096_8 * (w2m1)**(3.0_8/TWO)* &
rn(8) = 0.00127230170115096_8 * (w2m1)**(THREE/TWO)* &
((2078505.0_8/16.0_8)*w**7 - 2297295.0_8/16.0_8 * w**5 + &
(675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * sin(3.0_8*phi)
(675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * sin(THREE*phi)
! l = 10, m = -2
rn(9) = 0.012974982402692_8 * (w2m1)*((2078505.0_8/128.0_8)*w**8 - &
765765.0_8/32.0_8 * w**6 + (675675.0_8/64.0_8)*w**4 - &
@ -517,9 +517,9 @@ contains
765765.0_8/32.0_8 * w**6 + (675675.0_8/64.0_8)*w**4 -&
45045.0_8/32.0_8 * w**2 + 3465.0_8/128.0_8) * cos(TWO*phi)
! l = 10, m = 3
rn(14) = 0.00127230170115096_8 * (w2m1)**(3.0_8/TWO)* &
rn(14) = 0.00127230170115096_8 * (w2m1)**(THREE/TWO)* &
((2078505.0_8/16.0_8)*w**7 - 2297295.0_8/16.0_8 * w**5 + &
(675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * cos(3.0_8*phi)
(675675.0_8/16.0_8)*w**3 - 45045.0_8/16.0_8 * w) * cos(THREE*phi)
! l = 10, m = 4
rn(15) = 0.000128521880085575_8 * (w2m1)**2*((14549535.0_8/16.0_8)*w**6 - &
11486475.0_8/16.0_8 * w**4 + (2027025.0_8/16.0_8)*w**2 - &

View file

@ -7,7 +7,7 @@ module mesh
use search, only: binary_search
#ifdef MPI
use mpi
use message_passing
#endif
implicit none

11
src/message_passing.F90 Normal file
View file

@ -0,0 +1,11 @@
module message_passing
#ifdef MPI
#ifdef MPIF08
use mpi_f08
#else
use mpi
#endif
#endif
end module message_passing

View file

@ -1,10 +1,17 @@
module mpiio_interface
#ifdef MPI
use mpi
#ifndef HDF5
use message_passing
implicit none
#ifdef MPIF08
#define FH_TYPE type(MPI_File)
#else
#define FH_TYPE integer
#endif
integer :: mpiio_err ! MPI error code
! Generic HDF5 write procedure interface
@ -48,11 +55,11 @@ contains
subroutine mpi_create_file(filename, fh)
character(*), intent(in) :: filename ! name of file to create
integer, intent(inout) :: fh ! file handle
FH_TYPE, intent(inout) :: fh ! file handle
! Create the file
call MPI_FILE_OPEN(MPI_COMM_WORLD, filename, MPI_MODE_CREATE + &
MPI_MODE_WRONLY, MPI_INFO_NULL, fh, mpiio_err)
MPI_MODE_WRONLY, MPI_INFO_NULL, fh, mpiio_err)
end subroutine mpi_create_file
@ -64,7 +71,7 @@ contains
character(*), intent(in) :: filename ! name of file to open
character(*), intent(in) :: mode ! open 'r' read, 'w' write
integer, intent(inout) :: fh ! file handle
FH_TYPE, intent(inout) :: fh ! file handle
integer :: open_mode
@ -76,7 +83,7 @@ contains
! Create the file
call MPI_FILE_OPEN(MPI_COMM_WORLD, filename, &
open_mode, MPI_INFO_NULL, fh, mpiio_err)
open_mode, MPI_INFO_NULL, fh, mpiio_err)
end subroutine mpi_open_file
@ -86,7 +93,7 @@ contains
subroutine mpi_close_file(fh)
integer, intent(inout) :: fh ! file handle
FH_TYPE, intent(inout) :: fh ! file handle
call MPI_FILE_CLOSE(fh, mpiio_err)
@ -98,16 +105,16 @@ contains
subroutine mpi_write_integer(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: buffer ! data to write
logical, intent(in) :: collect ! collective I/O
if (collect) then
call MPI_FILE_WRITE_ALL(fh, buffer, 1, MPI_INTEGER, &
MPI_STATUS_IGNORE, mpiio_err)
MPI_STATUS_IGNORE, mpiio_err)
else
call MPI_FILE_WRITE(fh, buffer, 1, MPI_INTEGER, &
MPI_STATUS_IGNORE, mpiio_err)
MPI_STATUS_IGNORE, mpiio_err)
end if
end subroutine mpi_write_integer
@ -118,7 +125,7 @@ contains
subroutine mpi_read_integer(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(inout) :: buffer ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -138,7 +145,7 @@ contains
subroutine mpi_write_integer_1Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of array
integer, intent(in) :: buffer(:) ! data to write
logical, intent(in) :: collect ! collective I/O
@ -159,7 +166,7 @@ contains
subroutine mpi_read_integer_1Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of array
integer, intent(inout) :: buffer(:) ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -180,7 +187,7 @@ contains
subroutine mpi_write_integer_2Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(2) ! length of array
integer, intent(in) :: buffer(length(1),length(2)) ! data to write
logical, intent(in) :: collect ! collective I/O
@ -201,7 +208,7 @@ contains
subroutine mpi_read_integer_2Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(2) ! length of array
integer, intent(inout) :: buffer(length(1),length(2)) ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -222,7 +229,7 @@ contains
subroutine mpi_write_integer_3Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(3) ! length of array
integer, intent(in) :: buffer(length(1),length(2),&
length(3)) ! data to write
@ -244,7 +251,7 @@ contains
subroutine mpi_read_integer_3Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(3) ! length of array
integer, intent(inout) :: buffer(length(1),length(2), &
length(3)) ! read data to here
@ -266,7 +273,7 @@ contains
subroutine mpi_write_integer_4Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(4) ! length of array
integer, intent(in) :: buffer(length(1),length(2),&
length(3),length(4)) ! data to write
@ -288,7 +295,7 @@ contains
subroutine mpi_read_integer_4Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(4) ! length of array
integer, intent(inout) :: buffer(length(1),length(2), &
length(3),length(4)) ! read data to here
@ -310,16 +317,16 @@ contains
subroutine mpi_write_double(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
real(8), intent(in) :: buffer ! data to write
logical, intent(in) :: collect ! collective I/O
if (collect) then
call MPI_FILE_WRITE_ALL(fh, buffer, 1, MPI_REAL8, &
MPI_STATUS_IGNORE, mpiio_err)
MPI_STATUS_IGNORE, mpiio_err)
else
call MPI_FILE_WRITE(fh, buffer, 1, MPI_REAL8, &
MPI_STATUS_IGNORE, mpiio_err)
MPI_STATUS_IGNORE, mpiio_err)
end if
end subroutine mpi_write_double
@ -330,7 +337,7 @@ contains
subroutine mpi_read_double(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
real(8), intent(inout) :: buffer ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -350,7 +357,7 @@ contains
subroutine mpi_write_double_1Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of array
real(8), intent(in) :: buffer(:) ! data to write
logical, intent(in) :: collect ! collective I/O
@ -371,7 +378,7 @@ contains
subroutine mpi_read_double_1Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of array
real(8), intent(inout) :: buffer(:) ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -392,7 +399,7 @@ contains
subroutine mpi_write_double_2Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(2) ! length of array
real(8), intent(in) :: buffer(length(1),length(2)) ! data to write
logical, intent(in) :: collect ! collective I/O
@ -413,7 +420,7 @@ contains
subroutine mpi_read_double_2Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(2) ! length of array
real(8), intent(inout) :: buffer(length(1),length(2)) ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -434,7 +441,7 @@ contains
subroutine mpi_write_double_3Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(3) ! length of array
real(8), intent(in) :: buffer(length(1),length(2),&
length(3)) ! data to write
@ -456,7 +463,7 @@ contains
subroutine mpi_read_double_3Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(3) ! length of array
real(8), intent(inout) :: buffer(length(1),length(2), &
length(3)) ! read data to here
@ -478,7 +485,7 @@ contains
subroutine mpi_write_double_4Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(4) ! length of array
real(8), intent(in) :: buffer(length(1),length(2),&
length(3),length(4)) ! data to write
@ -500,7 +507,7 @@ contains
subroutine mpi_read_double_4Darray(fh, buffer, length, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length(4) ! length of array
real(8), intent(inout) :: buffer(length(1),length(2), &
length(3),length(4)) ! read data to here
@ -522,7 +529,7 @@ contains
subroutine mpi_write_long(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer(8), intent(in) :: buffer ! data to write
logical, intent(in) :: collect ! collective I/O
@ -542,7 +549,7 @@ contains
subroutine mpi_read_long(fh, buffer, collect)
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer(8), intent(inout) :: buffer ! read data to here
logical, intent(in) :: collect ! collective I/O
@ -563,7 +570,7 @@ contains
subroutine mpi_write_string(fh, buffer, length, collect)
character(*), intent(in) :: buffer ! data to write
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of data
logical, intent(in) :: collect ! collective I/O
@ -584,7 +591,7 @@ contains
subroutine mpi_read_string(fh, buffer, length, collect)
character(*), intent(inout) :: buffer ! read data to here
integer, intent(in) :: fh ! file handle
FH_TYPE, intent(in) :: fh ! file handle
integer, intent(in) :: length ! length of string
logical, intent(in) :: collect ! collective I/O
@ -598,5 +605,6 @@ contains
end subroutine mpi_read_string
#endif
#endif
end module mpiio_interface

View file

@ -258,7 +258,6 @@ contains
type(Surface), pointer :: s => null()
type(Universe), pointer :: u => null()
class(Lattice), pointer :: l => null()
type(LocalCoord), pointer :: coord => null()
! display type of particle
select case (p % type)
@ -273,39 +272,34 @@ contains
end select
! loop through each level of universes
coord => p % coord0
i = 0
do while(associated(coord))
do i = 1, p % n_coord
! Print level
write(ou,*) ' Level ' // trim(to_str(i))
write(ou,*) ' Level ' // trim(to_str(i - 1))
! Print cell for this level
if (coord % cell /= NONE) then
c => cells(coord % cell)
if (p % coord(i) % cell /= NONE) then
c => cells(p % coord(i) % cell)
write(ou,*) ' Cell = ' // trim(to_str(c % id))
end if
! Print universe for this level
if (coord % universe /= NONE) then
u => universes(coord % universe)
if (p % coord(i) % universe /= NONE) then
u => universes(p % coord(i) % universe)
write(ou,*) ' Universe = ' // trim(to_str(u % id))
end if
! Print information on lattice
if (coord % lattice /= NONE) then
l => lattices(coord % lattice) % obj
if (p % coord(i) % lattice /= NONE) then
l => lattices(p % coord(i) % lattice) % obj
write(ou,*) ' Lattice = ' // trim(to_str(l % id))
write(ou,*) ' Lattice position = (' // trim(to_str(&
p % coord % lattice_x)) // ',' // trim(to_str(&
p % coord % lattice_y)) // ')'
p % coord(i) % lattice_x)) // ',' // trim(to_str(&
p % coord(i) % lattice_y)) // ')'
end if
! Print local coordinates
write(ou,'(1X,A,3ES12.4)') ' xyz = ', coord % xyz
write(ou,'(1X,A,3ES12.4)') ' uvw = ', coord % uvw
coord => coord % next
i = i + 1
write(ou,'(1X,A,3ES12.4)') ' xyz = ', p % coord(i) % xyz
write(ou,'(1X,A,3ES12.4)') ' uvw = ', p % coord(i) % uvw
end do
! Print surface
@ -2181,9 +2175,9 @@ contains
end select
end function get_label
!===============================================================================
! FIND_OFFSET uses a given map number, a target cell ID, and a target offset
! FIND_OFFSET uses a given map number, a target cell ID, and a target offset
! to build a string which is the path from the base universe to the target cell
! with the given offset
!===============================================================================
@ -2196,7 +2190,7 @@ contains
integer, intent(in) :: final ! Target offset
integer, intent(inout) :: offset ! Current offset
character(100) :: path ! Path to offset
integer :: i, j ! Index over cells
integer :: k, l, m ! Indices in lattice
integer :: old_k, old_l, old_m ! Previous indices in lattice
@ -2212,7 +2206,7 @@ contains
class(Lattice), pointer :: lat ! Pointer to current lattice
n = univ % n_cells
! Write to the geometry stack
if (univ%id == 0) then
path = trim(path) // to_str(univ%id)
@ -2223,31 +2217,31 @@ contains
! Look through all cells in this universe
do i = 1, n
cell_index = univ % cells(i)
cell_index = univ % cells(i)
c => cells(cell_index)
! If the cell ID matches the goal and the offset matches final,
! write to the geometry stack
if (cell_dict % get_key(c % id) == goal .AND. offset == final) then
path = trim(path) // "->" // to_str(c%id)
return
end if
end do
! Find the fill cell or lattice cell that we need to enter
do i = 1, n
later_cell = .false.
cell_index = univ % cells(i)
cell_index = univ % cells(i)
c => cells(cell_index)
this_cell = .false.
this_cell = .false.
! If we got here, we still think the target is in this universe
! or further down, but it's not this exact cell.
! Compare offset to next cell to see if we should enter this cell
! or further down, but it's not this exact cell.
! Compare offset to next cell to see if we should enter this cell
if (i /= n) then
do j = i+1, n
@ -2260,8 +2254,8 @@ contains
cycle
end if
! Break loop once we've found the next cell with an offset
exit
! Break loop once we've found the next cell with an offset
exit
end do
! Ensure we didn't just end the loop by iteration
@ -2278,13 +2272,13 @@ contains
else
lat => lattices(c % fill) % obj
temp_offset = lat % offset(map, 1, 1, 1)
end if
end if
! If the final offset is in the range of offset - temp_offset+offset
! then the goal is in this cell
if (final < temp_offset + offset) then
this_cell = .true.
end if
end if
end if
end if
@ -2341,7 +2335,7 @@ contains
! Loop over lattice coordinates
do k = 1, n_x
do l = 1, n_y
do m = 1, n_z
do m = 1, n_z
if (final >= lat % offset(map, k, l, m) + offset) then
if (k == n_x .and. l == n_y .and. m == n_z) then
@ -2364,14 +2358,14 @@ contains
! Target is at this lattice position
lat_offset = lat % offset(map, old_k, old_l, old_m)
offset = offset + lat_offset
next_univ => universes(lat % universes(old_k, old_l, old_m))
next_univ => universes(lat % universes(old_k, old_l, old_m))
path = trim(path) // "(" // trim(to_str(old_k)) // &
"," // trim(to_str(old_l)) // "," // &
trim(to_str(old_m)) // ")"
call find_offset(map, goal, next_univ, final, offset, path)
return
end if
end do
end do
end do
@ -2404,10 +2398,24 @@ contains
end if
if (final >= lat % offset(map, k, l, m) + offset) then
old_m = m
old_l = l
old_k = k
cycle
if (k == lat % n_rings .and. l == n_y .and. m == n_z) then
! This is last lattice cell, so target must be here
lat_offset = lat % offset(map, k, l, m)
offset = offset + lat_offset
next_univ => universes(lat % universes(k, l, m))
path = trim(path) // "(" // &
trim(to_str(k - lat % n_rings)) // "," // &
trim(to_str(l - lat % n_rings)) // "," // &
trim(to_str(m)) // ")"
call find_offset(map, goal, next_univ, final, offset, &
path)
return
else
old_m = m
old_l = l
old_k = k
cycle
end if
else
! Target is at this lattice position
lat_offset = lat % offset(map, old_k, old_l, old_m)
@ -2429,7 +2437,7 @@ contains
end if
end if
end do
end do
end subroutine find_offset
end module output

View file

@ -7,9 +7,10 @@ module output_interface
#ifdef HDF5
use hdf5_interface
#endif
#else
#ifdef MPI
use mpiio_interface
#endif
#endif
implicit none
@ -22,8 +23,13 @@ module output_interface
integer(HID_T) :: hdf5_fh
integer(HID_T) :: hdf5_grp
#else
integer :: unit_fh
# endif
integer :: unit_fh
#ifdef MPIF08
type(MPI_File) :: mpi_fh
#else
integer :: mpi_fh
#endif
#endif
logical :: serial ! Serial I/O when using MPI/PHDF5
contains
generic, public :: write_data => write_double, &
@ -122,7 +128,7 @@ contains
open(NEWUNIT=self % unit_fh, FILE=filename, ACTION="write", &
STATUS='replace', ACCESS='stream')
else
call mpi_create_file(filename, self % unit_fh)
call mpi_create_file(filename, self % mpi_fh)
end if
#else
open(NEWUNIT=self % unit_fh, FILE=filename, ACTION="write", &
@ -170,7 +176,7 @@ contains
STATUS='old', ACCESS='stream')
end if
else
call mpi_open_file(filename, self % unit_fh, mode)
call mpi_open_file(filename, self % mpi_fh, mode)
end if
#else
! Check for read/write mode to open, default is read only
@ -203,7 +209,7 @@ contains
if (self % serial) then
close(UNIT=self % unit_fh)
else
call mpi_close_file(self % unit_fh)
call mpi_close_file(self % mpi_fh)
end if
#else
close(UNIT=self % unit_fh)
@ -293,7 +299,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer
else
call mpi_write_double(self % unit_fh, buffer, collect_)
call mpi_write_double(self % mpi_fh, buffer, collect_)
end if
#else
write(self % unit_fh) buffer
@ -354,7 +360,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer
else
call mpi_read_double(self % unit_fh, buffer, collect_)
call mpi_read_double(self % mpi_fh, buffer, collect_)
end if
#else
read(self % unit_fh) buffer
@ -417,7 +423,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length)
else
call mpi_write_double_1Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_double_1Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length)
@ -480,7 +486,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length)
else
call mpi_read_double_1Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_double_1Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length)
@ -543,7 +549,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length(1),1:length(2))
else
call mpi_write_double_2Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_double_2Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2))
@ -606,7 +612,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length(1),1:length(2))
else
call mpi_read_double_2Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_double_2Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2))
@ -669,7 +675,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
else
call mpi_write_double_3Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_double_3Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
@ -732,7 +738,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
else
call mpi_read_double_3Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_double_3Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
@ -798,7 +804,7 @@ contains
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
1:length(4))
else
call mpi_write_double_4Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_double_4Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
@ -864,7 +870,7 @@ contains
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
1:length(4))
else
call mpi_read_double_4Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_double_4Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
@ -926,7 +932,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer
else
call mpi_write_integer(self % unit_fh, buffer, collect_)
call mpi_write_integer(self % mpi_fh, buffer, collect_)
end if
#else
write(self % unit_fh) buffer
@ -987,7 +993,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer
else
call mpi_read_integer(self % unit_fh, buffer, collect_)
call mpi_read_integer(self % mpi_fh, buffer, collect_)
end if
#else
read(self % unit_fh) buffer
@ -1050,7 +1056,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length)
else
call mpi_write_integer_1Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_integer_1Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length)
@ -1114,7 +1120,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length)
else
call mpi_read_integer_1Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_integer_1Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length)
@ -1177,7 +1183,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length(1),1:length(2))
else
call mpi_write_integer_2Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_integer_2Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2))
@ -1240,7 +1246,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length(1),1:length(2))
else
call mpi_read_integer_2Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_integer_2Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2))
@ -1303,7 +1309,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
else
call mpi_write_integer_3Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_integer_3Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
@ -1366,7 +1372,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
else
call mpi_read_integer_3Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_integer_3Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3))
@ -1431,7 +1437,7 @@ contains
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
1:length(4))
else
call mpi_write_integer_4Darray(self % unit_fh, buffer, length, collect_)
call mpi_write_integer_4Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
write(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
@ -1497,7 +1503,7 @@ contains
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
1:length(4))
else
call mpi_read_integer_4Darray(self % unit_fh, buffer, length, collect_)
call mpi_read_integer_4Darray(self % mpi_fh, buffer, length, collect_)
end if
#else
read(self % unit_fh) buffer(1:length(1),1:length(2),1:length(3), &
@ -1560,7 +1566,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer
else
call mpi_write_long(self % unit_fh, buffer, collect_)
call mpi_write_long(self % mpi_fh, buffer, collect_)
end if
#else
write(self % unit_fh) buffer
@ -1622,7 +1628,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer
else
call mpi_read_long(self % unit_fh, buffer, collect_)
call mpi_read_long(self % mpi_fh, buffer, collect_)
end if
#else
read(self % unit_fh) buffer
@ -1688,7 +1694,7 @@ contains
if (self % serial) then
write(self % unit_fh) buffer
else
call mpi_write_string(self % unit_fh, buffer, n, collect_)
call mpi_write_string(self % mpi_fh, buffer, n, collect_)
end if
#else
write(self % unit_fh) buffer
@ -1754,7 +1760,7 @@ contains
if (self % serial) then
read(self % unit_fh) buffer
else
call mpi_read_string(self % unit_fh, buffer, n, collect_)
call mpi_read_string(self % mpi_fh, buffer, n, collect_)
end if
#else
read(self % unit_fh) buffer
@ -1914,7 +1920,7 @@ contains
# elif MPI
! Write out tally buffer
call MPI_FILE_READ(self % unit_fh, buffer, n1*n2, MPI_TALLYRESULT, &
call MPI_FILE_READ(self % mpi_fh, buffer, n1*n2, MPI_TALLYRESULT, &
MPI_STATUS_IGNORE, mpiio_err)
#else
@ -1943,7 +1949,11 @@ contains
# ifndef HDF5
integer(MPI_OFFSET_KIND) :: offset ! offset of data
integer :: size_bank ! size of bank to write
#ifdef MPIF08
type(MPI_Datatype) :: datatype
#else
integer :: datatype
#endif
# endif
# ifdef HDF5
integer(8) :: offset(1) ! source data offset
@ -2023,7 +2033,7 @@ contains
#elif MPI
! Get current offset for master
if (master) call MPI_FILE_GET_POSITION(self % unit_fh, offset, mpiio_err)
if (master) call MPI_FILE_GET_POSITION(self % mpi_fh, offset, mpiio_err)
! Determine offset on master process and broadcast to all processors
call MPI_TYPE_MATCH_SIZE(MPI_TYPECLASS_INTEGER, MPI_OFFSET_KIND, &
@ -2035,7 +2045,7 @@ contains
offset = offset + size_bank*work_index(rank)
! Write all source sites
call MPI_FILE_WRITE_AT(self % unit_fh, offset, source_bank(1), int(work), &
call MPI_FILE_WRITE_AT(self % mpi_fh, offset, source_bank(1), int(work), &
MPI_BANK, MPI_STATUS_IGNORE, mpiio_err)
#else
@ -2124,11 +2134,11 @@ contains
! Go to the end of the file to set file pointer
offset = 0
call MPI_FILE_SEEK(self % unit_fh, offset, MPI_SEEK_END, &
call MPI_FILE_SEEK(self % mpi_fh, offset, MPI_SEEK_END, &
mpiio_err)
! Get current offset (will be at EOF)
call MPI_FILE_GET_POSITION(self % unit_fh, offset, mpiio_err)
call MPI_FILE_GET_POSITION(self % mpi_fh, offset, mpiio_err)
! Get the size of the source bank on all procs
call MPI_TYPE_SIZE(MPI_BANK, size_bank, mpi_err)
@ -2140,7 +2150,7 @@ contains
offset = offset + size_bank*work_index(rank)
! Write all source sites
call MPI_FILE_READ_AT(self % unit_fh, offset, source_bank(1), int(work), &
call MPI_FILE_READ_AT(self % mpi_fh, offset, source_bank(1), int(work), &
MPI_BANK, MPI_STATUS_IGNORE, mpiio_err)
#else

View file

@ -26,9 +26,8 @@ module particle_header
! Is this level rotated?
logical :: rotated = .false.
! Pointer to next (more local) set of coordinates
type(LocalCoord), pointer :: next => null()
contains
procedure :: reset => reset_coord
end type LocalCoord
!===============================================================================
@ -42,8 +41,8 @@ module particle_header
integer :: type ! Particle type (n, p, e, etc)
! Particle coordinates
type(LocalCoord), pointer :: coord0 => null() ! coordinates on universe 0
type(LocalCoord), pointer :: coord => null() ! coordinates on lowest universe
integer :: n_coord ! number of current coordinates
type(LocalCoord) :: coord(MAX_COORD) ! coordinates for all levels
! Other physical data
real(8) :: wgt ! particle weight
@ -87,26 +86,6 @@ module particle_header
contains
!===============================================================================
! DEALLOCATE_COORD removes all levels of coordinates below a given level. This
! is used in distance_to_boundary when the particle moves from a lower universe
! to a higher universe since the data for the lower one is not needed anymore.
!===============================================================================
recursive subroutine deallocate_coord(coord)
type(LocalCoord), pointer :: coord
if (associated(coord)) then
! recursively deallocate lower coordinates
if (associated(coord % next)) call deallocate_coord(coord%next)
! deallocate this coord
deallocate(coord)
end if
end subroutine deallocate_coord
!===============================================================================
! INITIALIZE_PARTICLE sets default attributes for a particle from the source
! bank
@ -137,9 +116,8 @@ contains
this % fission = .false.
! Set up base level coordinates
allocate(this % coord0)
this % coord0 % universe = BASE_UNIVERSE
this % coord => this % coord0
this % coord(1) % universe = BASE_UNIVERSE
this % n_coord = 1
end subroutine initialize_particle
@ -150,13 +128,30 @@ contains
subroutine clear_particle(this)
class(Particle) :: this
integer :: i
! remove any coordinate levels
call deallocate_coord(this % coord0)
! Make sure coord pointer is nullified
nullify(this % coord)
do i = 1, MAX_COORD
call this % coord(i) % reset()
end do
end subroutine clear_particle
!===============================================================================
! RESET_COORD
!===============================================================================
elemental subroutine reset_coord(this)
class(LocalCoord), intent(inout) :: this
this % cell = NONE
this % universe = NONE
this % lattice = NONE
this % lattice_x = NONE
this % lattice_y = NONE
this % lattice_z = NONE
this % rotated = .false.
end subroutine reset_coord
end module particle_header

View file

@ -89,13 +89,13 @@ contains
call pr % read_data(p % id, 'id')
call pr % read_data(p % wgt, 'weight')
call pr % read_data(p % E, 'energy')
call pr % read_data(p % coord % xyz, 'xyz', length=3)
call pr % read_data(p % coord % uvw, 'uvw', length=3)
call pr % read_data(p % coord(1) % xyz, 'xyz', length=3)
call pr % read_data(p % coord(1) % uvw, 'uvw', length=3)
! Set particle last attributes
p % last_wgt = p % wgt
p % last_xyz = p % coord % xyz
p % last_uvw = p % coord % uvw
p % last_xyz = p % coord(1) % xyz
p % last_uvw = p % coord(1) % uvw
p % last_E = p % E
! Close hdf5 file

View file

@ -38,7 +38,7 @@ contains
filename = trim(filename) // '.binary'
#endif
!$omp critical
!$omp critical (WriteParticleRestart)
! Create file
call pr % file_create(filename)
@ -66,7 +66,7 @@ contains
! Close file
call pr % file_close()
!$omp end critical
!$omp end critical (WriteParticleRestart)
end subroutine write_particle_restart

View file

@ -37,7 +37,7 @@ contains
! Store pre-collision particle properties
p % last_wgt = p % wgt
p % last_E = p % E
p % last_uvw = p % coord0 % uvw
p % last_uvw = p % coord(1) % uvw
! Add to collision counter for particle
p % n_collision = p % n_collision + 1
@ -143,7 +143,7 @@ contains
case ('total')
cutoff = prn() * material_xs % total
case ('scatter')
cutoff = prn() * material_xs % total - material_xs % absorption
cutoff = prn() * (material_xs % total - material_xs % absorption)
case ('fission')
cutoff = prn() * material_xs % fission
end select
@ -254,19 +254,19 @@ contains
p % last_wgt = p % wgt
! Score implicit absorption estimate of keff
!$omp atomic
global_tallies(K_ABSORPTION) % value = &
global_tallies(K_ABSORPTION) % value + p % absorb_wgt * &
micro_xs(i_nuclide) % nu_fission / micro_xs(i_nuclide) % absorption
if (run_mode == MODE_EIGENVALUE) then
global_tally_absorption = global_tally_absorption + p % absorb_wgt * &
micro_xs(i_nuclide) % nu_fission / micro_xs(i_nuclide) % absorption
end if
else
! See if disappearance reaction happens
if (micro_xs(i_nuclide) % absorption > &
prn() * micro_xs(i_nuclide) % total) then
! Score absorption estimate of keff
!$omp atomic
global_tallies(K_ABSORPTION) % value = &
global_tallies(K_ABSORPTION) % value + p % wgt * &
micro_xs(i_nuclide) % nu_fission / micro_xs(i_nuclide) % absorption
if (run_mode == MODE_EIGENVALUE) then
global_tally_absorption = global_tally_absorption + p % wgt * &
micro_xs(i_nuclide) % nu_fission / micro_xs(i_nuclide) % absorption
end if
p % alive = .false.
p % event = EVENT_ABSORB
@ -335,7 +335,7 @@ contains
! S(a,b) scattering
call sab_scatter(i_nuclide, micro_xs(i_nuclide) % index_sab, &
p % E, p % coord0 % uvw, p % mu, &
p % E, p % coord(1) % uvw, p % mu, &
materials(p % material) % p0(i_nuc_mat))
else
@ -344,7 +344,7 @@ contains
! Perform collision physics for elastic scattering
call elastic_scatter(i_nuclide, rxn, &
p % E, p % coord0 % uvw, p % mu, p % wgt, &
p % E, p % coord(1) % uvw, p % mu, p % wgt, &
materials(p % material) % p0(i_nuc_mat))
end if
@ -387,7 +387,7 @@ contains
end do
! Perform collision physics for inelastic scattering
call inelastic_scatter(nuc, rxn, p % E, p % coord0 % uvw, &
call inelastic_scatter(nuc, rxn, p % E, p % coord(1) % uvw, &
p % mu, p % wgt, materials(p % material) % p0(i_nuc_mat))
p % event_MT = rxn % MT
@ -620,13 +620,13 @@ contains
if (r > ONE) then
! equally likely N-4 middle bins
j = int(r) + 2
elseif (r > 0.6) then
elseif (r > 0.6_8) then
! second to last bin has relative probability of 0.4
j = n_energy_out - 1
elseif (r > 0.5) then
elseif (r > HALF) then
! last bin has relative probability of 0.1
j = n_energy_out
elseif (r > 0.1) then
elseif (r > 0.1_8) then
! second bin has relative probability of 0.4
j = 2
else
@ -852,8 +852,8 @@ contains
case ('dbrc')
E_red = sqrt((awr * E) / kT)
E_low = (((E_red - 4.0_8)**2) * kT) / awr
E_up = (((E_red + 4.0_8)**2) * kT) / awr
E_low = (((E_red - FOUR)**2) * kT) / awr
E_up = (((E_red + FOUR)**2) * kT) / awr
! find lower and upper energy bound indices
! lower index
@ -906,8 +906,8 @@ contains
case ('ares')
E_red = sqrt((awr * E) / kT)
E_low = (((E_red - 4.0_8)**2) * kT) / awr
E_up = (((E_red + 4.0_8)**2) * kT) / awr
E_low = (((E_red - FOUR)**2) * kT) / awr
E_up = (((E_red + FOUR)**2) * kT) / awr
! find lower and upper energy bound indices
! lower index
@ -1108,7 +1108,7 @@ contains
if (ufs) then
! Determine indices on ufs mesh for current location
call get_mesh_indices(ufs_mesh, p % coord0 % xyz, ijk, in_mesh)
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!")
@ -1146,7 +1146,7 @@ contains
p % fission = .true. ! Fission neutrons will be banked
do i = int(n_bank,4) + 1, int(min(n_bank + nu, int(size(fission_bank),8)),4)
! Bank source neutrons by copying particle data
fission_bank(i) % xyz = p % coord0 % xyz
fission_bank(i) % xyz = p % coord(1) % xyz
! Set weight of fission bank site
fission_bank(i) % wgt = ONE/weight

View file

@ -7,7 +7,7 @@ module plot
use global
use mesh, only: get_mesh_indices
use output, only: write_message
use particle_header, only: deallocate_coord, Particle, LocalCoord
use particle_header, only: Particle, LocalCoord
use plot_header
use ppmlib, only: Image, init_image, allocate_image, &
deallocate_image, set_pixel
@ -46,7 +46,7 @@ contains
end subroutine run_plot
!===============================================================================
! POSITION_RGB computes the red/green/blue values for a given plot with the
! POSITION_RGB computes the red/green/blue values for a given plot with the
! current particle's position
!===============================================================================
@ -56,28 +56,20 @@ contains
type(ObjectPlot), pointer, intent(in) :: pl
integer, intent(out) :: rgb(3)
integer, intent(out) :: id
integer :: j
logical :: found_cell
integer :: level
type(Cell), pointer :: c => null()
type(LocalCoord), pointer :: coord => null()
call deallocate_coord(p % coord0 % next)
p % coord => p % coord0
type(Cell), pointer :: c
p % n_coord = 1
call find_cell(p, found_cell)
j = p % n_coord
if (check_overlaps) call check_cell_overlap(p)
! Loop through universes and stop on any specified level
level = 0
coord => p % coord0
do
if (level == pl % level) exit
if (.not. associated(coord % next)) exit
coord => coord % next
level = level + 1
end do
! Set coordinate level if specified
if (pl % level >= 0) j = pl % level + 1
if (.not. found_cell) then
! If no cell, revert to default color
rgb = pl % not_found % rgb
@ -85,7 +77,7 @@ contains
else
if (pl % color_by == PLOT_COLOR_MATS) then
! Assign color based on material
c => cells(coord % cell)
c => cells(p % coord(j) % cell)
if (c % material == MATERIAL_VOID) then
! By default, color void cells white
rgb = 255
@ -100,14 +92,14 @@ contains
end if
else if (pl % color_by == PLOT_COLOR_CELLS) then
! Assign color based on cell
rgb = pl % colors(coord % cell) % rgb
id = cells(coord % cell) % id
rgb = pl % colors(p % coord(j) % cell) % rgb
id = cells(p % coord(j) % cell) % id
else
rgb = 0
id = -1
end if
end if
end subroutine position_rgb
!===============================================================================
@ -141,31 +133,31 @@ contains
if (pl % basis == PLOT_BASIS_XY) then
in_i = 1
out_i = 2
xyz(1) = pl % origin(1) - pl % width(1) / 2.0
xyz(2) = pl % origin(2) + pl % width(2) / 2.0
xyz(1) = pl % origin(1) - pl % width(1) / TWO
xyz(2) = pl % origin(2) + pl % width(2) / TWO
xyz(3) = pl % origin(3)
else if (pl % basis == PLOT_BASIS_XZ) then
in_i = 1
out_i = 3
xyz(1) = pl % origin(1) - pl % width(1) / 2.0
xyz(1) = pl % origin(1) - pl % width(1) / TWO
xyz(2) = pl % origin(2)
xyz(3) = pl % origin(3) + pl % width(2) / 2.0
xyz(3) = pl % origin(3) + pl % width(2) / TWO
else if (pl % basis == PLOT_BASIS_YZ) then
in_i = 2
out_i = 3
xyz(1) = pl % origin(1)
xyz(2) = pl % origin(2) - pl % width(1) / 2.0
xyz(3) = pl % origin(3) + pl % width(2) / 2.0
xyz(2) = pl % origin(2) - pl % width(1) / TWO
xyz(3) = pl % origin(3) + pl % width(2) / TWO
end if
! allocate and initialize particle
call p % initialize()
p % coord % xyz = xyz
p % coord % uvw = (/ 0.5, 0.5, 0.5 /)
p % coord % universe = BASE_UNIVERSE
p % coord(1) % xyz = xyz
p % coord(1) % uvw = [ HALF, HALF, HALF ]
p % coord(1) % universe = BASE_UNIVERSE
do y = 1, img % height
call progress % set_value(dble(y)/dble(img % height)*100.)
call progress % set_value(dble(y)/dble(img % height)*100)
do x = 1, img % width
! get pixel color
@ -175,12 +167,12 @@ contains
call set_pixel(img, x-1, y-1, rgb(1), rgb(2), rgb(3))
! Advance pixel in first direction
p % coord0 % xyz(in_i) = p % coord0 % xyz(in_i) + in_pixel
p % coord(1) % xyz(in_i) = p % coord(1) % xyz(in_i) + in_pixel
end do
! Advance pixel in second direction
p % coord0 % xyz(in_i) = xyz(in_i)
p % coord0 % xyz(out_i) = p % coord0 % xyz(out_i) - out_pixel
p % coord(1) % xyz(in_i) = xyz(in_i)
p % coord(1) % xyz(out_i) = p % coord(1) % xyz(out_i) - out_pixel
end do
! Draw tally mesh boundaries on the image if requested
@ -201,10 +193,10 @@ contains
! DRAW_MESH_LINES draws mesh line boundaries on an image
!===============================================================================
subroutine draw_mesh_lines(pl, img)
type(ObjectPlot), pointer, intent(in) :: pl
type(Image), intent(inout) :: img
logical :: in_mesh
integer :: out_, in_ ! pixel location
integer :: r, g, b ! RGB color for meshlines pixels
@ -221,13 +213,13 @@ contains
real(8) :: xyz_ll(3) ! lower left xyz
real(8) :: xyz_ur(3) ! upper right xyz
type(StructuredMesh), pointer :: m => null()
m => pl % meshlines_mesh
r = pl % meshlines_color % rgb(1)
g = pl % meshlines_color % rgb(2)
b = pl % meshlines_color % rgb(3)
select case (pl % basis)
case(PLOT_BASIS_XY)
outer = 1
@ -243,10 +235,10 @@ contains
xyz_ll_plot = pl % origin
xyz_ur_plot = pl % origin
xyz_ll_plot(outer) = pl % origin(1) - pl % width(1) / 2.0
xyz_ll_plot(inner) = pl % origin(2) - pl % width(2) / 2.0
xyz_ur_plot(outer) = pl % origin(1) + pl % width(1) / 2.0
xyz_ur_plot(inner) = pl % origin(2) + pl % width(2) / 2.0
xyz_ll_plot(outer) = pl % origin(1) - pl % width(1) / TWO
xyz_ll_plot(inner) = pl % origin(2) - pl % width(2) / TWO
xyz_ur_plot(outer) = pl % origin(1) + pl % width(1) / TWO
xyz_ur_plot(inner) = pl % origin(2) + pl % width(2) / TWO
width = xyz_ur_plot - xyz_ll_plot
@ -259,24 +251,24 @@ contains
! check if we're in the mesh for this ijk
if (i > 0 .and. i <= m % dimension(outer) .and. &
j > 0 .and. j <= m % dimension(inner)) then
! get xyz's of lower left and upper right of this mesh cell
xyz_ll(outer) = m % lower_left(outer) + m % width(outer) * (i - 1)
xyz_ll(inner) = m % lower_left(inner) + m % width(inner) * (j - 1)
xyz_ur(outer) = m % lower_left(outer) + m % width(outer) * i
xyz_ur(inner) = m % lower_left(inner) + m % width(inner) * j
! map the xyz ranges to pixel ranges
frac = (xyz_ll(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(1) = int(frac * real(img % width, 8))
frac = (xyz_ur(outer) - xyz_ll_plot(outer)) / width(outer)
outrange(2) = int(frac * real(img % width, 8))
frac = (xyz_ur(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(1) = int((1. - frac) * real(img % height, 8))
inrange(1) = int((ONE - frac) * real(img % height, 8))
frac = (xyz_ll(inner) - xyz_ll_plot(inner)) / width(inner)
inrange(2) = int((1. - frac) * real(img % height, 8))
inrange(2) = int((ONE - frac) * real(img % height, 8))
! draw lines
do out_ = outrange(1), outrange(2)
@ -295,11 +287,11 @@ contains
call set_pixel(img, outrange(2) - plus, in_, r, g, b)
end do
end do
end if
end do
end do
end subroutine draw_mesh_lines
!===============================================================================
@ -350,7 +342,7 @@ contains
subroutine create_3d_dump(pl)
type(ObjectPlot), pointer :: pl
integer :: x, y, z ! voxel location indices
integer :: rgb(3) ! colors (red, green, blue) from 0-255
integer :: id ! id of cell or material
@ -361,15 +353,15 @@ contains
! compute voxel widths in each direction
vox = pl % width/dble(pl % pixels)
! initial particle position
ll = pl % origin - pl % width / 2.0
ll = pl % origin - pl % width / TWO
! allocate and initialize particle
call p % initialize()
p % coord0 % xyz = ll
p % coord0 % uvw = (/ 0.5, 0.5, 0.5 /)
p % coord0 % universe = BASE_UNIVERSE
p % coord(1) % xyz = ll
p % coord(1) % uvw = [ HALF, HALF, HALF ]
p % coord(1) % universe = BASE_UNIVERSE
! Open binary plot file for writing
open(UNIT=UNIT_PLOT, FILE=pl % path_plot, STATUS='replace', &
@ -378,11 +370,11 @@ contains
! write plot header info
write(UNIT_PLOT) pl % pixels, vox, ll
! move to center of voxels
ll = ll + vox / 2.0
! move to center of voxels
ll = ll + vox / TWO
do x = 1, pl % pixels(1)
call progress % set_value(dble(x)/dble(pl % pixels(1))*100.)
call progress % set_value(dble(x)/dble(pl % pixels(1))*100)
do y = 1, pl % pixels(2)
do z = 1, pl % pixels(3)
@ -393,21 +385,21 @@ contains
write(UNIT_PLOT) id
! advance particle in z direction
p % coord0 % xyz(3) = p % coord0 % xyz(3) + vox(3)
p % coord(1) % xyz(3) = p % coord(1) % xyz(3) + vox(3)
end do
! advance particle in y direction
p % coord0 % xyz(2) = p % coord0 % xyz(2) + vox(2)
p % coord0 % xyz(3) = ll(3)
p % coord(1) % xyz(2) = p % coord(1) % xyz(2) + vox(2)
p % coord(1) % xyz(3) = ll(3)
end do
! advance particle in y direction
p % coord0 % xyz(1) = p % coord0 % xyz(1) + vox(1)
p % coord0 % xyz(2) = ll(2)
p % coord0 % xyz(3) = ll(3)
p % coord(1) % xyz(1) = p % coord(1) % xyz(1) + vox(1)
p % coord(1) % xyz(2) = ll(2)
p % coord(1) % xyz(3) = ll(3)
end do
close(UNIT_PLOT)

View file

@ -14,7 +14,7 @@ module source
use string, only: to_str
#ifdef MPI
use mpi
use message_passing
#endif
implicit none
@ -132,8 +132,8 @@ contains
site % xyz = p_min + r*(p_max - p_min)
! Fill p with needed data
p % coord0 % xyz = site % xyz
p % coord0 % uvw = [ ONE, ZERO, ZERO ]
p % coord(1) % xyz = site % xyz
p % coord(1) % uvw = [ ONE, ZERO, ZERO ]
! Now search to see if location exists in geometry
call find_cell(p, found)
@ -161,8 +161,8 @@ contains
site % xyz = p_min + r*(p_max - p_min)
! Fill p with needed data
p % coord0 % xyz = site % xyz
p % coord0 % uvw = [ ONE, ZERO, ZERO ]
p % coord(1) % xyz = site % xyz
p % coord(1) % uvw = [ ONE, ZERO, ZERO ]
! Now search to see if location exists in geometry
call find_cell(p, found)
@ -306,8 +306,8 @@ contains
! copy attributes from source bank site
p % wgt = src % wgt
p % last_wgt = src % wgt
p % coord % xyz = src % xyz
p % coord % uvw = src % uvw
p % coord(1) % xyz = src % xyz
p % coord(1) % uvw = src % uvw
p % last_xyz = src % xyz
p % last_uvw = src % uvw
p % E = src % E

View file

@ -23,7 +23,7 @@ module state_point
use dict_header, only: ElemKeyValueII, ElemKeyValueCI
#ifdef MPI
use mpi
use message_passing
#endif
implicit none

View file

@ -11,13 +11,13 @@ module tally
mesh_intersects_2d, mesh_intersects_3d
use mesh_header, only: StructuredMesh
use output, only: header
use particle_header, only: LocalCoord, Particle, deallocate_coord
use particle_header, only: LocalCoord, Particle
use search, only: binary_search
use string, only: to_str
use tally_header, only: TallyResult, TallyMapItem, TallyMapElement
#ifdef MPI
use mpi
use message_passing
#endif
implicit none
@ -140,7 +140,7 @@ contains
end if
case (SCORE_SCATTER_PN, SCORE_SCATTER_YN)
case (SCORE_SCATTER_PN)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
if (p % event /= EVENT_SCATTER) then
@ -153,6 +153,19 @@ contains
score = p % last_wgt
case (SCORE_SCATTER_YN)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
if (p % event /= EVENT_SCATTER) then
i = i + (t % moment_order(i) + 1)**2 - 1
cycle SCORE_LOOP
end if
! Since only scattering events make it here, again we can use
! the weight entering the collision as the estimator for the
! reaction rate
score = p % last_wgt
case (SCORE_NU_SCATTER, SCORE_NU_SCATTER_N)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
@ -163,7 +176,7 @@ contains
score = p % wgt
case (SCORE_NU_SCATTER_PN, SCORE_NU_SCATTER_YN)
case (SCORE_NU_SCATTER_PN)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
if (p % event /= EVENT_SCATTER) then
@ -176,6 +189,19 @@ contains
score = p % wgt
case (SCORE_NU_SCATTER_YN)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
if (p % event /= EVENT_SCATTER) then
i = i + (t % moment_order(i) + 1)**2 - 1
cycle SCORE_LOOP
end if
! For scattering production, we need to use the post-collision
! weight as the estimate for the number of neutrons exiting a
! reaction with neutrons in the exit channel
score = p % wgt
case (SCORE_TRANSPORT)
! Only analog estimators are available.
! Skip any event where the particle didn't scatter
@ -444,13 +470,13 @@ contains
num_nm = 2 * n + 1
! multiply score by the angular flux moments and store
!$omp critical
!$omp critical (score_general_scatt_yn)
t % results(score_index: score_index + num_nm - 1, filter_index) &
% value = t &
% results(score_index: score_index + num_nm - 1, filter_index)&
% value &
+ score * calc_pn(n, p % mu) * calc_rn(n, p % last_uvw)
!$omp end critical
!$omp end critical (score_general_scatt_yn)
end do
i = i + (t % moment_order(i) + 1)**2 - 1
@ -461,7 +487,7 @@ contains
if (t % estimator == ESTIMATOR_ANALOG) then
uvw = p % last_uvw
else if (t % estimator == ESTIMATOR_TRACKLENGTH) then
uvw = p % coord0 % uvw
uvw = p % coord(1) % uvw
end if
! Find the order for a collection of requested moments
! and store the moment contribution of each
@ -472,13 +498,13 @@ contains
num_nm = 2 * n + 1
! multiply score by the angular flux moments and store
!$omp critical
!$omp critical (score_general_flux_tot_yn)
t % results(score_index: score_index + num_nm - 1, filter_index) &
% value = t &
% results(score_index: score_index + num_nm - 1, filter_index)&
% value &
+ score * calc_rn(n, uvw)
!$omp end critical
!$omp end critical (score_general_flux_tot_yn)
end do
i = i + (t % moment_order(i) + 1)**2 - 1
@ -883,7 +909,6 @@ contains
type(TallyObject), pointer :: t
type(StructuredMesh), pointer :: m
type(Material), pointer :: mat
type(LocalCoord), pointer :: coord
t => tallies(i_tally)
matching_bins(1:t%n_filters) = 1
@ -892,8 +917,8 @@ contains
! CHECK IF THIS TRACK INTERSECTS THE MESH
! Copy starting and ending location of particle
xyz0 = p % coord0 % xyz - (d_track - TINY_BIT) * p % coord0 % uvw
xyz1 = p % coord0 % xyz - TINY_BIT * p % coord0 % uvw
xyz0 = p % coord(1) % xyz - (d_track - TINY_BIT) * p % coord(1) % uvw
xyz1 = p % coord(1) % xyz - TINY_BIT * p % coord(1) % uvw
! Get index for mesh filter
i_filter_mesh = t % find_filter(FILTER_MESH)
@ -914,8 +939,8 @@ contains
end if
! Reset starting and ending location
xyz0 = p % coord0 % xyz - d_track * p % coord0 % uvw
xyz1 = p % coord0 % xyz
xyz0 = p % coord(1) % xyz - d_track * p % coord(1) % uvw
xyz1 = p % coord(1) % xyz
! =========================================================================
! CHECK FOR SCORING COMBINATION FOR FILTERS OTHER THAN MESH
@ -927,7 +952,7 @@ contains
! determine next universe bin
! TODO: Account for multiple universes when performing this filter
matching_bins(i) = get_next_bin(FILTER_UNIVERSE, &
p % coord % universe, i_tally)
p % coord(p % n_coord) % universe, i_tally)
case (FILTER_MATERIAL)
matching_bins(i) = get_next_bin(FILTER_MATERIAL, &
@ -935,15 +960,12 @@ contains
case (FILTER_CELL)
! determine next cell bin
coord => p % coord0
do while(associated(coord))
do j = 1, p % n_coord
position(FILTER_CELL) = 0
matching_bins(i) = get_next_bin(FILTER_CELL, &
coord % cell, i_tally)
p % coord(j) % cell, i_tally)
if (matching_bins(i) /= NO_BIN_FOUND) exit
coord => coord % next
end do
nullify(coord)
case (FILTER_CELLBORN)
! determine next cellborn bin
@ -983,7 +1005,7 @@ contains
n_cross = sum(abs(ijk1(:m % n_dimension) - ijk0(:m % n_dimension))) + 1
! Copy particle's direction
uvw = p % coord0 % uvw
uvw = p % coord(1) % uvw
! Bounding coordinates
do j = 1, m % n_dimension
@ -1109,12 +1131,12 @@ contains
logical, intent(out) :: found_bin
integer :: i ! loop index for filters
integer :: j
integer :: n ! number of bins for single filter
integer :: offset ! offset for distribcell
real(8) :: E ! particle energy
type(TallyObject), pointer :: t
type(StructuredMesh), pointer :: m
type(LocalCoord), pointer :: coord
found_bin = .true.
t => tallies(i_tally)
@ -1128,13 +1150,13 @@ contains
m => meshes(t % filters(i) % int_bins(1))
! Determine if we're in the mesh first
call get_mesh_bin(m, p % coord0 % xyz, matching_bins(i))
call get_mesh_bin(m, p % coord(1) % xyz, matching_bins(i))
case (FILTER_UNIVERSE)
! determine next universe bin
! TODO: Account for multiple universes when performing this filter
matching_bins(i) = get_next_bin(FILTER_UNIVERSE, &
p % coord % universe, i_tally)
p % coord(p % n_coord) % universe, i_tally)
case (FILTER_MATERIAL)
if (p % material /= MATERIAL_VOID) then
@ -1144,37 +1166,39 @@ contains
case (FILTER_CELL)
! determine next cell bin
coord => p % coord0
do while(associated(coord))
do j = 1, p % n_coord
position(FILTER_CELL) = 0
matching_bins(i) = get_next_bin(FILTER_CELL, &
coord % cell, i_tally)
p % coord(j) % cell, i_tally)
if (matching_bins(i) /= NO_BIN_FOUND) exit
coord => coord % next
end do
nullify(coord)
case (FILTER_DISTRIBCELL)
! determine next distribcell bin
matching_bins(i) = NO_BIN_FOUND
coord => p % coord0
offset = 0
do while(associated(coord))
if (cells(coord % cell) % type == CELL_FILL) then
offset = offset + cells(coord % cell) % &
do j = 1, p % n_coord
if (cells(p % coord(j) % cell) % type == CELL_FILL) then
offset = offset + cells(p % coord(j) % cell) % &
offset(t % filters(i) % offset)
elseif(cells(coord % cell) % type == CELL_LATTICE) then
offset = offset + lattices(coord % next % lattice) % obj % &
offset(t % filters(i) % offset, coord % next % lattice_x, &
coord % next % lattice_y, coord % next % lattice_z)
elseif(cells(p % coord(j) % cell) % type == CELL_LATTICE) then
if (lattices(p % coord(j + 1) % lattice) % obj &
% are_valid_indices([&
p % coord(j + 1) % lattice_x, &
p % coord(j + 1) % lattice_y, &
p % coord(j + 1) % lattice_z])) then
offset = offset + lattices(p % coord(j + 1) % lattice) % obj % &
offset(t % filters(i) % offset, &
p % coord(j + 1) % lattice_x, &
p % coord(j + 1) % lattice_y, &
p % coord(j + 1) % lattice_z)
end if
end if
if (t % filters(i) % int_bins(1) == coord % cell) then
if (t % filters(i) % int_bins(1) == p % coord(j) % cell) then
matching_bins(i) = offset + 1
exit
end if
coord => coord % next
end do
nullify(coord)
case (FILTER_CELLBORN)
! determine next cellborn bin
@ -1270,7 +1294,7 @@ contains
TALLY_LOOP: do i = 1, active_current_tallies % size()
! Copy starting and ending location of particle
xyz0 = p % last_xyz
xyz1 = p % coord0 % xyz
xyz1 = p % coord(1) % xyz
! Get pointer to tally
i_tally = active_current_tallies % get_item(i)
@ -1302,7 +1326,7 @@ contains
end if
! Copy particle's direction
uvw = p % coord0 % uvw
uvw = p % coord(1) % uvw
! determine incoming energy bin
j = t % find_filter(FILTER_ENERGYIN)

View file

@ -46,7 +46,7 @@ contains
end if
! Write current coordinates into the newest column.
coords(:, n_tracks) = p % coord0 % xyz
coords(:, n_tracks) = p % coord(1) % xyz
end subroutine write_particle_track
!===============================================================================
@ -69,12 +69,12 @@ contains
// '_' // trim(to_str(current_gen)) // '_' // trim(to_str(p % id)) &
// '.binary'
#endif
!$omp critical
!$omp critical (FinalizeParticleTrack)
call binout % file_create(fname)
length = [3, n_tracks]
call binout % write_data(coords, 'coordinates', length=length)
call binout % file_close()
!$omp end critical
!$omp end critical (FinalizeParticleTrack)
deallocate(coords)
end subroutine finalize_particle_track

View file

@ -1,5 +1,6 @@
module tracking
use constants, only: MODE_EIGENVALUE
use cross_section, only: calculate_xs
use error, only: fatal_error, warning
use geometry, only: find_cell, distance_to_boundary, cross_surface, &
@ -28,6 +29,8 @@ contains
type(Particle), intent(inout) :: p
integer :: j ! coordinate level
integer :: next_level ! next coordinate level to check
integer :: surface_crossed ! surface which particle is on
integer :: lattice_translation(3) ! in-lattice translation vector
integer :: last_cell ! most recent cell particle was in
@ -36,7 +39,6 @@ contains
real(8) :: d_collision ! sampled distance to collision
real(8) :: distance ! distance particle travels
logical :: found_cell ! found cell which particle is in?
type(LocalCoord), pointer :: coord
! Display message if high verbosity or trace is on
if (verbosity >= 9 .or. trace) then
@ -45,7 +47,7 @@ contains
! If the cell hasn't been determined based on the particle's location,
! initiate a search for the current cell
if (p % coord % cell == NONE) then
if (p % coord(p % n_coord) % cell == NONE) then
call find_cell(p, found_cell)
! Particle couldn't be located
@ -54,7 +56,7 @@ contains
end if
! set birth cell attribute
p % cell_born = p % coord % cell
p % cell_born = p % coord(p % n_coord) % cell
end if
! Initialize number of events to zero
@ -87,7 +89,7 @@ contains
! Find the distance to the nearest boundary
call distance_to_boundary(p, d_boundary, surface_crossed, &
&lattice_translation)
lattice_translation, next_level)
! Sample a distance to collision
if (material_xs % total == ZERO) then
@ -100,10 +102,8 @@ contains
distance = min(d_boundary, d_collision)
! Advance particle
coord => p % coord0
do while (associated(coord))
coord % xyz = coord % xyz + distance * coord % uvw
coord => coord % next
do j = 1, p % n_coord
p % coord(j) % xyz = p % coord(j) % xyz + distance * p % coord(j) % uvw
end do
! Score track-length tallies
@ -111,17 +111,18 @@ contains
call score_tracklength_tally(p, distance)
! Score track-length estimate of k-eff
!$omp atomic
global_tallies(K_TRACKLENGTH) % value = &
global_tallies(K_TRACKLENGTH) % value + p % wgt * distance * &
material_xs % nu_fission
if (run_mode == MODE_EIGENVALUE) then
global_tally_tracklength = global_tally_tracklength + p % wgt * &
distance * material_xs % nu_fission
end if
if (d_collision > d_boundary) then
! ====================================================================
! PARTICLE CROSSES SURFACE
last_cell = p % coord % cell
p % coord % cell = NONE
if (next_level > 0) p % n_coord = next_level
last_cell = p % coord(p % n_coord) % cell
p % coord(p % n_coord) % cell = NONE
if (any(lattice_translation /= 0)) then
! Particle crosses lattice boundary
p % surface = NONE
@ -138,10 +139,10 @@ contains
! PARTICLE HAS COLLISION
! Score collision estimate of keff
!$omp atomic
global_tallies(K_COLLISION) % value = &
global_tallies(K_COLLISION) % value + p % wgt * &
material_xs % nu_fission / material_xs % total
if (run_mode == MODE_EIGENVALUE) then
global_tally_collision = global_tally_collision + p % wgt * &
material_xs % nu_fission / material_xs % total
end if
! score surface current tallies -- this has to be done before the collision
! since the direction of the particle will change and we need to use the
@ -168,7 +169,7 @@ contains
p % fission = .false.
! Save coordinates for tallying purposes
p % last_xyz = p % coord0 % xyz
p % last_xyz = p % coord(1) % xyz
! Set last material to none since cross sections will need to be
! re-evaluated
@ -176,19 +177,15 @@ contains
! Set all uvws to base level -- right now, after a collision, only the
! base level uvws are changed
coord => p % coord0
do while(associated(coord % next))
if (coord % next % rotated) then
do j = 1, p % n_coord - 1
if (p % coord(j + 1) % rotated) then
! If next level is rotated, apply rotation matrix
coord % next % uvw = matmul(cells(coord % cell) % &
rotation_matrix, coord % uvw)
p % coord(j + 1) % uvw = matmul(cells(p % coord(j) % cell) % &
rotation_matrix, p % coord(j) % uvw)
else
! Otherwise, copy this level's direction
coord % next % uvw = coord % uvw
p % coord(j + 1) % uvw = p % coord(j) % uvw
end if
! Advance coordinate level
coord => coord % next
end do
end if

View file

@ -1,9 +1,10 @@
module trigger
#ifdef MPI
use mpi
use message_passing
#endif
use constants
use global
use string, only: to_str
use output, only: warning, write_message
@ -30,7 +31,7 @@ contains
character(len=52) :: name ! "eigenvalue" or tally score
integer :: n_pred_batches ! predicted # batches to satisfy all triggers
! Checks if current_batch is one for which the triggers must be checked
if (current_batch < n_batches .or. (.not. trigger_on)) return
if (mod((current_batch - n_batches), n_batch_interval) /= 0 .and. &
@ -39,24 +40,24 @@ contains
! Check the trigger and output the result
call check_tally_triggers(max_ratio, tally_id, name)
! When trigger threshold is reached, write information
! When trigger threshold is reached, write information
if (satisfy_triggers) then
call write_message("Triggers satisfied for batch " // &
trim(to_str(current_batch)))
! When trigger is not reached write convergence info for user
elseif (name == "eigenvalue") then
call write_message("Triggers unsatisfied, max unc./thresh. is " // &
trim(to_str(max_ratio)) // " for " // trim(name))
else
call write_message("Triggers unsatisfied, max unc./thresh. is " // &
call write_message("Triggers unsatisfied, max unc./thresh. is " // &
trim(to_str(max_ratio)) // " for " // trim(name) // &
" in tally " // trim(to_str(tally_id)))
end if
end if
! If batch_interval is not set, estimate batches till triggers are satisfied
if (pred_batches .and. .not. satisfy_triggers) then
! Estimate the number of remaining batches to convergence
! The prediction uses the fact that tally variances are proportional
! to 1/N where N is the number of the batches/particles
@ -65,9 +66,9 @@ contains
n_pred_batches = n_batch_interval + n_batches
! Write the predicted number of batches for the user
if (n_pred_batches > n_max_batches) then
if (n_pred_batches > n_max_batches) then
call warning("The estimated number of batches is " // &
trim(to_str(n_pred_batches)) // &
trim(to_str(n_pred_batches)) // &
" -- greater than max batches. ")
else
call write_message("The estimated number of batches is " // &
@ -98,8 +99,8 @@ contains
integer :: n_order ! loop index for moment orders
integer :: nm_order ! loop index for Ynm moment orders
real(8) :: uncertainty ! trigger uncertainty
real(8) :: std_dev = 0.0 ! trigger standard deviation
real(8) :: rel_err = 0.0 ! trigger relative error
real(8) :: std_dev = ZERO ! trigger standard deviation
real(8) :: rel_err = ZERO ! trigger relative error
real(8) :: ratio ! ratio of the uncertainty/trigger threshold
type(TallyObject), pointer :: t ! tally pointer
type(TriggerObject), pointer :: trigger ! tally trigger
@ -115,8 +116,8 @@ contains
! Check eigenvalue trigger
if (run_mode == MODE_EIGENVALUE) then
if (keff_trigger % trigger_type /= 0) then
select case (keff_trigger % trigger_type)
case(VARIANCE)
select case (keff_trigger % trigger_type)
case(VARIANCE)
uncertainty = k_combined(2) ** 2
case(STANDARD_DEVIATION)
uncertainty = k_combined(2)
@ -124,7 +125,7 @@ contains
uncertainty = k_combined(2) / k_combined(1)
end select
! If uncertainty is above threshold, store uncertainty ratio
! If uncertainty is above threshold, store uncertainty ratio
if (uncertainty > keff_trigger % threshold) then
satisfy_triggers = .false.
if (keff_trigger % trigger_type == VARIANCE) then
@ -133,11 +134,11 @@ contains
ratio = uncertainty / keff_trigger % threshold
end if
if (max_ratio < ratio) then
max_ratio = ratio
max_ratio = ratio
name = "eigenvalue"
end if
end if
end if
end if
end if
end if
end if
! Compute uncertainties for all tallies, scores with triggers
@ -153,10 +154,10 @@ contains
trigger => t % triggers(s)
! Initialize trigger uncertainties to zero
trigger % std_dev = 0.
trigger % rel_err = 0.
trigger % variance = 0.
trigger % std_dev = ZERO
trigger % rel_err = ZERO
trigger % variance = ZERO
! Surface current tally triggers require special treatment
if (t % type == TALLY_SURFACE_CURRENT) then
call compute_tally_current(t, trigger)
@ -178,9 +179,9 @@ contains
j = j - 1
else
if (j == t % n_filters) exit find_bin
end if
end if
end do find_bin
if (t % n_filters > 0) then
filter_index = sum((max(matching_bins(1:t%n_filters),1) - 1) * &
t % stride) + 1
@ -193,13 +194,13 @@ contains
! Initialize score bin index
NUCLIDE_LOOP: do n = 1, t % n_nuclide_bins
select case(t % score_bins(trigger % score_index))
case (SCORE_SCATTER_PN, SCORE_NU_SCATTER_PN)
score_index = score_index - 1
do n_order = 0, t % moment_order(trigger % score_index)
score_index = score_index + 1
@ -207,17 +208,17 @@ contains
score_index, filter_index, t)
if (trigger % variance < variance) then
trigger % variance = std_dev ** 2
trigger % variance = std_dev ** 2
end if
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
end do
case (SCORE_SCATTER_YN, SCORE_NU_SCATTER_YN, SCORE_FLUX_YN, &
SCORE_TOTAL_YN)
@ -242,32 +243,32 @@ contains
end do
end do
case default
call get_trigger_uncertainty(std_dev, rel_err, &
score_index, filter_index, t)
if (trigger % variance < variance) then
trigger % variance = std_dev ** 2
trigger % variance = std_dev ** 2
end if
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
end select
select case (t % triggers(s) % type)
case(VARIANCE)
select case (t % triggers(s) % type)
case(VARIANCE)
uncertainty = trigger % variance
case(STANDARD_DEVIATION)
uncertainty = trigger % std_dev
case default
uncertainty = trigger % rel_err
end select
if (uncertainty > t % triggers(s) % threshold) then
satisfy_triggers = .false.
@ -275,8 +276,8 @@ contains
ratio = sqrt(uncertainty / t % triggers(s) % threshold)
else
ratio = uncertainty / t % triggers(s) % threshold
end if
end if
if (max_ratio < ratio) then
max_ratio = ratio
name = t % triggers(s) % score_name
@ -297,7 +298,7 @@ contains
! COMPUTE_TALLY_CURRENT computes the current for a surface current tally with
! precision trigger(s).
!===============================================================================
subroutine compute_tally_current(t, trigger)
integer :: i ! mesh index for x
@ -310,8 +311,8 @@ contains
integer :: n ! number of incoming energy bins
integer :: filter_index ! index in results array for filters
logical :: print_ebin ! should incoming energy bin be displayed?
real(8) :: rel_err = 0.0 ! temporary relative error of result
real(8) :: std_dev = 0.0 ! temporary standard deviration of result
real(8) :: rel_err = ZERO ! temporary relative error of result
real(8) :: std_dev = ZERO ! temporary standard deviration of result
type(TallyObject), pointer :: t ! surface current tally
type(TriggerObject) :: trigger ! surface current tally trigger
type(StructuredMesh), pointer :: m ! surface current mesh
@ -350,10 +351,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = std_dev**2
@ -362,10 +363,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -377,22 +378,22 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
matching_bins(i_filter_surf) = OUT_RIGHT
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -404,10 +405,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -417,10 +418,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -432,10 +433,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -444,10 +445,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -459,10 +460,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -471,10 +472,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -486,10 +487,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -498,10 +499,10 @@ contains
filter_index = &
sum((matching_bins(1:t % n_filters) - 1) * t % stride) + 1
call get_trigger_uncertainty(std_dev, rel_err, 1, filter_index, t)
if (trigger % std_dev < std_dev) then
if (trigger % std_dev < std_dev) then
trigger % std_dev = std_dev
end if
if (trigger % rel_err < rel_err) then
if (trigger % rel_err < rel_err) then
trigger % rel_err = rel_err
end if
trigger % variance = trigger % std_dev**2
@ -539,11 +540,11 @@ contains
std_dev = sqrt((tally_result % sum_sq / n - mean * mean) / (n - 1))
! Compute the relative error if the mean is non-zero
if (mean == 0.) then
rel_err = 0.
if (mean == ZERO) then
rel_err = ZERO
else
rel_err = std_dev / mean
end if
end if
end subroutine get_trigger_uncertainty

View file

@ -1,24 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.insert(0, '../..')
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.10.binary')
sp.read_results()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1])
# write results to file
with open('results_test.dat','w') as fh:
fh.write(outstr)

58
tests/test_basic/test_basic.py Normal file → Executable file
View file

@ -1,60 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import TestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_results()
finally:
teardown()
harness = TestHarness('statepoint.10.*')
harness.main()

View file

@ -1,92 +0,0 @@
#!/usr/bin/env python
import sys
import numpy as np
sys.path.insert(0, '../..')
import openmc
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.20.binary')
sp.read_results()
# extract tally results and convert to vector
tally1 = sp.get_tally(scores=['flux'], filters=[openmc.Filter('mesh', [1])], \
estimator='tracklength')
tally2 = sp.get_tally(scores=['flux'], filters=[openmc.Filter('mesh', [2])], \
estimator='analog')
tally3 = sp.get_tally(scores=['nu-fission'], \
filters=[openmc.Filter('mesh', [2])], estimator='analog')
tally4 = sp.get_tally(scores=['current'], filters=[openmc.Filter('mesh', [2]), \
openmc.Filter('surface', [1,2,3,4,5,6])], \
estimator='analog')
results1 = np.zeros((tally1.sum.size + tally1.sum.size, ))
results1[0::2] = tally1.sum.ravel()
results1[1::2] = tally1.sum_sq.ravel()
results2 = np.zeros((tally2.sum.size + tally2.sum.size, ))
results2[0::2] = tally2.sum.ravel()
results2[1::2] = tally2.sum_sq.ravel()
results3 = np.zeros((tally3.sum.size + tally3.sum.size, ))
results3[0::2] = tally3.sum.ravel()
results3[1::2] = tally3.sum_sq.ravel()
results4 = np.zeros((tally4.sum.size + tally4.sum.size, ))
results4[0::2] = tally4.sum.ravel()
results4[1::2] = tally4.sum_sq.ravel()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp.k_combined[0], sp.k_combined[1])
# write out tally results
outstr += 'tally 1:\n'
for item in results1:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 2:\n'
for item in results2:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 3:\n'
for item in results3:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 4:\n'
for item in results4:
outstr += "{0:12.6E}\n".format(item)
# write out cmfd answers
outstr += 'cmfd indices\n'
for item in sp._cmfd_indices:
outstr += "{0:12.6E}\n".format(item)
outstr += 'k cmfd\n'
for item in sp._k_cmfd:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd entropy\n'
for item in sp._cmfd_entropy:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd balance\n'
for item in sp._cmfd_balance:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd dominance ratio\n'
for item in sp._cmfd_dominance:
outstr += "{0:10.3E}\n".format(item)
outstr += 'cmfd openmc source comparison\n'
for item in sp._cmfd_srccmp:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd source\n'
cmfdsrc = np.reshape(sp._cmfd_src, np.product(sp._cmfd_indices), order='F')
for item in cmfdsrc:
outstr += "{0:12.6E}\n".format(item)
# write results to file
with open('results_test.dat', 'w') as fh:
fh.write(outstr)

View file

@ -14,7 +14,7 @@ tally 1:
3.894180E+01
1.517824E+02
3.528006E+01
1.246309E+02
1.246308E+02
2.863448E+01
8.222321E+01
2.125384E+01

View file

@ -1,68 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import CMFDTestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_output_exists():
assert os.path.exists(os.path.join(cwd, 'tallies.out')), 'Tally output file does not exist.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
output.append(os.path.join(cwd, 'tallies.out'))
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_output_exists()
test_results()
finally:
teardown()
harness = CMFDTestHarness('statepoint.20.*', True)
harness.main()

View file

@ -1,92 +0,0 @@
#!/usr/bin/env python
import sys
import numpy as np
sys.path.insert(0, '../..')
import openmc
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.20.binary')
sp.read_results()
# extract tally results and convert to vector
tally1 = sp.get_tally(scores=['flux'], filters=[openmc.Filter('mesh', [1])], \
estimator='tracklength')
tally2 = sp.get_tally(scores=['flux'], filters=[openmc.Filter('mesh', [2])], \
estimator='analog')
tally3 = sp.get_tally(scores=['nu-fission'], \
filters=[openmc.Filter('mesh', [2])], estimator='analog')
tally4 = sp.get_tally(scores=['current'], filters=[openmc.Filter('mesh', [2]), \
openmc.Filter('surface', [1,2,3,4,5,6])], \
estimator='analog')
results1 = np.zeros((tally1.sum.size + tally1.sum.size, ))
results1[0::2] = tally1.sum.ravel()
results1[1::2] = tally1.sum_sq.ravel()
results2 = np.zeros((tally2.sum.size + tally2.sum.size, ))
results2[0::2] = tally2.sum.ravel()
results2[1::2] = tally2.sum_sq.ravel()
results3 = np.zeros((tally3.sum.size + tally3.sum.size, ))
results3[0::2] = tally3.sum.ravel()
results3[1::2] = tally3.sum_sq.ravel()
results4 = np.zeros((tally4.sum.size + tally4.sum.size, ))
results4[0::2] = tally4.sum.ravel()
results4[1::2] = tally4.sum_sq.ravel()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp._k_combined[0], sp._k_combined[1])
# write out tally results
outstr += 'tally 1:\n'
for item in results1:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 2:\n'
for item in results2:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 3:\n'
for item in results3:
outstr += "{0:12.6E}\n".format(item)
outstr += 'tally 4:\n'
for item in results4:
outstr += "{0:12.6E}\n".format(item)
# write out cmfd answers
outstr += 'cmfd indices\n'
for item in sp._cmfd_indices:
outstr += "{0:12.6E}\n".format(item)
outstr += 'k cmfd\n'
for item in sp._k_cmfd:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd entropy\n'
for item in sp._cmfd_entropy:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd balance\n'
for item in sp._cmfd_balance:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd dominance ratio\n'
for item in sp._cmfd_dominance:
outstr += "{0:10.3E}\n".format(item)
outstr += 'cmfd openmc source comparison\n'
for item in sp._cmfd_srccmp:
outstr += "{0:12.6E}\n".format(item)
outstr += 'cmfd source\n'
cmfdsrc = np.reshape(sp._cmfd_src, np.product(sp._cmfd_indices), order='F')
for item in cmfdsrc:
outstr += "{0:12.6E}\n".format(item)
# write results to file
with open('results_test.dat', 'w') as fh:
fh.write(outstr)

View file

@ -1,5 +1,5 @@
k-combined:
1.170519E+00 8.422959E-03
1.170519E+00 8.422960E-03
tally 1:
1.078122E+01
1.170828E+01
@ -47,7 +47,7 @@ tally 2:
6.213554E+00
1.946061E+00
7.376629E+01
2.729167E+02
2.729166E+02
5.253400E+01
1.385018E+02
6.438590E+00

View file

@ -1,69 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import CMFDTestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_output_exists():
assert os.path.exists(os.path.join(cwd, 'tallies.out')), 'Tally output file does not exist.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.20.*'))
output.append(os.path.join(cwd, 'tallies.out'))
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_output_exists()
test_results()
finally:
teardown()
harness = CMFDTestHarness('statepoint.20.*', True)
harness.main()

View file

@ -1,37 +0,0 @@
#!/usr/bin/env python
import sys
import numpy as np
sys.path.insert(0, '../..')
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.10.binary')
sp.read_results()
# extract tally results and convert to vector
tally = sp._tallies[1]
results = np.zeros((tally._sum.size + tally._sum.size, ))
results[0::2] = tally._sum.ravel()
results[1::2] = tally._sum_sq.ravel()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp._k_combined[0], sp._k_combined[1])
# write out tally results
outstr += 'tallies:\n'
for item in results:
outstr += "{0:12.6E}\n".format(item)
# write results to file
with open('results_test.dat','w') as fh:
fh.write(outstr)

View file

@ -1,5 +1,5 @@
k-combined:
2.913599E-01 6.738749E-03
tallies:
tally 1:
6.420923E+01
5.190738E+02

View file

@ -1,65 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import TestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_created_output():
assert os.path.exists(os.path.join(cwd, 'tallies.out')), 'Tally output file does not exist.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
output.append(os.path.join(cwd, 'results_test.dat'))
output.append(os.path.join(cwd, 'tallies.out'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_created_output()
test_results()
finally:
teardown()
harness = TestHarness('statepoint.10.*', True)
harness.main()

View file

@ -1,25 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.insert(0, '../..')
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.10.binary')
sp.read_results()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp._k_combined[0], sp._k_combined[1])
# write results to file
with open('results_test.dat','w') as fh:
fh.write(outstr)

View file

@ -1,60 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import TestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_results()
finally:
teardown()
harness = TestHarness('statepoint.10.*')
harness.main()

View file

@ -1,25 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.insert(0, '../..')
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.10.binary')
sp.read_results()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp._k_combined[0], sp._k_combined[1])
# write results to file
with open('results_test.dat','w') as fh:
fh.write(outstr)

View file

@ -1,2 +1,2 @@
k-combined:
1.092203E+00 1.990175E-02
1.092203E+00 1.990176E-02

View file

@ -1,60 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import TestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob('statepoint.10.*')
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_results()
finally:
teardown()
harness = TestHarness('statepoint.10.*')
harness.main()

View file

@ -1,25 +0,0 @@
#!/usr/bin/env python
import sys
sys.path.insert(0, '../..')
from openmc.statepoint import StatePoint
# read in statepoint file
if len(sys.argv) > 1:
sp = StatePoint(sys.argv[1])
else:
sp = StatePoint('statepoint.10.binary')
sp.read_results()
# set up output string
outstr = ''
# write out k-combined
outstr += 'k-combined:\n'
outstr += "{0:12.6E} {1:12.6E}\n".format(sp._k_combined[0], sp._k_combined[1])
# write results to file
with open('results_test.dat','w') as fh:
fh.write(outstr)

View file

@ -1,2 +1,2 @@
k-combined:
8.085745E-01 9.674582E-03
8.085745E-01 9.674599E-03

View file

@ -1,60 +1,10 @@
#!/usr/bin/env python
import os
import sys
from subprocess import Popen, STDOUT, PIPE, call
import filecmp
import glob
from optparse import OptionParser
sys.path.insert(0, '..')
from testing_harness import TestHarness
parser = OptionParser()
parser.add_option('--mpi_exec', dest='mpi_exec', default='')
parser.add_option('--mpi_np', dest='mpi_np', default='3')
parser.add_option('--exe', dest='exe')
(opts, args) = parser.parse_args()
cwd = os.getcwd()
def test_run():
if opts.mpi_exec != '':
proc = Popen([opts.mpi_exec, '-np', opts.mpi_np, opts.exe, cwd],
stderr=STDOUT, stdout=PIPE)
else:
proc = Popen([opts.exe, cwd], stderr=STDOUT, stdout=PIPE)
print(proc.communicate()[0])
returncode = proc.returncode
assert returncode == 0, 'OpenMC did not exit successfully.'
def test_created_statepoint():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
assert len(statepoint) == 1, 'Either multiple or no statepoint files exist.'
assert statepoint[0].endswith('binary') or statepoint[0].endswith('h5'),\
'Statepoint file is not a binary or hdf5 file.'
def test_results():
statepoint = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
call([sys.executable, 'results.py', statepoint[0]])
compare = filecmp.cmp('results_test.dat', 'results_true.dat')
if not compare:
os.rename('results_test.dat', 'results_error.dat')
assert compare, 'Results do not agree.'
def teardown():
output = glob.glob(os.path.join(cwd, 'statepoint.10.*'))
output.append(os.path.join(cwd, 'results_test.dat'))
for f in output:
if os.path.exists(f):
os.remove(f)
if __name__ == '__main__':
# test for openmc executable
if opts.exe is None:
raise Exception('Must specify OpenMC executable from command line with --exe.')
# run tests
try:
test_run()
test_created_statepoint()
test_results()
finally:
teardown()
harness = TestHarness('statepoint.10.*')
harness.main()

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