Merge remote-tracking branch 'upstream/develop' into cpp_geometry

This commit is contained in:
Sterling Harper 2018-08-19 11:02:26 -04:00
commit 8b287dc72d
192 changed files with 51525 additions and 2744 deletions

View file

@ -1,16 +1,18 @@
sudo: required
dist: trusty
dist: xenial
language: python
python:
- "3.4"
- "3.5"
- "3.6"
- "3.7"
addons:
apt:
packages:
- gfortran
- mpich
- libmpich-dev
- gfortran
- mpich
- libmpich-dev
- libhdf5-serial-dev
- libhdf5-mpich-dev
cache:
directories:
- $HOME/nndc_hdf5
@ -31,10 +33,6 @@ env:
- OMP=y MPI=n PHDF5=n
- OMP=n MPI=y PHDF5=n
- OMP=n MPI=y PHDF5=y
before_install:
- sudo add-apt-repository ppa:nschloe/hdf5-backports -y
- sudo apt-get update -q
- sudo apt-get install libhdf5-serial-dev libhdf5-mpich-dev -y
install:
- ./tools/ci/travis-install.sh
before_script:

View file

@ -84,8 +84,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
# Make sure version is sufficient
execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
if(GCC_VERSION VERSION_LESS 4.8)
message(FATAL_ERROR "gfortran version must be 4.8 or higher")
if(GCC_VERSION VERSION_LESS 4.9)
message(FATAL_ERROR "gcc version must be 4.9 or higher")
endif()
# GCC compiler options
@ -210,7 +210,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
endif()
list(APPEND cxxflags -std=c++11 -O2)
list(APPEND cxxflags -std=c++14 -O2)
if(debug)
list(REMOVE_ITEM cxxflags -O2)
list(APPEND cxxflags -g -O0)
@ -236,6 +236,14 @@ message(STATUS "Linker flags: ${ldflags}")
add_library(pugixml vendor/pugixml/pugixml.cpp)
target_include_directories(pugixml PUBLIC vendor/pugixml/)
#===============================================================================
# xtensor header-only library
#===============================================================================
add_subdirectory(vendor/xtl)
add_subdirectory(vendor/xtensor)
target_link_libraries(xtensor INTERFACE xtl)
#===============================================================================
# RPATH information
#===============================================================================
@ -280,7 +288,6 @@ set_target_properties(faddeeva PROPERTIES
add_library(libopenmc SHARED
src/algorithm.F90
src/angle_distribution.F90
src/angleenergy_header.F90
src/bank_header.F90
src/api.F90
@ -298,7 +305,6 @@ add_library(libopenmc SHARED
src/eigenvalue.F90
src/endf.F90
src/endf_header.F90
src/energy_distribution.F90
src/error.F90
src/geometry.F90
src/geometry_header.F90
@ -326,7 +332,6 @@ add_library(libopenmc SHARED
src/physics_mg.F90
src/plot.F90
src/plot_header.F90
src/product_header.F90
src/progress_header.F90
src/pugixml/pugixml_f.F90
src/random_lcg.F90
@ -334,9 +339,6 @@ add_library(libopenmc SHARED
src/relaxng
src/sab_header.F90
src/secondary_correlated.F90
src/secondary_kalbach.F90
src/secondary_nbody.F90
src/secondary_uncorrelated.F90
src/set_header.F90
src/settings.F90
src/simulation_header.F90
@ -384,12 +386,19 @@ add_library(libopenmc SHARED
src/tallies/trigger.F90
src/tallies/trigger_header.F90
src/cell.cpp
src/distribution.cpp
src/distribution_angle.cpp
src/distribution_energy.cpp
src/distribution_multi.cpp
src/distribution_spatial.cpp
src/endf.cpp
src/initialize.cpp
src/finalize.cpp
src/geometry.cpp
src/geometry_aux.cpp
src/hdf5_interface.cpp
src/lattice.cpp
src/material.cpp
src/math_functions.cpp
src/message_passing.cpp
src/mgxs.cpp
@ -400,6 +409,12 @@ add_library(libopenmc SHARED
src/position.cpp
src/pugixml/pugixml_c.cpp
src/random_lcg.cpp
src/reaction.cpp
src/reaction_product.cpp
src/secondary_correlated.cpp
src/secondary_kalbach.cpp
src/secondary_nbody.cpp
src/secondary_uncorrelated.cpp
src/scattdata.cpp
src/settings.cpp
src/simulation.cpp
@ -452,7 +467,7 @@ endif()
# target_link_libraries treats any arguments starting with - but not -l as
# linker flags. Thus, we can pass both linker flags and libraries together.
target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml
faddeeva)
faddeeva xtensor)
#===============================================================================
# openmc executable

View file

@ -36,8 +36,8 @@ development team will be happy to discuss it.
## How to Submit Changes
All changes to OpenMC happen through pull requests. For a full overview of the
process, see the developer's guide section on [Development
Workflow](http://openmc.readthedocs.io/en/latest/devguide/workflow.html).
process, see the developer's guide section on [Contributing to
OpenMC](http://openmc.readthedocs.io/en/latest/devguide/contributing.html).
## Code Style

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,129 @@
.. _devguide_contributing:
======================
Contributing to OpenMC
======================
Thank you for considering contributing to OpenMC! We look forward to welcoming
new members to the community and will do our best to help you get up to speed.
The purpose of this section is to document how the project is managed: how
contributions (bug fixes, enhancements, new features) are made, how they are
evaluated, who is permitted to merge pull requests, and what happens in the
event of disagreements. Once you have read through this section, the
:ref:`devguide_workflow` section outlines the actual mechanics of making a
contribution (forking, submitting a pull request, etc.).
The goal of our governance model is to:
- Encourage new contributions.
- Encourage contributors to remain involved.
- Avoid unnecessary processes and bureaucracy whenever possible.
- Create a transparent decision making process which makes it clear how
contributors can be involved in decision making.
Overview
--------
OpenMC uses a liberal contribution model for project governance. Anyone involved
in development in a non-trivial capacity is given an opportunity to influence
the direction of the project. Project decisions are made through a
consensus-seeking process rather than by voting.
Terminology
-----------
- A *Contributor* is any individual creating or commenting on an issue or pull
request.
- A *Committer* is a subset of contributors who are authorized to review and
merge pull requests.
- The *TC* (Technical Committee) is a group of committers who have the authority
to make decisions on behalf of the project team in order to resolve disputes.
- The *Project Lead* is a single individual who has the authority to make a final
decision when the TC is unable to reach consensus.
Contribution Process
--------------------
Any change to the OpenMC repository must be made through a pull request (PR).
This applies to all changes to documentation, code, binary files, etc. Even long
term committers and TC members must use pull requests.
No pull request may be merged without being independently reviewed.
For non-trivial contributions, pull requests should not be merged for at least
36 hours to ensure that contributors in other timezones have time to review.
Consideration should be given to weekends and other holiday periods to ensure
active committers have reasonable time to become involved in the discussion and
review process if they wish. Any committer may request that the review period be
extended if they are unable to review the change within 36 hours.
During review, a committer may request that a specific contributor who is most
versed in a particular area review the PR before it can be merged.
A pull request can be merged by any committer, but only if no objections are
raised by any other committer. In the case of an objection being raised, all
involved committers should seek consensus through discussion and compromise.
In the case of an objection being raised in a pull request by another committer,
all involved committers should seek to arrive at a consensus by way of
addressing concerns being expressed through discussion, compromise on the
proposed change, or withdrawal of the proposed change.
If objections to a PR are made and committers cannot reach a consensus on how to
proceed, the decision is escalated to the TC. TC members should regularly
discuss pending contributions in order to find a resolution. It is expected that
only a small minority of issues be brought to the TC for resolution and that
discussion and compromise among committers be the default resolution mechanism.
Becoming a Committer
--------------------
All contributors who make a non-trivial contribution will be added as a
committer in a timely manner. Committers are expected to follow this policy.
TC Process
----------
Any issues brought to the TC will be addressed among the committee with a
consensus-seeking process. The group tries to find a resolution that has no
objections among TC members. If a consensus cannot be reached, the Project Lead
has the ultimate authority to make a final decision. It is expected that the
majority of decisions made by the TC are via a consensus seeking process and
that the Project Lead intercedes only as a last resort.
Resolution may involve returning the issue to committers with suggestions on how
to move forward towards a consensus.
Members can be added to the TC at any time. Any committer can nominate another
committer to the TC and the TC uses its standard consensus seeking process to
evaluate whether or not to add this new member. Members who do not participate
consistently at the level of a majority of the other members are expected to
resign.
In the event that the Project Lead resigns or otherwise steps down, the TC uses
a consensus seeking process to choose a new Project Lead.
Leadership Team
---------------
The TC consists of the following individuals:
- `Paul Romano <https://github.com/paulromano>`_
- `Sterling Harper <https://github.com/smharper>`_
- `Adam Nelson <https://github.com/nelsonag>`_
- `Benoit Forget <https://github.com/bforget>`_
The Project Lead is Paul Romano.
Next Steps
----------
If you are interested in working on a specific feature or helping to address
outstanding issues, consider joining the developer's `mailing list
<https://groups.google.com/forum/#!forum/openmc-dev>`_ and/or `Slack community
<https://openmc.slack.com/signup>`_. Note that some issues have specifically
been labeled as good for `first-time contributors
<https://github.com/openmc-dev/openmc/issues?q=is%3Aopen+is%3Aissue+label%3AFirst-Timers-Only>`_.
Once you're at the point of writing code, make sure your read through the
:ref:`devguide_workflow` section to understand the mechanics of making pull
requests and what is expected during code reviews.

View file

@ -4,16 +4,17 @@
Developer's Guide
=================
Welcome to the OpenMC Developer's Guide! This guide documents and explains the
structure of the OpenMC source code and how to do various development tasks such
as debugging.
Welcome to the OpenMC Developer's Guide! This guide documents how contributions
are made to OpenMC, what style rules exist for the code, how to run tests, and
other related topics.
.. toctree::
:numbered:
:maxdepth: 2
styleguide
contributing
workflow
styleguide
tests
user-input
docbuild

View file

@ -174,9 +174,7 @@ Follow the `C++ Core Guidelines`_ except when they conflict with another
guideline listed here. For convenience, many important guidelines from that
list are repeated here.
Conform to the C++11 standard. Note that this is a significant difference
between our style and the C++ Core Guidelines. Many suggestions in those
Guidelines require C++14.
Conform to the C++14 standard.
Always use C++-style comments (``//``) as opposed to C-style (``/**/``). (It
is more difficult to comment out a large section of code that uses C-style

View file

@ -22,13 +22,11 @@ ongoing development takes place prior to a release and is not guaranteed to be
stable. When the development team decides that a release should occur, the
*develop* branch is merged into *master*.
Trivial changes to the code may be committed directly to the *develop* branch by
a trusted developer. However, most new features should be developed on a branch
All new features, enhancements, and bug fixes should be developed on a branch
that branches off of *develop*. When the feature is completed, a `pull request`_
is initiated on GitHub that is then reviewed by a trusted developer. If the pull
request is satisfactory, it is then merged into *develop*. Note that a trusted
developer may not review their own pull request (i.e., an independent code
review is required).
is initiated on GitHub that is then reviewed by a committer. If the pull request
is satisfactory, it is then merged into *develop*. Note that a committer may not
review their own pull request (i.e., an independent code review is required).
Code Review Criteria
--------------------
@ -37,9 +35,9 @@ In order to be considered suitable for inclusion in the *develop* branch, the
following criteria must be satisfied for all proposed changes:
- Changes have a clear purpose and are useful.
- Compiles and passes the regression suite with all configurations (This is
- Compiles and passes all tests under multiple build configurations (This is
checked by Travis CI).
- If appropriate, test cases are added to regression suite.
- If appropriate, test cases are added to regression or unit test suites.
- No memory leaks (checked with valgrind_).
- Conforms to the OpenMC `style guide`_.
- No degradation of performance or greatly increased memory usage. This is not a
@ -76,13 +74,11 @@ features and bug fixes. The general steps for contributing are as follows:
4. Issue a pull request from GitHub and select the *develop* branch of
openmc-dev/openmc as the target.
.. image:: ../_images/pullrequest.png
At a minimum, you should describe what the changes you've made are and why
you are making them. If the changes are related to an oustanding issue, make
sure it is cross-referenced.
5. A trusted developer will review your pull request based on the criteria
5. A committer will review your pull request based on the criteria
above. Any issues with the pull request can be discussed directly on the pull
request page itself.
@ -133,6 +129,4 @@ can interfere with virtual environments.
.. _openmc-dev/openmc: https://github.com/openmc-dev/openmc
.. _paid plan: https://github.com/plans
.. _Bitbucket: https://bitbucket.org
.. _ctest: http://www.cmake.org/cmake/help/v2.8.12/ctest.html
.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html
.. _pip: https://pip.pypa.io/en/stable/

View file

@ -123,7 +123,8 @@ The current version of the summary file format is 6.0.
**/macroscopics/**
:Attributes: - **n_macroscopics** (*int*) -- Number of macroscopic data sets
:Attributes:
- **n_macroscopics** (*int*) -- Number of macroscopic data sets
in the problem.
:Datasets: - **names** (*char[][]*) -- Names of the macroscopic data sets.

View file

@ -122,6 +122,7 @@ Constructing Tallies
openmc.SpatialLegendreFilter
openmc.SphericalHarmonicsFilter
openmc.ZernikeFilter
openmc.ZernikeRadialFilter
openmc.ParticleFilter
openmc.Mesh
openmc.Trigger

View file

@ -106,9 +106,13 @@ class Cell(_FortranObjectWithID):
if fill_type.value == 1:
if n.value > 1:
return [Material(index=i) for i in indices[:n.value]]
#TODO: off-by-one
return [Material(index=i+1 if i >= 0 else i)
for i in indices[:n.value]]
else:
return Material(index=indices[0])
#TODO: off-by-one
index = indices[0] + 1 if indices[0] >= 0 else indices[0]
return Material(index=index)
else:
raise NotImplementedError

View file

@ -1,6 +1,6 @@
from contextlib import contextmanager
from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, c_char,
POINTER, Structure, c_void_p, create_string_buffer)
from ctypes import (CDLL, c_bool, c_int, c_int32, c_int64, c_double, c_char_p,
c_char, POINTER, Structure, c_void_p, create_string_buffer)
from warnings import warn
import numpy as np
@ -52,7 +52,7 @@ _dll.openmc_simulation_init.restype = c_int
_dll.openmc_simulation_init.errcheck = _error_handler
_dll.openmc_simulation_finalize.restype = c_int
_dll.openmc_simulation_finalize.errcheck = _error_handler
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)]
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p), POINTER(c_bool)]
_dll.openmc_statepoint_write.restype = c_int
_dll.openmc_statepoint_write.errcheck = _error_handler
@ -269,7 +269,7 @@ def source_bank():
return as_array(ptr, (n.value,)).view(bank_dtype)
def statepoint_write(filename=None):
def statepoint_write(filename=None, write_source=True):
"""Write a statepoint file.
Parameters
@ -277,11 +277,13 @@ def statepoint_write(filename=None):
filename : str or None
Path to the statepoint to write. If None is passed, a default name that
contains the current batch will be written.
write_source : bool
Whether or not to include the source bank in the statepoint.
"""
if filename is not None:
filename = c_char_p(filename.encode())
_dll.openmc_statepoint_write(filename)
_dll.openmc_statepoint_write(filename, c_bool(write_source))
@contextmanager

View file

@ -20,7 +20,7 @@ __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter',
'EnergyFunctionFilter', 'LegendreFilter', 'MaterialFilter', 'MeshFilter',
'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SphericalHarmonicsFilter',
'SpatialLegendreFilter', 'SurfaceFilter',
'UniverseFilter', 'ZernikeFilter', 'filters']
'UniverseFilter', 'ZernikeFilter', 'ZernikeRadialFilter', 'filters']
# Tally functions
_dll.openmc_cell_filter_get_bins.argtypes = [
@ -360,6 +360,10 @@ class ZernikeFilter(Filter):
_dll.openmc_zernike_filter_set_order(self._index, order)
class ZernikeRadialFilter(ZernikeFilter):
filter_type = 'zernikeradial'
_FILTER_TYPE_MAP = {
'azimuthal': AzimuthalFilter,
'cell': CellFilter,
@ -380,7 +384,8 @@ _FILTER_TYPE_MAP = {
'spatiallegendre': SpatialLegendreFilter,
'surface': SurfaceFilter,
'universe': UniverseFilter,
'zernike': ZernikeFilter
'zernike': ZernikeFilter,
'zernikeradial': ZernikeRadialFilter
}

View file

@ -21,6 +21,9 @@ _dll.calc_rn_c.argtypes = [c_int, ndpointer(c_double), ndpointer(c_double)]
_dll.calc_zn_c.restype = None
_dll.calc_zn_c.argtypes = [c_int, c_double, c_double, ndpointer(c_double)]
_dll.calc_zn_rad_c.restype = None
_dll.calc_zn_rad_c.argtypes = [c_int, c_double, ndpointer(c_double)]
_dll.rotate_angle_c.restype = None
_dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double,
POINTER(c_double)]
@ -155,6 +158,32 @@ def calc_zn(n, rho, phi):
return zn
def calc_zn_rad(n, rho):
""" Calculate the even orders in n-th order modified Zernike polynomial
moment with no azimuthal dependency (m=0) for a given radial location in
the unit disk. The normalization of the polynomials is such that the
integral of Z_pq*Z_pq over the unit disk is exactly pi.
Parameters
----------
n : int
Maximum order
rho : float
Radial location in the unit disk
Returns
-------
numpy.ndarray
Corresponding resulting list of coefficients
"""
num_bins = n // 2 + 1
zn_rad = np.zeros(num_bins, dtype=np.float64)
_dll.calc_zn_rad_c(n, rho, zn_rad)
return zn_rad
def rotate_angle(uvw0, mu, phi=None):
""" Rotates direction cosines through a polar angle whose cosine is
mu and through an azimuthal angle sampled uniformly.

View file

@ -22,7 +22,7 @@ _FILTER_TYPES = (
'universe', 'material', 'cell', 'cellborn', 'surface', 'mesh', 'energy',
'energyout', 'mu', 'polar', 'azimuthal', 'distribcell', 'delayedgroup',
'energyfunction', 'cellfrom', 'legendre', 'spatiallegendre',
'sphericalharmonics', 'zernike', 'particle'
'sphericalharmonics', 'zernike', 'zernikeradial', 'particle'
)
_CURRENT_NAMES = (

View file

@ -234,8 +234,8 @@ class SphericalHarmonicsFilter(ExpansionFilter):
r"""Score spherical harmonic expansion moments up to specified order.
This filter allows you to obtain real spherical harmonic moments of either
the particle's direction or the cosine of the scattering angle. Specifying a
filter with order :math:`\ell` tallies moments for all orders from 0 to
the particle's direction or the cosine of the scattering angle. Specifying
a filter with order :math:`\ell` tallies moments for all orders from 0 to
:math:`\ell`.
Parameters
@ -342,11 +342,11 @@ class ZernikeFilter(ExpansionFilter):
\frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}.
With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk
is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where :math:`\epsilon_m` is
2 if :math:`m` equals 0 and 1 otherwise.
is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where
:math:`\epsilon_m` is 2 if :math:`m` equals 0 and 1 otherwise.
Specifying a filter with order N tallies moments for all :math:`n` from 0 to
N and each value of :math:`m`. The ordering of the Zernike polynomial
Specifying a filter with order N tallies moments for all :math:`n` from 0
to N and each value of :math:`m`. The ordering of the Zernike polynomial
moments follows the ANSI Z80.28 standard, where the one-dimensional index
:math:`j` corresponds to the :math:`n` and :math:`m` by
@ -463,3 +463,63 @@ class ZernikeFilter(ExpansionFilter):
subelement.text = str(self.r)
return element
class ZernikeRadialFilter(ZernikeFilter):
r"""Score the :math:`m = 0` (radial variation only) Zernike moments up to
specified order.
The Zernike polynomials are defined the same as in :class:`ZernikeFilter`.
.. math::
Z_n^{0}(\rho, \theta) = R_n^{0}(\rho)
where the radial polynomials are
.. math::
R_n^{0}(\rho) = \sum\limits_{k=0}^{n/2} \frac{(-1)^k (n-k)!}{k! ((
\frac{n}{2} - k)!)^{2}} \rho^{n-2k}.
With this definition, the integral of :math:`(Z_n^0)^2` over the unit disk
is :math:`\frac{\pi}{n+1}`.
If there is only radial dependency, the polynomials are integrated over
the azimuthal angles. The only terms left are :math:`Z_n^{0}(\rho, \theta)
= R_n^{0}(\rho)`. Note that :math:`n` could only be even orders.
Therefore, for a radial Zernike polynomials up to order of :math:`n`,
there are :math:`\frac{n}{2} + 1` terms in total. The indexing is from the
lowest even order (0) to highest even order.
Parameters
----------
order : int
Maximum radial Zernike polynomial order
x : float
x-coordinate of center of circle for normalization
y : float
y-coordinate of center of circle for normalization
r : int or None
Radius of circle for normalization
Attributes
----------
order : int
Maximum radial Zernike polynomial order
x : float
x-coordinate of center of circle for normalization
y : float
y-coordinate of center of circle for normalization
r : int or None
Radius of circle for normalization
id : int
Unique identifier for the filter
num_bins : int
The number of filter bins
"""
@ExpansionFilter.order.setter
def order(self, order):
ExpansionFilter.order.__set__(self, order)
self.bins = ['Z{},0'.format(n) for n in range(0, order+1, 2)]

View file

@ -1,130 +0,0 @@
module angle_distribution
use algorithm, only: binary_search
use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR
use distribution_univariate, only: DistributionContainer, Tabular
use hdf5_interface, only: read_attribute, get_shape, read_dataset, &
open_dataset, close_dataset, HID_T, HSIZE_T
use random_lcg, only: prn
implicit none
private
!===============================================================================
! ANGLEDISTRIBUTION represents an angular distribution that is to be used in an
! uncorrelated angle-energy distribution. This occurs whenever the angle
! distrbution is given in File 4 in an ENDF file. The distribution of angles
! depends on the incoming energy of the neutron, so this type stores a
! distribution for each of a set of incoming energies.
!===============================================================================
type, public :: AngleDistribution
real(8), allocatable :: energy(:)
type(DistributionContainer), allocatable :: distribution(:)
contains
procedure :: sample => angle_sample
procedure :: from_hdf5 => angle_from_hdf5
end type AngleDistribution
contains
function angle_sample(this, E) result(mu)
class(AngleDistribution), intent(in) :: this
real(8), intent(in) :: E ! incoming energy
real(8) :: mu ! sampled cosine of scattering angle
integer :: i ! index on incoming energy grid
integer :: n ! number of incoming energies
real(8) :: r ! interpolation factor on incoming energy grid
! Determine number of incoming energies
n = size(this%energy)
! Find energy bin and calculate interpolation factor -- if the energy is
! outside the range of the tabulated energies, choose the first or last bins
if (E < this%energy(1)) then
i = 1
r = ZERO
elseif (E > this%energy(n)) then
i = n - 1
r = ONE
else
i = binary_search(this%energy, n, E)
r = (E - this%energy(i))/(this%energy(i+1) - this%energy(i))
end if
! Sample between the ith and (i+1)th bin
if (r > prn()) i = i + 1
! Sample i-th distribution
mu = this%distribution(i)%obj%sample()
! Make sure mu is in range [-1,1]
if (abs(mu) > ONE) mu = sign(ONE, mu)
end function angle_sample
subroutine angle_from_hdf5(this, group_id)
class(AngleDistribution), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer :: i, j
integer :: n
integer :: n_energy
integer(HID_T) :: dset_id
integer(HSIZE_T) :: dims(1), dims2(2)
integer, allocatable :: offsets(:)
integer, allocatable :: interp(:)
real(8), allocatable :: temp(:,:)
! Get incoming energies
dset_id = open_dataset(group_id, 'energy')
call get_shape(dset_id, dims)
n_energy = int(dims(1), 4)
allocate(this % energy(n_energy))
allocate(this % distribution(n_energy))
call read_dataset(this % energy, dset_id)
call close_dataset(dset_id)
! Get outgoing energy distribution data
dset_id = open_dataset(group_id, 'mu')
call read_attribute(offsets, dset_id, 'offsets')
call read_attribute(interp, dset_id, 'interpolation')
call get_shape(dset_id, dims2)
allocate(temp(dims2(1), dims2(2)))
call read_dataset(temp, dset_id)
call close_dataset(dset_id)
do i = 1, n_energy
! Determine number of outgoing energies
j = offsets(i)
if (i < n_energy) then
n = offsets(i+1) - j
else
n = size(temp, 1) - j
end if
! Create and initialize tabular distribution
allocate(Tabular :: this % distribution(i) % obj)
select type (mudist => this % distribution(i) % obj)
type is (Tabular)
mudist % interpolation = interp(i)
allocate(mudist % x(n), mudist % p(n), mudist % c(n))
mudist % x(:) = temp(j+1:j+n, 1)
mudist % p(:) = temp(j+1:j+n, 2)
! To get answers that match ACE data, for now we still use the tabulated
! CDF values that were passed through to the HDF5 library. At a later
! time, we can remove the CDF values from the HDF5 library and
! reconstruct them using the PDF
if (.true.) then
mudist % c(:) = temp(j+1:j+n, 3)
else
call mudist % initialize(temp(j+1:j+n, 1), temp(j+1:j+n, 2), interp(i))
end if
end select
j = j + n
end do
end subroutine angle_from_hdf5
end module angle_distribution

21
src/angle_energy.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef OPENMC_ANGLE_ENERGY_H
#define OPENMC_ANGLE_ENERGY_H
namespace openmc {
//==============================================================================
//! Abstract type that defines a correlated or uncorrelated angle-energy
//! distribution that is a function of incoming energy. Each derived type must
//! implement a sample() method that returns an outgoing energy and
//! scattering cosine given an incoming energy.
//==============================================================================
class AngleEnergy {
public:
virtual void sample(double E_in, double& E_out, double& mu) const = 0;
virtual ~AngleEnergy() = default;
};
}
#endif // OPENMC_ANGLE_ENERGY_H

View file

@ -37,8 +37,6 @@ module openmc_api
public :: openmc_calculate_volumes
public :: openmc_cell_filter_get_bins
public :: openmc_cell_get_id
public :: openmc_cell_get_fill
public :: openmc_cell_set_fill
public :: openmc_cell_set_id
public :: openmc_cell_set_temperature
public :: openmc_energy_filter_get_bins
@ -214,7 +212,7 @@ contains
if (p % material == MATERIAL_VOID) then
id = 0
else
id = materials(p % material) % id
id = materials(p % material) % id()
end if
end if
instance = p % cell_instance - 1
@ -259,7 +257,6 @@ contains
if (allocated(tallies)) then
do i = 1, size(tallies)
associate (t => tallies(i) % obj)
t % active = .false.
t % n_realizations = 0
if (allocated(t % results)) then
t % results(:, :, :) = ZERO
@ -278,14 +275,6 @@ contains
k_abs_tra = ZERO
k_sum(:) = ZERO
! Clear active tally lists
call active_analog_tallies % clear()
call active_tracklength_tallies % clear()
call active_meshsurf_tallies % clear()
call active_collision_tallies % clear()
call active_surface_tallies % clear()
call active_tallies % clear()
! Reset timers
call time_total % reset()
call time_total % reset()

View file

@ -1,7 +1,6 @@
#include "cell.h"
#include <cmath>
#include <limits>
#include <sstream>
#include <string>
@ -10,6 +9,8 @@
#include "geometry.h"
#include "hdf5_interface.h"
#include "lattice.h"
#include "material.h"
#include "openmc.h"
#include "settings.h"
#include "surface.h"
#include "xml_interface.h"
@ -21,14 +22,13 @@ namespace openmc {
// Constants
//==============================================================================
// TODO: Convert to enum
constexpr int32_t OP_LEFT_PAREN {std::numeric_limits<int32_t>::max()};
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
extern "C" double FP_PRECISION;
//==============================================================================
// Global variables
//==============================================================================
@ -201,52 +201,64 @@ generate_rpn(int32_t cell_id, std::vector<int32_t> infix)
Cell::Cell(pugi::xml_node cell_node)
{
if (check_for_node(cell_node, "id")) {
id = stoi(get_node_value(cell_node, "id"));
id = std::stoi(get_node_value(cell_node, "id"));
} else {
fatal_error("Must specify id of cell in geometry XML file.");
}
//TODO: don't automatically lowercase cell and surface names
if (check_for_node(cell_node, "name")) {
name = get_node_value(cell_node, "name");
}
if (check_for_node(cell_node, "universe")) {
universe = stoi(get_node_value(cell_node, "universe"));
universe = std::stoi(get_node_value(cell_node, "universe"));
} else {
universe = 0;
}
if (check_for_node(cell_node, "fill")) {
fill = stoi(get_node_value(cell_node, "fill"));
} else {
fill = C_NONE;
}
if (check_for_node(cell_node, "material")) {
//TODO: read material ids.
material.push_back(C_NONE+1);
material.shrink_to_fit();
} else {
material.push_back(C_NONE);
material.shrink_to_fit();
}
// Make sure that either material or fill was specified.
if ((material[0] == C_NONE) && (fill == C_NONE)) {
// Make sure that either material or fill was specified, but not both.
bool fill_present = check_for_node(cell_node, "fill");
bool material_present = check_for_node(cell_node, "material");
if (!(fill_present || material_present)) {
std::stringstream err_msg;
err_msg << "Neither material nor fill was specified for cell " << id;
fatal_error(err_msg);
}
// Make sure that material and fill haven't been specified simultaneously.
if ((material[0] != C_NONE) && (fill != C_NONE)) {
if (fill_present && material_present) {
std::stringstream err_msg;
err_msg << "Cell " << id << " has both a material and a fill specified; "
<< "only one can be specified per cell";
fatal_error(err_msg);
}
if (fill_present) {
fill = std::stoi(get_node_value(cell_node, "fill"));
} else {
fill = C_NONE;
}
// Read the material element. There can be zero materials (filled with a
// universe), more than one material (distribmats), and some materials may
// be "void".
if (material_present) {
std::vector<std::string> mats
{get_node_array<std::string>(cell_node, "material", true)};
if (mats.size() > 0) {
material.reserve(mats.size());
for (std::string mat : mats) {
if (mat.compare("void") == 0) {
material.push_back(MATERIAL_VOID);
} else {
material.push_back(std::stoi(mat));
}
}
} else {
std::stringstream err_msg;
err_msg << "An empty material element was specified for cell " << id;
fatal_error(err_msg);
}
}
// Read the region specification.
std::string region_spec;
if (check_for_node(cell_node, "region")) {
@ -305,7 +317,7 @@ Cell::distance(Position r, Direction u, int32_t on_surface) const
// Calculate the distance to this surface.
// Note the off-by-one indexing
bool coincident {token == on_surface};
double d {surfaces_c[abs(token)-1]->distance(r, u, coincident)};
double d {global_surfaces[abs(token)-1]->distance(r, u, coincident)};
// Check if this distance is the new minimum.
if (d < min_dist) {
@ -346,7 +358,8 @@ Cell::to_hdf5(hid_t cell_group) const
region_spec << " |";
} else {
// Note the off-by-one indexing
region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token);
region_spec << " "
<< copysign(global_surfaces[abs(token)-1]->id, token);
}
}
write_string(cell_group, "region", region_spec.str(), false);
@ -369,7 +382,7 @@ Cell::contains_simple(Position r, Direction u, int32_t on_surface) const
return false;
} else {
// Note the off-by-one indexing
bool sense = surfaces_c[abs(token)-1]->sense(r, u);
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
if (sense != (token > 0)) {return false;}
}
}
@ -411,7 +424,7 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
stack[i_stack] = false;
} else {
// Note the off-by-one indexing
bool sense = surfaces_c[abs(token)-1]->sense(r, u);;
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
stack[i_stack] = (sense == (token > 0));
}
}
@ -433,7 +446,7 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
//==============================================================================
extern "C" void
read_cells(pugi::xml_node *node)
read_cells(pugi::xml_node* node)
{
// Count the number of cells.
for (pugi::xml_node cell_node: node->children("cell")) {n_cells++;}
@ -441,10 +454,8 @@ read_cells(pugi::xml_node *node)
fatal_error("No cells found in geometry.xml!");
}
// Allocate the vector of Cells.
global_cells.reserve(n_cells);
// Loop over XML cell elements and populate the array.
global_cells.reserve(n_cells);
for (pugi::xml_node cell_node: node->children("cell")) {
global_cells.push_back(new Cell(cell_node));
}
@ -462,11 +473,72 @@ read_cells(pugi::xml_node *node)
global_universes[it->second]->cells.push_back(i);
}
}
global_universes.shrink_to_fit();
// Allocate the cell overlap count if necessary.
if (openmc_check_overlaps) overlap_check_count.resize(n_cells, 0);
}
//==============================================================================
// C-API functions
//==============================================================================
extern "C" int
openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n)
{
if (index >= 1 && index <= global_cells.size()) {
//TODO: off-by-one
Cell& c {*global_cells[index - 1]};
*type = c.type;
if (c.type == FILL_MATERIAL) {
*indices = c.material.data();
*n = c.material.size();
} else {
*indices = &c.fill;
*n = 1;
}
} else {
strcpy(openmc_err_msg, "Index in cells array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
extern "C" int
openmc_cell_set_fill(int32_t index, int type, int32_t n,
const int32_t* indices)
{
if (index >= 1 && index <= global_cells.size()) {
//TODO: off-by-one
Cell& c {*global_cells[index - 1]};
if (type == FILL_MATERIAL) {
c.type = FILL_MATERIAL;
c.material.clear();
for (int i = 0; i < n; i++) {
int i_mat = indices[i];
if (i_mat == MATERIAL_VOID) {
c.material.push_back(MATERIAL_VOID);
} else if (i_mat >= 1 && i_mat <= global_materials.size()) {
//TODO: off-by-one
c.material.push_back(i_mat - 1);
} else {
strcpy(openmc_err_msg, "Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
}
c.material.shrink_to_fit();
} else if (type == FILL_UNIVERSE) {
c.type = FILL_UNIVERSE;
} else {
c.type = FILL_LATTICE;
}
} else {
strcpy(openmc_err_msg, "Index in cells array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
@ -474,35 +546,39 @@ read_cells(pugi::xml_node *node)
extern "C" {
Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];}
int32_t cell_id(Cell *c) {return c->id;}
int32_t cell_id(Cell* c) {return c->id;}
void cell_set_id(Cell *c, int32_t id) {c->id = id;}
void cell_set_id(Cell* c, int32_t id) {c->id = id;}
int cell_type(Cell *c) {return c->type;}
int cell_type(Cell* c) {return c->type;}
void cell_set_type(Cell *c, int type) {c->type = type;}
int32_t cell_universe(Cell* c) {return c->universe;}
int32_t cell_universe(Cell *c) {return c->universe;}
int32_t cell_fill(Cell* c) {return c->fill;}
void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;}
int32_t cell_n_instances(Cell* c) {return c->n_instances;}
int32_t cell_fill(Cell *c) {return c->fill;}
int cell_material_size(Cell* c) {return c->material.size();}
int32_t* cell_fill_ptr(Cell *c) {return &c->fill;}
//TODO: off-by-one
int32_t cell_material(Cell* c, int i)
{
int32_t mat = c->material[i-1];
if (mat == MATERIAL_VOID) return MATERIAL_VOID;
return mat + 1;
}
int32_t cell_n_instances(Cell *c) {return c->n_instances;}
bool cell_simple(Cell* c) {return c->simple;}
bool cell_simple(Cell *c) {return c->simple;}
bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface)
bool cell_contains(Cell* c, double xyz[3], double uvw[3], int32_t on_surface)
{
Position r {xyz};
Direction u {uvw};
return c->contains(r, u, on_surface);
}
void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface,
double *min_dist, int32_t *i_surf)
void cell_distance(Cell* c, double xyz[3], double uvw[3], int32_t on_surface,
double* min_dist, int32_t* i_surf)
{
Position r {xyz};
Direction u {uvw};
@ -511,9 +587,9 @@ extern "C" {
*i_surf = out.second;
}
int32_t cell_offset(Cell *c, int map) {return c->offset[map];}
int32_t cell_offset(Cell* c, int map) {return c->offset[map];}
void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);}
void cell_to_hdf5(Cell* c, hid_t group) {c->to_hdf5(group);}
void extend_cells_c(int32_t n)
{

View file

@ -2,6 +2,7 @@
#define OPENMC_CELL_H
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
@ -18,6 +19,7 @@ namespace openmc {
// Constants
//==============================================================================
// TODO: Convert to enum
extern "C" int FILL_MATERIAL;
extern "C" int FILL_UNIVERSE;
extern "C" int FILL_LATTICE;

View file

@ -11,7 +11,7 @@ module cmfd_execute
implicit none
private
public :: execute_cmfd, cmfd_init_batch
public :: execute_cmfd, cmfd_init_batch, cmfd_tally_init
contains
@ -360,6 +360,19 @@ contains
end function get_matrix_idx
!===============================================================================
! CMFD_TALLY_INIT
!===============================================================================
subroutine cmfd_tally_init()
integer :: i
if (cmfd_run) then
do i = 1, size(cmfd_tallies)
cmfd_tallies(i) % obj % active = .true.
end do
end if
end subroutine cmfd_tally_init
!===============================================================================
! CMFD_TALLY_RESET resets all cmfd tallies
!===============================================================================

View file

@ -131,34 +131,6 @@ module constants
! Void material
integer, parameter :: MATERIAL_VOID = -1
! Lattice types
integer, parameter :: &
LATTICE_RECT = 1, & ! Rectangular lattice
LATTICE_HEX = 2 ! Hexagonal lattice
! Lattice boundary crossings
integer, parameter :: &
LATTICE_LEFT = 1, & ! Flag for crossing left (x) lattice boundary
LATTICE_RIGHT = 2, & ! Flag for crossing right (x) lattice boundary
LATTICE_BACK = 3, & ! Flag for crossing back (y) lattice boundary
LATTICE_FRONT = 4, & ! Flag for crossing front (y) lattice boundary
LATTICE_BOTTOM = 5, & ! Flag for crossing bottom (z) lattice boundary
LATTICE_TOP = 6 ! Flag for crossing top (z) lattice boundary
! Surface types
integer, parameter :: &
SURF_PX = 1, & ! Plane parallel to x-plane
SURF_PY = 2, & ! Plane parallel to y-plane
SURF_PZ = 3, & ! Plane parallel to z-plane
SURF_PLANE = 4, & ! Arbitrary plane
SURF_CYL_X = 5, & ! Cylinder along x-axis
SURF_CYL_Y = 6, & ! Cylinder along y-axis
SURF_CYL_Z = 7, & ! Cylinder along z-axis
SURF_SPHERE = 8, & ! Sphere
SURF_CONE_X = 9, & ! Cone parallel to x-axis
SURF_CONE_Y = 10, & ! Cone parallel to y-axis
SURF_CONE_Z = 11 ! Cone parallel to z-axis
! Flag to say that the outside of a lattice is not defined
integer, parameter :: NO_OUTER_UNIVERSE = -1
@ -238,12 +210,6 @@ module constants
! Depletion reactions
integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N]
! ACE table types
integer, parameter :: &
ACE_NEUTRON = 1, & ! continuous-energy neutron
ACE_THERMAL = 2, & ! thermal S(a,b) scattering data
ACE_DOSIMETRY = 3 ! dosimetry cross sections
! MGXS Table Types
integer, parameter :: &
MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data
@ -265,11 +231,6 @@ module constants
EMISSION_DELAYED = 2, & ! Delayed emission of secondary particle
EMISSION_TOTAL = 3 ! Yield represents total emission (prompt + delayed)
! Cross section filetypes
integer, parameter :: &
ASCII = 1, & ! ASCII cross section file
BINARY = 2 ! Binary cross section file
! Library types
integer, parameter :: &
LIBRARY_NEUTRON = 1, &
@ -350,14 +311,11 @@ module constants
SCORE_FISS_Q_RECOV = -15, & ! recoverable fission Q-value
SCORE_DECAY_RATE = -16 ! delayed neutron precursor decay rate
! Maximum scattering order supported
integer, parameter :: MAX_ANG_ORDER = 10
! Tally map bin finding
integer, parameter :: NO_BIN_FOUND = -1
! Tally filter and map types
integer, parameter :: N_FILTER_TYPES = 21
integer, parameter :: N_FILTER_TYPES = 22
integer, parameter :: &
FILTER_UNIVERSE = 1, &
FILTER_MATERIAL = 2, &
@ -379,7 +337,9 @@ module constants
FILTER_SPH_HARMONICS = 18, &
FILTER_SPTL_LEGENDRE = 19, &
FILTER_ZERNIKE = 20, &
FILTER_PARTICLE = 21
FILTER_ZERNIKE_RADIAL = 21, &
FILTER_PARTICLE = 22
! Mesh types
integer, parameter :: &

View file

@ -1,8 +1,8 @@
//! \file constants.h
//! A collection of constants
#ifndef CONSTANTS_H
#define CONSTANTS_H
#ifndef OPENMC_CONSTANTS_H
#define OPENMC_CONSTANTS_H
#include <cmath>
#include <array>
@ -11,6 +11,7 @@
namespace openmc {
// TODO: Replace with xtensor/other library?
typedef std::vector<double> double_1dvec;
typedef std::vector<std::vector<double> > double_2dvec;
typedef std::vector<std::vector<std::vector<double> > > double_3dvec;
@ -21,29 +22,283 @@ typedef std::vector<int> int_1dvec;
typedef std::vector<std::vector<int> > int_2dvec;
typedef std::vector<std::vector<std::vector<int> > > int_3dvec;
constexpr int MAX_SAMPLE {10000};
// ============================================================================
// VERSIONING NUMBERS
constexpr std::array<int, 3> VERSION {0, 10, 0};
// OpenMC major, minor, and release numbers
constexpr int VERSION_MAJOR {0};
constexpr int VERSION_MINOR {10};
constexpr int VERSION_RELEASE {0};
constexpr std::array<int, 3> VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE};
// HDF5 data format
constexpr int HDF5_VERSION[] {1, 0};
// Version numbers for binary files
constexpr std::array<int, 2> VERSION_PARTICLE_RESTART {2, 0};
constexpr std::array<int, 2> VERSION_TRACK {2, 0};
constexpr std::array<int, 2> VERSION_SUMMARY {6, 0};
constexpr std::array<int, 2> VERSION_VOLUME {1, 0};
constexpr std::array<int, 2> VERSION_VOXEL {1, 0};
constexpr std::array<int, 2> VERSION_MGXS_LIBRARY {1, 0};
constexpr char VERSION_MULTIPOLE[] {"v0.2"};
// ============================================================================
// ADJUSTABLE PARAMETERS
// NOTE: This is the only section of the constants module that should ever be
// adjusted. Modifying constants in other sections may cause the code to fail.
// Monoatomic ideal-gas scattering treatment threshold
constexpr double FREE_GAS_THRESHOLD {400.0};
// Significance level for confidence intervals
constexpr double CONFIDENCE_LEVEL {0.95};
// Used for surface current tallies
constexpr double TINY_BIT {1e-8};
// User for precision in geometry
constexpr double FP_PRECISION {1e-14};
constexpr double FP_REL_PRECISION {1e-5};
constexpr double FP_COINCIDENT {1e-12};
// Maximum number of collisions/crossings
constexpr int MAX_EVENTS {1000000};
constexpr int MAX_SAMPLE {100000};
// Maximum number of words in a single line, length of line, and length of
// single word
constexpr int MAX_WORDS {500};
constexpr int MAX_LINE_LEN {250};
constexpr int MAX_WORD_LEN {150};
constexpr int MAX_FILE_LEN {255};
// Physical Constants
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
// Maximum number of external source spatial resamples to encounter before an
// error is thrown.
constexpr int EXTSRC_REJECT_THRESHOLD {10000};
constexpr double EXTSRC_REJECT_FRACTION {0.05};
// ============================================================================
// MATH AND PHYSICAL CONSTANTS
// Values here are from the Committee on Data for Science and Technology
// (CODATA) 2014 recommendation (doi:10.1103/RevModPhys.88.035009).
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
// use so for now we will reuse the Fortran constant until we are OK with
// modifying test results
constexpr double PI {3.1415926535898};
const double SQRT_PI {std::sqrt(PI)};
constexpr double INFTY {std::numeric_limits<double>::max()};
// Physical constants
constexpr double MASS_NEUTRON {1.00866491588}; // mass of a neutron in amu
constexpr double MASS_NEUTRON_EV {939.5654133e6}; // mass of a neutron in eV/c^2
constexpr double MASS_PROTON {1.007276466879}; // mass of a proton in amu
constexpr double MASS_ELECTRON_EV {0.5109989461e6}; // electron mass energy equivalent in eV/c^2
constexpr double FINE_STRUCTURE {137.035999139}; // inverse fine structure constant
constexpr double PLANCK_C {1.2398419739062977e4}; // Planck's constant times c in eV-Angstroms
constexpr double AMU {1.660539040e-27}; // 1 amu in kg
constexpr double C_LIGHT {2.99792458e8}; // speed of light in m/s
constexpr double N_AVOGADRO {0.6022140857}; // Avogadro's number in 10^24/mol
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
// Electron subshell labels
constexpr char SUBSHELLS[][4] {
"K ", "L1 ", "L2 ", "L3 ", "M1 ", "M2 ", "M3 ", "M4 ", "M5 ",
"N1 ", "N2 ", "N3 ", "N4 ", "N5 ", "N6 ", "N7 ", "O1 ", "O2 ",
"O3 ", "O4 ", "O5 ", "O6 ", "O7 ", "O8 ", "O9 ", "P1 ", "P2 ",
"P3 ", "P4 ", "P5 ", "P6 ", "P7 ", "P8 ", "P9 ", "P10", "P11",
"Q1 ", "Q2 ", "Q3 "
};
// Void material
// TODO: refactor and remove
constexpr int MATERIAL_VOID {-1};
// ============================================================================
// CROSS SECTION RELATED CONSTANTS
// Angular distribution type
// TODO: Convert to enum
constexpr int ANGLE_ISOTROPIC {1};
constexpr int ANGLE_32_EQUI {2};
constexpr int ANGLE_TABULAR {3};
constexpr int ANGLE_LEGENDRE {4};
constexpr int ANGLE_HISTOGRAM {5};
// Temperature treatment method
// TODO: Convert to enum?
constexpr int TEMPERATURE_NEAREST {1};
constexpr int TEMPERATURE_INTERPOLATION {2};
// Secondary energy mode for S(a,b) inelastic scattering
// TODO: Convert to enum
constexpr int SAB_SECONDARY_EQUAL {0}; // Equally-likely outgoing energy bins
constexpr int SAB_SECONDARY_SKEWED {1}; // Skewed outgoing energy bins
constexpr int SAB_SECONDARY_CONT {2}; // Continuous, linear-linear interpolation
// Elastic mode for S(a,b) elastic scattering
// TODO: Convert to enum
constexpr int SAB_ELASTIC_DISCRETE {3}; // Sample from discrete cosines
constexpr int SAB_ELASTIC_EXACT {4}; // Exact treatment for coherent elastic
// Reaction types
// TODO: Convert to enum
constexpr int TOTAL_XS {1};
constexpr int ELASTIC {2};
constexpr int N_NONELASTIC {3};
constexpr int N_LEVEL {4};
constexpr int MISC {5};
constexpr int N_2ND {11};
constexpr int N_2N {16};
constexpr int N_3N {17};
constexpr int N_FISSION {18};
constexpr int N_F {19};
constexpr int N_NF {20};
constexpr int N_2NF {21};
constexpr int N_NA {22};
constexpr int N_N3A {23};
constexpr int N_2NA {24};
constexpr int N_3NA {25};
constexpr int N_NP {28};
constexpr int N_N2A {29};
constexpr int N_2N2A {30};
constexpr int N_ND {32};
constexpr int N_NT {33};
constexpr int N_N3HE {34};
constexpr int N_ND2A {35};
constexpr int N_NT2A {36};
constexpr int N_4N {37};
constexpr int N_3NF {38};
constexpr int N_2NP {41};
constexpr int N_3NP {42};
constexpr int N_N2P {44};
constexpr int N_NPA {45};
constexpr int N_N1 {51};
constexpr int N_N40 {90};
constexpr int N_NC {91};
constexpr int N_DISAPPEAR {101};
constexpr int N_GAMMA {102};
constexpr int N_P {103};
constexpr int N_D {104};
constexpr int N_T {105};
constexpr int N_3HE {106};
constexpr int N_A {107};
constexpr int N_2A {108};
constexpr int N_3A {109};
constexpr int N_2P {111};
constexpr int N_PA {112};
constexpr int N_T2A {113};
constexpr int N_D2A {114};
constexpr int N_PD {115};
constexpr int N_PT {116};
constexpr int N_DA {117};
constexpr int N_5N {152};
constexpr int N_6N {153};
constexpr int N_2NT {154};
constexpr int N_TA {155};
constexpr int N_4NP {156};
constexpr int N_3ND {157};
constexpr int N_NDA {158};
constexpr int N_2NPA {159};
constexpr int N_7N {160};
constexpr int N_8N {161};
constexpr int N_5NP {162};
constexpr int N_6NP {163};
constexpr int N_7NP {164};
constexpr int N_4NA {165};
constexpr int N_5NA {166};
constexpr int N_6NA {167};
constexpr int N_7NA {168};
constexpr int N_4ND {169};
constexpr int N_5ND {170};
constexpr int N_6ND {171};
constexpr int N_3NT {172};
constexpr int N_4NT {173};
constexpr int N_5NT {174};
constexpr int N_6NT {175};
constexpr int N_2N3HE {176};
constexpr int N_3N3HE {177};
constexpr int N_4N3HE {178};
constexpr int N_3N2P {179};
constexpr int N_3N3A {180};
constexpr int N_3NPA {181};
constexpr int N_DT {182};
constexpr int N_NPD {183};
constexpr int N_NPT {184};
constexpr int N_NDT {185};
constexpr int N_NP3HE {186};
constexpr int N_ND3HE {187};
constexpr int N_NT3HE {188};
constexpr int N_NTA {189};
constexpr int N_2N2P {190};
constexpr int N_P3HE {191};
constexpr int N_D3HE {192};
constexpr int N_3HEA {193};
constexpr int N_4N2P {194};
constexpr int N_4N2A {195};
constexpr int N_4NPA {196};
constexpr int N_3P {197};
constexpr int N_N3P {198};
constexpr int N_3N2PA {199};
constexpr int N_5N2P {200};
constexpr int COHERENT {502};
constexpr int INCOHERENT {504};
constexpr int PAIR_PROD_ELEC {515};
constexpr int PAIR_PROD {516};
constexpr int PAIR_PROD_NUC {517};
constexpr int PHOTOELECTRIC {522};
constexpr int N_P0 {600};
constexpr int N_PC {649};
constexpr int N_D0 {650};
constexpr int N_DC {699};
constexpr int N_T0 {700};
constexpr int N_TC {749};
constexpr int N_3HE0 {750};
constexpr int N_3HEC {799};
constexpr int N_A0 {800};
constexpr int N_AC {849};
constexpr int N_2N0 {875};
constexpr int N_2NC {891};
// Fission neutron emission (nu) type
constexpr int NU_NONE {0}; // No nu values (non-fissionable)
constexpr int NU_POLYNOMIAL {1}; // Nu values given by polynomial
constexpr int NU_TABULAR {2}; // Nu values given by tabular distribution
// Library types
constexpr int LIBRARY_NEUTRON {1};
constexpr int LIBRARY_THERMAL {2};
constexpr int LIBRARY_PHOTON {3};
constexpr int LIBRARY_MULTIGROUP {4};
// Probability table parameters
constexpr int URR_CUM_PROB {1};
constexpr int URR_TOTAL {2};
constexpr int URR_ELASTIC {3};
constexpr int URR_FISSION {4};
constexpr int URR_N_GAMMA {5};
constexpr int URR_HEATING {6};
// Maximum number of partial fission reactions
constexpr int PARTIAL_FISSION_MAX {4};
// Resonance elastic scattering methods
// TODO: Convert to enum
constexpr int RES_SCAT_ARES {1};
constexpr int RES_SCAT_DBRC {2};
constexpr int RES_SCAT_WCM {3};
constexpr int RES_SCAT_CXS {4};
// Electron treatments
// TODO: Convert to enum
constexpr int ELECTRON_LED {1}; // Local Energy Deposition
constexpr int ELECTRON_TTB {2}; // Thick Target Bremsstrahlung
// ============================================================================
// MULTIGROUP RELATED
// MGXS Table Types
// TODO: Convert to enum
constexpr int MGXS_ISOTROPIC {1}; // Isotroically weighted data
constexpr int MGXS_ANGLE {2}; // Data by angular bins
@ -53,18 +308,8 @@ constexpr double MACROSCOPIC_AWR {-2.};
// Number of mu bins to use when converting Legendres to tabular type
constexpr int DEFAULT_NMU {33};
// Temperature treatment method
constexpr int TEMPERATURE_NEAREST {1};
constexpr int TEMPERATURE_INTERPOLATION {2};
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
// use so for now we will reuse the Fortran constant until we are OK with
// modifying test results
constexpr double PI {3.1415926535898};
const double SQRT_PI {std::sqrt(PI)};
// Mgxs::get_xs enumerated types
// TODO: Convert to enum
constexpr int MG_GET_XS_TOTAL {0};
constexpr int MG_GET_XS_ABSORPTION {1};
constexpr int MG_GET_XS_INVERSE_VELOCITY {2};
@ -81,11 +326,120 @@ constexpr int MG_GET_XS_NU_FISSION {12};
constexpr int MG_GET_XS_CHI_PROMPT {13};
constexpr int MG_GET_XS_CHI_DELAYED {14};
extern "C" double FP_COINCIDENT;
extern "C" double FP_PRECISION;
constexpr double INFTY {std::numeric_limits<double>::max()};
// ============================================================================
// TALLY-RELATED CONSTANTS
// Tally result entries
constexpr int RESULT_VALUE {1};
constexpr int RESULT_SUM {2};
constexpr int RESULT_SUM_SQ {3};
// Tally type
// TODO: Convert to enum
constexpr int TALLY_VOLUME {1};
constexpr int TALLY_MESH_SURFACE {2};
constexpr int TALLY_SURFACE {3};
// Tally estimator types
// TODO: Convert to enum
constexpr int ESTIMATOR_ANALOG {1};
constexpr int ESTIMATOR_TRACKLENGTH {2};
constexpr int ESTIMATOR_COLLISION {3};
// Event types for tallies
// TODO: Convert to enum
constexpr int EVENT_SURFACE {-2};
constexpr int EVENT_LATTICE {-1};
constexpr int EVENT_SCATTER {1};
constexpr int EVENT_ABSORB {2};
// Tally score type -- if you change these, make sure you also update the
// _SCORES dictionary in openmc/capi/tally.py
// TODO: Convert to enum
constexpr int SCORE_FLUX {-1}; // flux
constexpr int SCORE_TOTAL {-2}; // total reaction rate
constexpr int SCORE_SCATTER {-3}; // scattering rate
constexpr int SCORE_NU_SCATTER {-4}; // scattering production rate
constexpr int SCORE_ABSORPTION {-5}; // absorption rate
constexpr int SCORE_FISSION {-6}; // fission rate
constexpr int SCORE_NU_FISSION {-7}; // neutron production rate
constexpr int SCORE_KAPPA_FISSION {-8}; // fission energy production rate
constexpr int SCORE_CURRENT {-9}; // current
constexpr int SCORE_EVENTS {-10}; // number of events
constexpr int SCORE_DELAYED_NU_FISSION {-11}; // delayed neutron production rate
constexpr int SCORE_PROMPT_NU_FISSION {-12}; // prompt neutron production rate
constexpr int SCORE_INVERSE_VELOCITY {-13}; // flux-weighted inverse velocity
constexpr int SCORE_FISS_Q_PROMPT {-14}; // prompt fission Q-value
constexpr int SCORE_FISS_Q_RECOV {-15}; // recoverable fission Q-value
constexpr int SCORE_DECAY_RATE {-16}; // delayed neutron precursor decay rate
// Tally map bin finding
constexpr int NO_BIN_FOUND {-1};
// Tally filter and map types
// TODO: Refactor to remove or convert to enum
constexpr int FILTER_UNIVERSE {1};
constexpr int FILTER_MATERIAL {2};
constexpr int FILTER_CELL {3};
constexpr int FILTER_CELLBORN {4};
constexpr int FILTER_SURFACE {5};
constexpr int FILTER_MESH {6};
constexpr int FILTER_ENERGYIN {7};
constexpr int FILTER_ENERGYOUT {8};
constexpr int FILTER_DISTRIBCELL {9};
constexpr int FILTER_MU {10};
constexpr int FILTER_POLAR {11};
constexpr int FILTER_AZIMUTHAL {12};
constexpr int FILTER_DELAYEDGROUP {13};
constexpr int FILTER_ENERGYFUNCTION {14};
constexpr int FILTER_CELLFROM {15};
constexpr int FILTER_MESHSURFACE {16};
constexpr int FILTER_LEGENDRE {17};
constexpr int FILTER_SPH_HARMONICS {18};
constexpr int FILTER_SPTL_LEGENDRE {19};
constexpr int FILTER_ZERNIKE {20};
constexpr int FILTER_PARTICLE {21};
// Mesh types
constexpr int MESH_REGULAR {1};
// Tally surface current directions
constexpr int OUT_LEFT {1}; // x min
constexpr int IN_LEFT {2}; // x min
constexpr int OUT_RIGHT {3}; // x max
constexpr int IN_RIGHT {4}; // x max
constexpr int OUT_BACK {5}; // y min
constexpr int IN_BACK {6}; // y min
constexpr int OUT_FRONT {7}; // y max
constexpr int IN_FRONT {8}; // y max
constexpr int OUT_BOTTOM {9}; // z min
constexpr int IN_BOTTOM {10}; // z min
constexpr int OUT_TOP {11}; // z max
constexpr int IN_TOP {12}; // z max
// Tally trigger types and threshold
constexpr int VARIANCE {1};
constexpr int RELATIVE_ERROR {2};
constexpr int STANDARD_DEVIATION {3};
// Global tally parameters
constexpr int K_COLLISION {1};
constexpr int K_ABSORPTION {2};
constexpr int K_TRACKLENGTH {3};
constexpr int LEAKAGE {4};
// Differential tally independent variables
constexpr int DIFF_DENSITY {1};
constexpr int DIFF_NUCLIDE_DENSITY {2};
constexpr int DIFF_TEMPERATURE {3};
constexpr int C_NONE {-1};
// Interpolation rules
enum class Interpolation {
histogram, lin_lin, lin_log, log_lin, log_log
};
} // namespace openmc
#endif // CONSTANTS_H
#endif // OPENMC_CONSTANTS_H

266
src/distribution.cpp Normal file
View file

@ -0,0 +1,266 @@
#include "distribution.h"
#include <algorithm> // for copy
#include <cmath> // for sqrt, floor, max
#include <iterator> // for back_inserter
#include <numeric> // for accumulate
#include <string> // for string, stod
#include "error.h"
#include "math_functions.h"
#include "random_lcg.h"
#include "xml_interface.h"
namespace openmc {
//==============================================================================
// Discrete implementation
//==============================================================================
Discrete::Discrete(pugi::xml_node node)
{
auto params = get_node_array<double>(node, "parameters");
std::size_t n = params.size();
std::copy(params.begin(), params.begin() + n/2, std::back_inserter(x_));
std::copy(params.begin() + n/2, params.end(), std::back_inserter(p_));
normalize();
}
Discrete::Discrete(const double* x, const double* p, int n)
: x_{x, x+n}, p_{p, p+n}
{
normalize();
}
double Discrete::sample() const
{
int n = x_.size();
if (n > 1) {
double xi = prn();
double c = 0.0;
for (int i = 0; i < n; ++i) {
c += p_[i];
if (xi < c) return x_[i];
}
// throw exception?
} else {
return x_[0];
}
}
void Discrete::normalize()
{
// Renormalize density function so that it sums to unity
double norm = std::accumulate(p_.begin(), p_.end(), 0.0);
for (auto& p_i : p_)
p_i /= norm;
}
//==============================================================================
// Uniform implementation
//==============================================================================
Uniform::Uniform(pugi::xml_node node)
{
auto params = get_node_array<double>(node, "parameters");
if (params.size() != 2)
openmc::fatal_error("Uniform distribution must have two "
"parameters specified.");
a_ = params.at(0);
b_ = params.at(1);
}
double Uniform::sample() const
{
return a_ + prn()*(b_ - a_);
}
//==============================================================================
// Maxwell implementation
//==============================================================================
Maxwell::Maxwell(pugi::xml_node node)
{
theta_ = std::stod(get_node_value(node, "parameters"));
}
double Maxwell::sample() const
{
return maxwell_spectrum_c(theta_);
}
//==============================================================================
// Watt implementation
//==============================================================================
Watt::Watt(pugi::xml_node node)
{
auto params = get_node_array<double>(node, "parameters");
if (params.size() != 2)
openmc::fatal_error("Watt energy distribution must have two "
"parameters specified.");
a_ = params.at(0);
b_ = params.at(1);
}
double Watt::sample() const
{
return watt_spectrum_c(a_, b_);
}
//==============================================================================
// Tabular implementation
//==============================================================================
Tabular::Tabular(pugi::xml_node node)
{
if (check_for_node(node, "interpolation")) {
std::string temp = get_node_value(node, "interpolation");
if (temp == "histogram") {
interp_ = Interpolation::histogram;
} else if (temp == "linear-linear") {
interp_ = Interpolation::lin_lin;
} else {
openmc::fatal_error("Unknown interpolation type for distribution: " + temp);
}
} else {
interp_ = Interpolation::histogram;
}
// Read and initialize tabular distribution
auto params = get_node_array<double>(node, "parameters");
std::size_t n = params.size() / 2;
const double* x = params.data();
const double* p = x + n;
init(x, p, n);
}
Tabular::Tabular(const double* x, const double* p, int n, Interpolation interp, const double* c)
: interp_{interp}
{
init(x, p, n, c);
}
void Tabular::init(const double* x, const double* p, std::size_t n, const double* c)
{
// Copy x/p arrays into vectors
std::copy(x, x + n, std::back_inserter(x_));
std::copy(p, p + n, std::back_inserter(p_));
// Check interpolation parameter
if (interp_ != Interpolation::histogram &&
interp_ != Interpolation::lin_lin) {
openmc::fatal_error("Only histogram and linear-linear interpolation "
"for tabular distribution is supported.");
}
// Calculate cumulative distribution function
if (c) {
std::copy(c, c + n, std::back_inserter(c_));
} else {
c_.resize(n);
c_[0] = 0.0;
for (int i = 1; i < n; ++i) {
if (interp_ == Interpolation::histogram) {
c_[i] = c_[i-1] + p_[i-1]*(x_[i] - x_[i-1]);
} else if (interp_ == Interpolation::lin_lin) {
c_[i] = c_[i-1] + 0.5*(p_[i-1] + p_[i]) * (x_[i] - x_[i-1]);
}
}
}
// Normalize density and distribution functions
for (int i = 0; i < n; ++i) {
p_[i] = p_[i]/c_[n-1];
c_[i] = c_[i]/c_[n-1];
}
}
double Tabular::sample() const
{
// Sample value of CDF
double c = prn();
// Find first CDF bin which is above the sampled value
double c_i = c_[0];
int i;
std::size_t n = c_.size();
for (i = 0; i < n - 1; ++i) {
if (c <= c_[i+1]) break;
c_i = c_[i+1];
}
// Determine bounding PDF values
double x_i = x_[i];
double p_i = p_[i];
if (interp_ == Interpolation::histogram) {
// Histogram interpolation
if (p_i > 0.0) {
return x_i + (c - c_i)/p_i;
} else {
return x_i;
}
} else {
// Linear-linear interpolation
double x_i1 = x_[i + 1];
double p_i1 = p_[i + 1];
double m = (p_i1 - p_i)/(x_i1 - x_i);
if (m == 0.0) {
return x_i + (c - c_i)/p_i;
} else {
return x_i + (std::sqrt(std::max(0.0, p_i*p_i + 2*m*(c - c_i))) - p_i)/m;
}
}
}
//==============================================================================
// Equiprobable implementation
//==============================================================================
double Equiprobable::sample() const
{
std::size_t n = x_.size();
double r = prn();
int i = std::floor((n - 1)*r);
double xl = x_[i];
double xr = x_[i+i];
return xl + ((n - 1)*r - i) * (xr - xl);
}
//==============================================================================
// Helper function
//==============================================================================
UPtrDist distribution_from_xml(pugi::xml_node node)
{
if (!check_for_node(node, "type"))
openmc::fatal_error("Distribution type must be specified.");
// Determine type of distribution
std::string type = get_node_value(node, "type", true, true);
// Allocate extension of Distribution
if (type == "uniform") {
return UPtrDist{new Uniform(node)};
} else if (type == "maxwell") {
return UPtrDist{new Maxwell(node)};
} else if (type == "watt") {
return UPtrDist{new Watt(node)};
} else if (type == "discrete") {
return UPtrDist{new Discrete(node)};
} else if (type == "tabular") {
return UPtrDist{new Tabular(node)};
} else {
openmc::fatal_error("Invalid distribution type: " + type);
}
}
} // namespace openmc

150
src/distribution.h Normal file
View file

@ -0,0 +1,150 @@
//! \file distribution.h
//! Univariate probability distributions
#ifndef OPENMC_DISTRIBUTION_H
#define OPENMC_DISTRIBUTION_H
#include <cstddef> // for size_t
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "pugixml.hpp"
#include "constants.h"
namespace openmc {
//==============================================================================
//! Abstract class representing a univariate probability distribution
//==============================================================================
class Distribution {
public:
virtual ~Distribution() = default;
virtual double sample() const = 0;
};
//==============================================================================
//! A discrete distribution (probability mass function)
//==============================================================================
class Discrete : public Distribution {
public:
explicit Discrete(pugi::xml_node node);
Discrete(const double* x, const double* p, int n);
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
std::vector<double> x_; //!< Possible outcomes
std::vector<double> p_; //!< Probability of each outcome
//! Normalize distribution so that probabilities sum to unity
void normalize();
};
//==============================================================================
//! Uniform distribution over the interval [a,b]
//==============================================================================
class Uniform : public Distribution {
public:
explicit Uniform(pugi::xml_node node);
Uniform(double a, double b) : a_{a}, b_{b} {};
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
double a_; //!< Lower bound of distribution
double b_; //!< Upper bound of distribution
};
//==============================================================================
//! Maxwellian distribution of form c*E*exp(-E/theta)
//==============================================================================
class Maxwell : public Distribution {
public:
explicit Maxwell(pugi::xml_node node);
Maxwell(double theta) : theta_{theta} { };
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
double theta_; //!< Factor in exponential [eV]
};
//==============================================================================
//! Watt fission spectrum with form c*exp(-E/a)*sinh(sqrt(b*E))
//==============================================================================
class Watt : public Distribution {
public:
explicit Watt(pugi::xml_node node);
Watt(double a, double b) : a_{a}, b_{b} { };
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
double a_; //!< Factor in exponential [eV]
double b_; //!< Factor in square root [1/eV]
};
//==============================================================================
//! Histogram or linear-linear interpolated tabular distribution
//==============================================================================
class Tabular : public Distribution {
public:
explicit Tabular(pugi::xml_node node);
Tabular(const double* x, const double* p, int n, Interpolation interp,
const double* c=nullptr);
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
std::vector<double> x_; //!< tabulated independent variable
std::vector<double> p_; //!< tabulated probability density
std::vector<double> c_; //!< cumulative distribution at tabulated values
Interpolation interp_; //!< interpolation rule
//! Initialize tabulated probability density function
//! \param x Array of values for independent variable
//! \param p Array of tabulated probabilities
//! \param n Number of tabulated values
void init(const double* x, const double* p, std::size_t n,
const double* c=nullptr);
};
//==============================================================================
//! Equiprobable distribution
//==============================================================================
class Equiprobable : public Distribution {
public:
explicit Equiprobable(pugi::xml_node node);
Equiprobable(const double* x, int n) : x_{x, x+n} { };
//! Sample a value from the distribution
//! \return Sampled value
double sample() const;
private:
std::vector<double> x_; //! Possible outcomes
};
using UPtrDist = std::unique_ptr<Distribution>;
//! Return univariate probability distribution specified in XML file
//! \param[in] node XML node representing distribution
//! \return Unique pointer to distribution
UPtrDist distribution_from_xml(pugi::xml_node node);
} // namespace openmc
#endif // OPENMC_DISTRIBUTION_H

View file

@ -0,0 +1,95 @@
#include "distribution_angle.h"
#include <cmath> // for abs, copysign
#include <vector> // for vector
#include "endf.h"
#include "hdf5_interface.h"
#include "random_lcg.h"
#include "search.h"
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
namespace openmc {
//==============================================================================
// AngleDistribution implementation
//==============================================================================
AngleDistribution::AngleDistribution(hid_t group)
{
// Get incoming energies
read_dataset(group, "energy", energy_);
int n_energy = energy_.size();
// Get outgoing energy distribution data
std::vector<int> offsets;
std::vector<int> interp;
hid_t dset = open_dataset(group, "mu");
read_attribute(dset, "offsets", offsets);
read_attribute(dset, "interpolation", interp);
xt::xarray<double> temp;
read_dataset(dset, temp);
close_dataset(dset);
for (int i = 0; i < n_energy; ++i) {
// Determine number of outgoing energies
int j = offsets[i];
int n;
if (i < n_energy - 1) {
n = offsets[i+1] - j;
} else {
n = temp.shape()[1] - j;
}
// Create and initialize tabular distribution
auto xs = xt::view(temp, 0, xt::range(j, j+n));
auto ps = xt::view(temp, 1, xt::range(j, j+n));
auto cs = xt::view(temp, 2, xt::range(j, j+n));
std::vector<double> x {xs.begin(), xs.end()};
std::vector<double> p {ps.begin(), ps.end()};
std::vector<double> c {cs.begin(), cs.end()};
// To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF
Tabular* mudist = new Tabular{x.data(), p.data(), n, int2interp(interp[i]),
c.data()};
distribution_.emplace_back(mudist);
}
}
double AngleDistribution::sample(double E) const
{
// Determine number of incoming energies
auto n = energy_.size();
// Find energy bin and calculate interpolation factor -- if the energy is
// outside the range of the tabulated energies, choose the first or last bins
int i;
double r;
if (E < energy_[0]) {
i = 0;
r = 0.0;
} else if (E > energy_[n - 1]) {
i = n - 2;
r = 1.0;
} else {
i = lower_bound_index(energy_.begin(), energy_.end(), E);
r = (E - energy_[i])/(energy_[i+1] - energy_[i]);
}
// Sample between the ith and (i+1)th bin
if (r > prn()) ++i;
// Sample i-th distribution
double mu = distribution_[i]->sample();
// Make sure mu is in range [-1,1] and return
if (std::abs(mu) > 1.0) mu = std::copysign(1.0, mu);
return mu;
}
} // namespace openmc

39
src/distribution_angle.h Normal file
View file

@ -0,0 +1,39 @@
//! \file distribution_angle.h
//! Angle distribution dependent on incident particle energy
#ifndef OPENMC_DISTRIBUTION_ANGLE_H
#define OPENMC_DISTRIBUTION_ANGLE_H
#include <vector> // for vector
#include "distribution.h"
#include "hdf5.h"
namespace openmc {
//==============================================================================
//! Angle distribution that depends on incident particle energy
//==============================================================================
class AngleDistribution {
public:
AngleDistribution() = default;
explicit AngleDistribution(hid_t group);
//! Sample an angle given an incident particle energy
//! \param[in] E Particle energy in [eV]
//! \return Cosine of the angle in the range [-1,1]
double sample(double E) const;
//! Determine whether angle distribution is empty
//! \return Whether distribution is empty
bool empty() const { return energy_.empty(); }
private:
std::vector<double> energy_;
std::vector<UPtrDist> distribution_;
};
} // namespace openmc
#endif // OPENMC_DISTRIBUTION_ANGLE_H

337
src/distribution_energy.cpp Normal file
View file

@ -0,0 +1,337 @@
#include "distribution_energy.h"
#include <algorithm> // for max, min, copy, move
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include "endf.h"
#include "hdf5_interface.h"
#include "math_functions.h"
#include "random_lcg.h"
#include "search.h"
#include "xtensor/xview.hpp"
namespace openmc {
//==============================================================================
// DiscretePhoton implementation
//==============================================================================
DiscretePhoton::DiscretePhoton(hid_t group)
{
read_attribute(group, "primary_flag", primary_flag_);
read_attribute(group, "energy", energy_);
read_attribute(group, "atomic_weight_ratio", A_);
}
double DiscretePhoton::sample(double E) const
{
if (primary_flag_ == 2) {
return energy_ + A_/(A_+ 1)*E;
} else {
return energy_;
}
}
//==============================================================================
// LevelInelastic implementation
//==============================================================================
LevelInelastic::LevelInelastic(hid_t group)
{
read_attribute(group, "threshold", threshold_);
read_attribute(group, "mass_ratio", mass_ratio_);
}
double LevelInelastic::sample(double E) const
{
return mass_ratio_*(E - threshold_);
}
//==============================================================================
// ContinuousTabular implementation
//==============================================================================
ContinuousTabular::ContinuousTabular(hid_t group)
{
// Open incoming energy dataset
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
interpolation_.push_back(int2interp(i));
n_region_ = breakpoints_.size();
// Get incoming energies
read_dataset(dset, energy_);
std::size_t n_energy = energy_.size();
close_dataset(dset);
// Get outgoing energy distribution data
dset = open_dataset(group, "distribution");
std::vector<int> offsets;
std::vector<int> interp;
std::vector<int> n_discrete;
read_attribute(dset, "offsets", offsets);
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
for (int i = 0; i < n_energy; ++i) {
// Determine number of outgoing energies
int j = offsets[i];
int n;
if (i < n_energy - 1) {
n = offsets[i+1] - j;
} else {
n = eout.shape()[1] - j;
}
// Assign interpolation scheme and number of discrete lines
CTTable d;
d.interpolation = int2interp(interp[i]);
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j+n));
d.p = xt::view(eout, 1, xt::range(j, j+n));
// To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF
if (true) {
d.c = xt::view(eout, 2, xt::range(j, j+n));
} else {
// Calculate cumulative distribution function -- discrete portion
for (int k = 0; k < d.n_discrete; ++k) {
if (k == 0) {
d.c[k] = d.p[k];
} else {
d.c[k] = d.c[k-1] + d.p[k];
}
}
// Continuous portion
for (int k = d.n_discrete; k < n; ++k) {
if (k == d.n_discrete) {
d.c[k] = d.c[k-1] + d.p[k];
} else {
if (d.interpolation == Interpolation::histogram) {
d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]);
} else if (d.interpolation == Interpolation::lin_lin) {
d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) *
(d.e_out[k] - d.e_out[k-1]);
}
}
}
// Normalize density and distribution functions
d.p /= d.c[n - 1];
d.c /= d.c[n - 1];
}
distribution_.push_back(std::move(d));
} // incoming energies
}
double ContinuousTabular::sample(double E) const
{
// Read number of interpolation regions and incoming energies
bool histogram_interp;
if (n_region_ == 1) {
histogram_interp = (interpolation_[0] == Interpolation::histogram);
} else {
histogram_interp = false;
}
// Find energy bin and calculate interpolation factor -- if the energy is
// outside the range of the tabulated energies, choose the first or last bins
auto n_energy_in = energy_.size();
int i;
double r;
if (E < energy_[0]) {
i = 0;
r = 0.0;
} else if (E > energy_[n_energy_in - 1]) {
i = n_energy_in - 2;
r = 1.0;
} else {
i = lower_bound_index(energy_.begin(), energy_.end(), E);
r = (E - energy_[i]) / (energy_[i+1] - energy_[i]);
}
// Sample between the ith and [i+1]th bin
int l;
if (histogram_interp) {
l = i;
} else {
l = r > prn() ? i + 1 : i;
}
// Interpolation for energy E1 and EK
int n_energy_out = distribution_[i].e_out.size();
double E_i_1 = distribution_[i].e_out[0];
double E_i_K = distribution_[i].e_out[n_energy_out - 1];
n_energy_out = distribution_[i+1].e_out.size();
double E_i1_1 = distribution_[i+1].e_out[0];
double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1];
double E_1 = E_i_1 + r*(E_i1_1 - E_i_1);
double E_K = E_i_K + r*(E_i1_K - E_i_K);
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
double r1 = prn();
double c_k = distribution_[l].c[0];
double c_k1;
int k;
for (k = 0; k < n_energy_out - 2; ++k) {
c_k1 = distribution_[l].c[k+1];
if (r1 < c_k1) break;
c_k = c_k1;
}
// Check to make sure 1 <= k <= NP - 1
k = std::max(0, std::min(k, n_energy_out - 2));
double E_l_k = distribution_[l].e_out[k];
double p_l_k = distribution_[l].p[k];
double E_out;
if (distribution_[l].interpolation == Interpolation::histogram) {
// Histogram interpolation
if (p_l_k > 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k;
}
} else if (distribution_[l].interpolation == Interpolation::lin_lin) {
// Linear-linear interpolation
double E_l_k1 = distribution_[l].e_out[k+1];
double p_l_k1 = distribution_[l].p[k+1];
double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k);
if (frac == 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k +
2.0*frac*(r1 - c_k))) - p_l_k)/frac;
}
}
// Now interpolate between incident energy bins i and i + 1
if (!histogram_interp && n_energy_out > 1) {
if (l == i) {
return E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1);
} else {
return E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1);
}
} else {
return E_out;
}
}
//==============================================================================
// MaxwellEnergy implementation
//==============================================================================
MaxwellEnergy::MaxwellEnergy(hid_t group)
{
read_attribute(group, "u", u_);
hid_t dset = open_dataset(group, "theta");
theta_ = Tabulated1D{dset};
close_dataset(dset);
}
double MaxwellEnergy::sample(double E) const
{
// Get temperature corresponding to incoming energy
double theta = theta_(E);
while (true) {
// Sample maxwell fission spectrum
double E_out = maxwell_spectrum_c(theta);
// Accept energy based on restriction energy
if (E_out <= E - u_) return E_out;
}
}
//==============================================================================
// Evaporation implementation
//==============================================================================
Evaporation::Evaporation(hid_t group)
{
read_attribute(group, "u", u_);
hid_t dset = open_dataset(group, "theta");
theta_ = Tabulated1D{dset};
close_dataset(dset);
}
double Evaporation::sample(double E) const
{
// Get temperature corresponding to incoming energy
double theta = theta_(E);
double y = (E - u_)/theta;
double v = 1.0 - std::exp(-y);
// Sample outgoing energy based on evaporation spectrum probability
// density function
double x;
while (true) {
x = -std::log((1.0 - v*prn())*(1.0 - v*prn()));
if (x <= y) break;
}
return x*theta;
}
//==============================================================================
// WattEnergy implementation
//==============================================================================
WattEnergy::WattEnergy(hid_t group)
{
// Read restriction energy
read_attribute(group, "u", u_);
// Read tabulated functions
hid_t dset = open_dataset(group, "a");
a_ = Tabulated1D{dset};
close_dataset(dset);
dset = open_dataset(group, "b");
b_ = Tabulated1D{dset};
close_dataset(dset);
}
double WattEnergy::sample(double E) const
{
// Determine Watt parameters at incident energy
double a = a_(E);
double b = b_(E);
while (true) {
// Sample energy-dependent Watt fission spectrum
double E_out = watt_spectrum_c(a, b);
// Accept energy based on restriction energy
if (E_out <= E - u_) return E_out;
}
}
}

152
src/distribution_energy.h Normal file
View file

@ -0,0 +1,152 @@
//! \file distribution_energy.h
//! Energy distributions that depend on incident particle energy
#ifndef OPENMC_DISTRIBUTION_ENERGY_H
#define OPENMC_DISTRIBUTION_ENERGY_H
#include <vector>
#include "xtensor/xtensor.hpp"
#include "hdf5.h"
#include "constants.h"
#include "endf.h"
namespace openmc {
//===============================================================================
//! Abstract class defining an energy distribution that is a function of the
//! incident energy of a projectile. Each derived type must implement a sample()
//! function that returns a sampled outgoing energy given an incoming energy
//===============================================================================
class EnergyDistribution {
public:
virtual double sample(double E) const = 0;
virtual ~EnergyDistribution() = default;
};
//===============================================================================
//! Discrete photon energy distribution
//===============================================================================
class DiscretePhoton : public EnergyDistribution {
public:
explicit DiscretePhoton(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
int primary_flag_; //!< Indicator of whether the photon is a primary or
//!< non-primary photon.
double energy_; //!< Photon energy or binding energy
double A_; //!< Atomic weight ratio of the target nuclide
};
//===============================================================================
//! Level inelastic scattering distribution
//===============================================================================
class LevelInelastic : public EnergyDistribution {
public:
explicit LevelInelastic(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
double threshold_; //!< Energy threshold in lab, (A + 1)/A * |Q|
double mass_ratio_; //!< (A/(A+1))^2
};
//===============================================================================
//! An energy distribution represented as a tabular distribution with histogram
//! or linear-linear interpolation. This corresponds to ACE law 4, which NJOY
//! produces for a number of ENDF energy distributions.
//===============================================================================
class ContinuousTabular : public EnergyDistribution {
public:
explicit ContinuousTabular(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
//! Outgoing energy for a single incoming energy
struct CTTable {
Interpolation interpolation; //!< Interpolation law
int n_discrete; //!< Number of of discrete energies
xt::xtensor<double, 1> e_out; //!< Outgoing energies in [eV]
xt::xtensor<double, 1> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution
};
int n_region_; //!< Number of inteprolation regions
std::vector<int> breakpoints_; //!< Breakpoints between regions
std::vector<Interpolation> interpolation_; //!< Interpolation laws
std::vector<double> energy_; //!< Incident energy in [eV]
std::vector<CTTable> distribution_; //!< Distributions for each incident energy
};
//===============================================================================
//! Evaporation spectrum corresponding to ACE law 9 and ENDF File 5, LF=9.
//===============================================================================
class Evaporation : public EnergyDistribution {
public:
explicit Evaporation(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
Tabulated1D theta_; //!< Incoming energy dependent parameter
double u_; //!< Restriction energy
};
//===============================================================================
//! Energy distribution of neutrons emitted from a Maxwell fission spectrum.
//! This corresponds to ACE law 7 and ENDF File 5, LF=7.
//===============================================================================
class MaxwellEnergy : public EnergyDistribution {
public:
explicit MaxwellEnergy(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
Tabulated1D theta_; //!< Incoming energy dependent parameter
double u_; //!< Restriction energy
};
//===============================================================================
//! Energy distribution of neutrons emitted from a Watt fission spectrum. This
//! corresponds to ACE law 11 and ENDF File 5, LF=11.
//===============================================================================
class WattEnergy : public EnergyDistribution {
public:
explicit WattEnergy(hid_t group);
//! Sample energy distribution
//! \param[in] E Incident particle energy in [eV]
//! \return Sampled energy in [eV]
double sample(double E) const;
private:
Tabulated1D a_; //!< Energy-dependent 'a' parameter
Tabulated1D b_; //!< Energy-dependent 'b' parameter
double u_; //!< Restriction energy
};
} // namespace openmc
#endif // OPENMC_DISTRIBUTION_ENERGY_H

View file

@ -0,0 +1,51 @@
#include "distribution_multi.h"
#include <algorithm> // for move
#include <cmath> // for sqrt, sin, cos, max
#include "constants.h"
#include "math_functions.h"
#include "random_lcg.h"
namespace openmc {
//==============================================================================
// PolarAzimuthal implementation
//==============================================================================
PolarAzimuthal::PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi) :
UnitSphereDistribution{u}, mu_{std::move(mu)}, phi_{std::move(phi)} { }
Direction PolarAzimuthal::sample() const
{
// Sample cosine of polar angle
double mu = mu_->sample();
if (mu == 1.0) return u_ref;
// Sample azimuthal angle
double phi = phi_->sample();
return rotate_angle(u_ref, mu, &phi);
}
//==============================================================================
// Isotropic implementation
//==============================================================================
Direction Isotropic::sample() const
{
double phi = 2.0*PI*prn();
double mu = 2.0*prn() - 1.0;
return {mu, std::sqrt(1.0 - mu*mu) * std::cos(phi),
std::sqrt(1.0 - mu*mu) * std::sin(phi)};
}
//==============================================================================
// Monodirectional implementation
//==============================================================================
Direction Monodirectional::sample() const
{
return u_ref;
}
} // namespace openmc

73
src/distribution_multi.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef DISTRIBUTION_MULTI_H
#define DISTRIBUTION_MULTI_H
#include <memory>
#include "distribution.h"
#include "position.h"
namespace openmc {
//==============================================================================
//! Probability density function for points on the unit sphere. Extensions of
//! this type are used to sample angular distributions for starting sources
//==============================================================================
class UnitSphereDistribution {
public:
UnitSphereDistribution() { };
explicit UnitSphereDistribution(Direction u) : u_ref{u} { };
virtual ~UnitSphereDistribution() = default;
//! Sample a direction from the distribution
//! \return Direction sampled
virtual Direction sample() const = 0;
Direction u_ref {0.0, 0.0, 1.0}; //!< reference direction
};
//==============================================================================
//! Explicit distribution of polar and azimuthal angles
//==============================================================================
class PolarAzimuthal : public UnitSphereDistribution {
public:
PolarAzimuthal(Direction u, UPtrDist mu, UPtrDist phi);
//! Sample a direction from the distribution
//! \return Direction sampled
Direction sample() const;
private:
UPtrDist mu_; //!< Distribution of polar angle
UPtrDist phi_; //!< Distribution of azimuthal angle
};
//==============================================================================
//! Uniform distribution on the unit sphere
//==============================================================================
class Isotropic : public UnitSphereDistribution {
public:
Isotropic() { };
//! Sample a direction from the distribution
//! \return Sampled direction
Direction sample() const;
};
//==============================================================================
//! Monodirectional distribution
//==============================================================================
class Monodirectional : public UnitSphereDistribution {
public:
Monodirectional(Direction u) : UnitSphereDistribution{u} { };
//! Sample a direction from the distribution
//! \return Sampled direction
Direction sample() const;
};
} // namespace openmc
#endif // DISTRIBUTION_MULTI_H

View file

@ -0,0 +1,97 @@
#include "distribution_spatial.h"
#include "error.h"
#include "random_lcg.h"
#include "xml_interface.h"
namespace openmc {
//==============================================================================
// CartesianIndependent implementation
//==============================================================================
CartesianIndependent::CartesianIndependent(pugi::xml_node node)
{
// Read distribution for x coordinate
if (check_for_node(node, "x")) {
pugi::xml_node node_dist = node.child("x");
x_ = distribution_from_xml(node_dist);
} else {
// If no distribution was specified, default to a single point at x=0
double x[] {0.0};
double p[] {1.0};
x_ = UPtrDist{new Discrete{x, p, 1}};
}
// Read distribution for y coordinate
if (check_for_node(node, "y")) {
pugi::xml_node node_dist = node.child("y");
y_ = distribution_from_xml(node_dist);
} else {
// If no distribution was specified, default to a single point at y=0
double x[] {0.0};
double p[] {1.0};
y_ = UPtrDist{new Discrete{x, p, 1}};
}
// Read distribution for z coordinate
if (check_for_node(node, "z")) {
pugi::xml_node node_dist = node.child("z");
z_ = distribution_from_xml(node_dist);
} else {
// If no distribution was specified, default to a single point at z=0
double x[] {0.0};
double p[] {1.0};
z_ = UPtrDist{new Discrete{x, p, 1}};
}
}
Position CartesianIndependent::sample() const
{
return {x_->sample(), y_->sample(), z_->sample()};
}
//==============================================================================
// SpatialBox implementation
//==============================================================================
SpatialBox::SpatialBox(pugi::xml_node node)
{
// Read lower-right/upper-left coordinates
auto params = get_node_array<double>(node, "parameters");
if (params.size() != 6)
openmc::fatal_error("Box/fission spatial source must have six "
"parameters specified.");
lower_left_ = Position{params[0], params[1], params[2]};
upper_right_ = Position{params[3], params[4], params[5]};
}
Position SpatialBox::sample() const
{
Position xi {prn(), prn(), prn()};
return lower_left_ + xi*(upper_right_ - lower_left_);
}
//==============================================================================
// SpatialPoint implementation
//==============================================================================
SpatialPoint::SpatialPoint(pugi::xml_node node)
{
// Read location of point source
auto params = get_node_array<double>(node, "parameters");
if (params.size() != 3)
openmc::fatal_error("Point spatial source must have three "
"parameters specified.");
// Set position
r_ = Position{params.data()};
}
Position SpatialPoint::sample() const
{
return r_;
}
} // namespace openmc

View file

@ -0,0 +1,74 @@
#ifndef OPENMC_DISTRIBTUION_SPATIAL_H
#define OPENMC_DISTRIBUTION_SPATIAL_H
#include "pugixml.hpp"
#include "distribution.h"
#include "position.h"
namespace openmc {
//==============================================================================
//! Probability density function for points in Euclidean space
//==============================================================================
class SpatialDistribution {
public:
virtual ~SpatialDistribution() = default;
//! Sample a position from the distribution
virtual Position sample() const = 0;
};
//==============================================================================
//! Distribution of points specified by independent distributions in x,y,z
//==============================================================================
class CartesianIndependent : public SpatialDistribution {
public:
explicit CartesianIndependent(pugi::xml_node node);
//! Sample a position from the distribution
//! \return Sampled position
Position sample() const;
private:
UPtrDist x_; //!< Distribution of x coordinates
UPtrDist y_; //!< Distribution of y coordinates
UPtrDist z_; //!< Distribution of z coordinates
};
//==============================================================================
//! Uniform distribution of points over a box
//==============================================================================
class SpatialBox : public SpatialDistribution {
public:
explicit SpatialBox(pugi::xml_node node);
//! Sample a position from the distribution
//! \return Sampled position
Position sample() const;
private:
Position lower_left_; //!< Lower-left coordinates of box
Position upper_right_; //!< Upper-right coordinates of box
bool only_fissionable {false}; //!< Only accept sites in fissionable region?
};
//==============================================================================
//! Distribution at a single point
//==============================================================================
class SpatialPoint : public SpatialDistribution {
public:
explicit SpatialPoint(pugi::xml_node node);
//! Sample a position from the distribution
//! \return Sampled position
Position sample() const;
private:
Position r_; //!< Single position at which sites are generated
};
} // namespace openmc
#endif // OPENMC_DISTRIBUTION_SPATIAL_H

147
src/endf.cpp Normal file
View file

@ -0,0 +1,147 @@
#include "endf.h"
#include <algorithm> // for copy
#include <cmath> // for log, exp
#include <iterator> // for back_inserter
#include "constants.h"
#include "hdf5_interface.h"
#include "search.h"
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
namespace openmc {
//==============================================================================
// Functions
//==============================================================================
Interpolation int2interp(int i)
{
switch (i) {
case 1:
return Interpolation::histogram;
case 2:
return Interpolation::lin_lin;
case 3:
return Interpolation::lin_log;
case 4:
return Interpolation::log_lin;
case 5:
return Interpolation::log_log;
}
}
bool is_fission(int mt)
{
return mt == 18 || mt == 19 || mt == 20 || mt == 21 || mt == 38;
}
//==============================================================================
// Polynomial implementation
//==============================================================================
Polynomial::Polynomial(hid_t dset)
{
// Read coefficients into a vector
read_dataset(dset, coef_);
}
double Polynomial::operator()(double x) const
{
// Use Horner's rule to evaluate polynomial. Note that coefficients are
// ordered in increasing powers of x.
double y = 0.0;
for (auto c = coef_.crbegin(); c != coef_.crend(); ++c) {
y = y*x + *c;
}
return y;
}
//==============================================================================
// Tabulated1D implementation
//==============================================================================
Tabulated1D::Tabulated1D(hid_t dset)
{
read_attribute(dset, "breakpoints", nbt_);
n_regions_ = nbt_.size();
// Change 1-indexing to 0-indexing
for (auto& b : nbt_) --b;
std::vector<int> int_temp;
read_attribute(dset, "interpolation", int_temp);
// Convert vector of ints into Interpolation
for (const auto i : int_temp)
int_.push_back(int2interp(i));
xt::xarray<double> arr;
read_dataset(dset, arr);
auto xs = xt::view(arr, 0);
auto ys = xt::view(arr, 1);
std::copy(xs.begin(), xs.end(), std::back_inserter(x_));
std::copy(ys.begin(), ys.end(), std::back_inserter(y_));
n_pairs_ = x_.size();
}
double Tabulated1D::operator()(double x) const
{
// find which bin the abscissa is in -- if the abscissa is outside the
// tabulated range, the first or last point is chosen, i.e. no interpolation
// is done outside the energy range
int i;
if (x < x_[0]) {
return y_[0];
} else if (x > x_[n_pairs_ - 1]) {
return y_[n_pairs_ - 1];
} else {
i = lower_bound_index(x_.begin(), x_.end(), x);
}
// determine interpolation scheme
Interpolation interp;
if (n_regions_ == 0) {
interp = Interpolation::lin_lin;
} else if (n_regions_ == 1) {
interp = int_[0];
} else if (n_regions_ > 1) {
for (int j = 0; j < n_regions_; ++j) {
if (i < nbt_[j]) {
interp = int_[j];
break;
}
}
}
// handle special case of histogram interpolation
if (interp == Interpolation::histogram) return y_[i];
// determine bounding values
double x0 = x_[i];
double x1 = x_[i + 1];
double y0 = y_[i];
double y1 = y_[i + 1];
// determine interpolation factor and interpolated value
double r;
switch (interp) {
case Interpolation::lin_lin:
r = (x - x0)/(x1 - x0);
return y0 + r*(y1 - y0);
case Interpolation::lin_log:
r = log(x/x0)/log(x1/x0);
return y0 + r*(y1 - y0);
case Interpolation::log_lin:
r = (x - x0)/(x1 - x0);
return y0*exp(r*log(y1/y0));
case Interpolation::log_log:
r = log(x/x0)/log(x1/x0);
return y0*exp(r*log(y1/y0));
}
}
} // namespace openmc

78
src/endf.h Normal file
View file

@ -0,0 +1,78 @@
//! \file endf.h
//! Classes and functions related to the ENDF-6 format
#ifndef OPENMC_ENDF_H
#define OPENMC_ENDF_H
#include <vector>
#include "constants.h"
#include "hdf5.h"
namespace openmc {
//! Convert integer representing interpolation law to enum
//! \param[in] i Intereger (e.g. 1=histogram, 2=lin-lin)
//! \return Corresponding enum value
Interpolation int2interp(int i);
//! Determine whether MT number corresponds to a fission reaction
//! \param[in] MT ENDF MT value
//! \return Whether corresponding reaction is a fission reaction
bool is_fission(int MT);
//==============================================================================
//! Abstract one-dimensional function
//==============================================================================
class Function1D {
public:
virtual double operator()(double x) const = 0;
};
//==============================================================================
//! One-dimensional function expressed as a polynomial
//==============================================================================
class Polynomial : public Function1D {
public:
//! Construct polynomial from HDF5 data
//! \param[in] dset Dataset containing coefficients
explicit Polynomial(hid_t dset);
//! Evaluate the polynomials
//! \param[in] x independent variable
//! \return Polynomial evaluated at x
double operator()(double x) const;
private:
std::vector<double> coef_; //!< Polynomial coefficients
};
//==============================================================================
//! One-dimensional interpolable function
//==============================================================================
class Tabulated1D : public Function1D {
public:
Tabulated1D() = default;
//! Construct function from HDF5 data
//! \param[in] dset Dataset containing tabulated data
explicit Tabulated1D(hid_t dset);
//! Evaluate the tabulated function
//! \param[in] x independent variable
//! \return Function evaluated at x
double operator()(double x) const;
private:
std::size_t n_regions_ {0}; //!< number of interpolation regions
std::vector<int> nbt_; //!< values separating interpolation regions
std::vector<Interpolation> int_; //!< interpolation schemes
std::size_t n_pairs_; //!< number of (x,y) pairs
std::vector<double> x_; //!< values of abscissa
std::vector<double> y_; //!< values of ordinate
};
} // namespace openmc
#endif // OPENMC_ENDF_H

View file

@ -1,583 +0,0 @@
module energy_distribution
use algorithm, only: binary_search
use constants, only: ZERO, ONE, HALF, TWO, PI, HISTOGRAM, LINEAR_LINEAR
use endf_header, only: Tabulated1D
use hdf5_interface
use math, only: maxwell_spectrum, watt_spectrum
use random_lcg, only: prn
!===============================================================================
! ENERGYDISTRIBUTION (abstract) defines an energy distribution that is a
! function of the incident energy of a projectile. Each derived type must
! implement a sample() function that returns a sampled outgoing energy given an
! incoming energy
!===============================================================================
type, abstract :: EnergyDistribution
contains
procedure(energy_distribution_sample_), deferred :: sample
procedure(energy_distribution_from_hdf5_), deferred :: from_hdf5
end type EnergyDistribution
abstract interface
function energy_distribution_sample_(this, E_in) result(E_out)
import EnergyDistribution
class(EnergyDistribution), intent(in) :: this
real(8), intent(in) :: E_in
real(8) :: E_out
end function energy_distribution_sample_
subroutine energy_distribution_from_hdf5_(this, group_id)
import EnergyDistribution
import HID_T
class(EnergyDistribution), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
end subroutine energy_distribution_from_hdf5_
end interface
type :: EnergyDistributionContainer
class(EnergyDistribution), allocatable :: obj
end type EnergyDistributionContainer
!===============================================================================
! Derived classes
!===============================================================================
!===============================================================================
! TABULAREQUIPROBABLE represents an energy distribution with tabular
! equiprobable energy bins as given in ACE law 1. This is an older
! representation that has largely been replaced with ACE laws 4, 44, and 61.
!===============================================================================
type, extends(EnergyDistribution) :: TabularEquiprobable
integer :: n_region ! number of interpolation regions
integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions
integer, allocatable :: interpolation(:) ! interpolation region codes
real(8), allocatable :: energy_in(:) ! incoming energies
real(8), allocatable :: energy_out(:,:) ! table of outgoing energies for
! each incoming energy
contains
procedure :: sample => equiprobable_sample
procedure :: from_hdf5 => equiprobable_from_hdf5
end type TabularEquiprobable
!===============================================================================
! DISCRETEPHOTON gives the energy distribution for a discrete photon (usually
! used for photon production from an incident-neutron reaction)
!===============================================================================
type, extends(EnergyDistribution) :: DiscretePhoton
integer :: primary_flag
real(8) :: energy
real(8) :: A
contains
procedure :: sample => discrete_photon_sample
procedure :: from_hdf5 => discrete_photon_from_hdf5
end type DiscretePhoton
!===============================================================================
! LEVELINELASTIC gives the energy distribution for level inelastic scattering by
! neutrons as in ENDF MT=51--90.
!===============================================================================
type, extends(EnergyDistribution) :: LevelInelastic
real(8) :: threshold
real(8) :: mass_ratio
contains
procedure :: sample => level_inelastic_sample
procedure :: from_hdf5 => level_inelastic_from_hdf5
end type LevelInelastic
!===============================================================================
! CONTINUOUSTABULAR gives an energy distribution represented as a tabular
! distribution with histogram or linear-linear interpolation. This corresponds
! to ACE law 4, which NJOY produces for a number of ENDF energy distributions.
!===============================================================================
type CTTable
integer :: interpolation
integer :: n_discrete
real(8), allocatable :: e_out(:)
real(8), allocatable :: p(:)
real(8), allocatable :: c(:)
end type CTTable
type, extends(EnergyDistribution) :: ContinuousTabular
integer :: n_region
integer, allocatable :: breakpoints(:)
integer, allocatable :: interpolation(:)
real(8), allocatable :: energy(:)
type(CTTable), allocatable :: distribution(:)
contains
procedure :: sample => continuous_sample
procedure :: from_hdf5 => continuous_from_hdf5
end type ContinuousTabular
!===============================================================================
! MAXWELLENERGY gives the energy distribution of neutrons emitted from a Maxwell
! fission spectrum. This corresponds to ACE law 7 and ENDF File 5, LF=7.
!===============================================================================
type, extends(EnergyDistribution) :: MaxwellEnergy
type(Tabulated1D) :: theta ! incoming-energy-dependent parameter
real(8) :: u ! restriction energy
contains
procedure :: sample => maxwellenergy_sample
procedure :: from_hdf5 => maxwellenergy_from_hdf5
end type MaxwellEnergy
!===============================================================================
! EVAPORATION represents an evaporation spectrum corresponding to ACE law 9 and
! ENDF File 5, LF=9.
!===============================================================================
type, extends(EnergyDistribution) :: Evaporation
type(Tabulated1D) :: theta
real(8) :: u
contains
procedure :: sample => evaporation_sample
procedure :: from_hdf5 => evaporation_from_hdf5
end type Evaporation
!===============================================================================
! WATTENERGY gives the energy distribution of neutrons emitted from a Watt
! fission spectrum. This corresponds to ACE law 11 and ENDF File 5, LF=11.
!===============================================================================
type, extends(EnergyDistribution) :: WattEnergy
type(Tabulated1D) :: a
type(Tabulated1D) :: b
real(8) :: u
contains
procedure :: sample => watt_sample
procedure :: from_hdf5 => watt_from_hdf5
end type WattEnergy
contains
function equiprobable_sample(this, E_in) result(E_out)
class(TabularEquiprobable), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
integer :: i, k, l ! indices
integer :: n_energy_in ! number of incoming energies
integer :: n_energy_out ! number of outgoing energies
real(8) :: r ! interpolation factor on incoming energy
real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i
real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1
real(8) :: E_1, E_K ! endpoints interpolated between i and i+1
real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l
! Determine number of incoming/outgoing energies
n_energy_in = size(this%energy_in)
n_energy_out = size(this%energy_out, 1)
! Determine index on incoming energy grid and interpolation factor
i = binary_search(this%energy_in, size(this%energy_in), E_in)
r = (E_in - this%energy_in(i)) / &
(this%energy_in(i+1) - this%energy_in(i))
! Sample outgoing energy bin
k = 1 + int(n_energy_out * prn())
! Determine E_1 and E_K
E_i_1 = this%energy_out(1, i)
E_i_K = this%energy_out(n_energy_out, i)
E_i1_1 = this%energy_out(1, i+1)
E_i1_K = this%energy_out(n_energy_out, i+1)
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
E_K = E_i_K + r*(E_i1_K - E_i_K)
! Randomly select between the outgoing table for incoming energy E_i and
! E_(i+1)
if (prn() < r) then
l = i + 1
else
l = i
end if
! Determine E_l_k and E_l_k+1
E_l_k = this%energy_out(k, l)
E_l_k1 = this%energy_out(k+1, l)
! Determine E' (denoted here as E_out)
E_out = E_l_k + prn()*(E_l_k1 - E_l_k)
! Now interpolate between incident energy bins i and i + 1
if (l == i) then
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1)
else
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1)
end if
end function equiprobable_sample
subroutine equiprobable_from_hdf5(this, group_id)
class(TabularEquiprobable), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
end subroutine equiprobable_from_hdf5
function discrete_photon_sample(this, E_in) result(E_out)
class(DiscretePhoton), intent(in) :: this
real(8), intent(in) :: E_in
real(8) :: E_out
if (this % primary_flag == 2) then
E_out = this % energy + this % A/(this % A + 1)*E_in
else
E_out = this % energy
end if
end function discrete_photon_sample
subroutine discrete_photon_from_hdf5(this, group_id)
class(DiscretePhoton), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
call read_attribute(this % primary_flag, group_id, 'primary_flag')
call read_attribute(this % energy, group_id, 'energy')
call read_attribute(this % A, group_id, 'atomic_weight_ratio')
end subroutine discrete_photon_from_hdf5
function level_inelastic_sample(this, E_in) result(E_out)
class(LevelInelastic), intent(in) :: this
real(8), intent(in) :: E_in
real(8) :: E_out
E_out = this%mass_ratio*(E_in - this%threshold)
end function level_inelastic_sample
subroutine level_inelastic_from_hdf5(this, group_id)
class(LevelInelastic), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
call read_attribute(this%threshold, group_id, 'threshold')
call read_attribute(this%mass_ratio, group_id, 'mass_ratio')
end subroutine level_inelastic_from_hdf5
function continuous_sample(this, E_in) result(E_out)
class(ContinuousTabular), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
integer :: i, k, l ! indices
integer :: n_energy_in ! number of incoming energies
integer :: n_energy_out ! number of outgoing energies
real(8) :: r ! interpolation factor on incoming energy
real(8) :: r1 ! random number on [0,1)
real(8) :: frac ! interpolation factor on outgoing energy
real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i
real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1
real(8) :: E_1, E_K ! endpoints interpolated between i and i+1
real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l
real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l
real(8) :: c_k, c_k1 ! cumulative probability
logical :: histogram_interp ! whether histogram interpolation is used
! Read number of interpolation regions and incoming energies
if (this%n_region == 1) then
histogram_interp = (this%interpolation(1) == 1)
else
histogram_interp = .false.
end if
! Find energy bin and calculate interpolation factor -- if the energy is
! outside the range of the tabulated energies, choose the first or last bins
n_energy_in = size(this%energy)
if (E_in < this%energy(1)) then
i = 1
r = ZERO
elseif (E_in > this%energy(n_energy_in)) then
i = n_energy_in - 1
r = ONE
else
i = binary_search(this%energy, n_energy_in, E_in)
r = (E_in - this%energy(i)) / &
(this%energy(i+1) - this%energy(i))
end if
! Sample between the ith and (i+1)th bin
if (histogram_interp) then
l = i
else
if (r > prn()) then
l = i + 1
else
l = i
end if
end if
! Interpolation for energy E1 and EK
n_energy_out = size(this%distribution(i)%e_out)
E_i_1 = this%distribution(i)%e_out(1)
E_i_K = this%distribution(i)%e_out(n_energy_out)
n_energy_out = size(this%distribution(i+1)%e_out)
E_i1_1 = this%distribution(i+1)%e_out(1)
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
E_K = E_i_K + r*(E_i1_K - E_i_K)
! Determine outgoing energy bin
n_energy_out = size(this%distribution(l)%e_out)
r1 = prn()
c_k = this%distribution(l)%c(1)
do k = 1, n_energy_out - 1
c_k1 = this%distribution(l)%c(k+1)
if (r1 < c_k1) exit
c_k = c_k1
end do
! Check to make sure 1 <= k <= NP - 1
k = max(1, min(k, n_energy_out - 1))
E_l_k = this%distribution(l)%e_out(k)
p_l_k = this%distribution(l)%p(k)
if (this%distribution(l)%interpolation == HISTOGRAM) then
! Histogram interpolation
if (p_l_k > ZERO) then
E_out = E_l_k + (r1 - c_k)/p_l_k
else
E_out = E_l_k
end if
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
! Linear-linear interpolation
E_l_k1 = this%distribution(l)%e_out(k+1)
p_l_k1 = this%distribution(l)%p(k+1)
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
if (frac == ZERO) then
E_out = E_l_k + (r1 - c_k)/p_l_k
else
E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + &
TWO*frac*(r1 - c_k))) - p_l_k)/frac
end if
end if
! Now interpolate between incident energy bins i and i + 1
if (.not. histogram_interp .and. n_energy_out > 1) then
if (l == i) then
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1)
else
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1)
end if
end if
end function continuous_sample
subroutine continuous_from_hdf5(this, group_id)
class(ContinuousTabular), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer :: i, j, k
integer :: n
integer :: n_energy
integer(HID_T) :: dset_id
integer(HSIZE_T) :: dims(1), dims2(2)
integer, allocatable :: temp(:,:)
integer, allocatable :: offsets(:)
integer, allocatable :: interp(:)
integer, allocatable :: n_discrete(:)
real(8), allocatable :: eout(:,:)
! Open incoming energy dataset
dset_id = open_dataset(group_id, 'energy')
! Get interpolation parameters
call read_attribute(temp, dset_id, 'interpolation')
allocate(this%breakpoints(size(temp, 1)))
allocate(this%interpolation(size(temp, 1)))
this%breakpoints(:) = temp(:, 1)
this%interpolation(:) = temp(:, 2)
this%n_region = size(this%breakpoints)
! Get incoming energies
call get_shape(dset_id, dims)
n_energy = int(dims(1), 4)
allocate(this%energy(n_energy))
allocate(this%distribution(n_energy))
call read_dataset(this%energy, dset_id)
call close_dataset(dset_id)
! Get outgoing energy distribution data
dset_id = open_dataset(group_id, 'distribution')
call read_attribute(offsets, dset_id, 'offsets')
call read_attribute(interp, dset_id, 'interpolation')
call read_attribute(n_discrete, dset_id, 'n_discrete_lines')
call get_shape(dset_id, dims2)
allocate(eout(dims2(1), dims2(2)))
call read_dataset(eout, dset_id)
call close_dataset(dset_id)
do i = 1, n_energy
! Determine number of outgoing energies
j = offsets(i)
if (i < n_energy) then
n = offsets(i+1) - j
else
n = size(eout, 1) - j
end if
associate (d => this % distribution(i))
! Assign interpolation scheme and number of discrete lines
d % interpolation = interp(i)
d % n_discrete = n_discrete(i)
! Allocate arrays for energies and PDF/CDF
allocate(d % e_out(n))
allocate(d % p(n))
allocate(d % c(n))
! Copy data
d % e_out(:) = eout(j+1:j+n, 1)
d % p(:) = eout(j+1:j+n, 2)
! To get answers that match ACE data, for now we still use the tabulated
! CDF values that were passed through to the HDF5 library. At a later
! time, we can remove the CDF values from the HDF5 library and
! reconstruct them using the PDF
if (.true.) then
d % c(:) = eout(j+1:j+n, 3)
else
! Calculate cumulative distribution function -- discrete portion
do k = 1, n_discrete(i)
if (k == 1) then
d % c(k) = d % p(k)
else
d % c(k) = d % c(k-1) + d % p(k)
end if
end do
! Continuous portion
do k = d % n_discrete + 1, n
if (k == d % n_discrete + 1) then
d % c(k) = sum(d % p(1:d % n_discrete))
else
if (d % interpolation == HISTOGRAM) then
d % c(k) = d % c(k-1) + d % p(k-1) * &
(d % e_out(k) - d % e_out(k-1))
elseif (d % interpolation == LINEAR_LINEAR) then
d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * &
(d % e_out(k) - d % e_out(k-1))
end if
end if
end do
! Normalize density and distribution functions
d % p(:) = d % p(:)/d % c(n)
d % c(:) = d % c(:)/d % c(n)
end if
end associate
end do
end subroutine continuous_from_hdf5
function maxwellenergy_sample(this, E_in) result(E_out)
class(MaxwellEnergy), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
real(8) :: theta ! Maxwell distribution parameter
! Get temperature corresponding to incoming energy
theta = this % theta % evaluate(E_in)
do
! Sample maxwell fission spectrum
E_out = maxwell_spectrum(theta)
! Accept energy based on restriction energy
if (E_out <= E_in - this%u) exit
end do
end function maxwellenergy_sample
subroutine maxwellenergy_from_hdf5(this, group_id)
class(MaxwellEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer(HID_T) :: dset_id
call read_attribute(this%u, group_id, 'u')
dset_id = open_dataset(group_id, 'theta')
call this%theta%from_hdf5(dset_id)
call close_dataset(dset_id)
end subroutine maxwellenergy_from_hdf5
function evaporation_sample(this, E_in) result(E_out)
class(Evaporation), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
real(8) :: theta ! evaporation spectrum parameter
real(8) :: x, y, v
! Get temperature corresponding to incoming energy
theta = this % theta % evaluate(E_in)
y = (E_in - this%u)/theta
v = 1 - exp(-y)
! Sample outgoing energy based on evaporation spectrum probability
! density function
do
x = -log((ONE - v*prn())*(ONE - v*prn()))
if (x <= y) exit
end do
E_out = x*theta
end function evaporation_sample
subroutine evaporation_from_hdf5(this, group_id)
class(Evaporation), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer(HID_T) :: dset_id
call read_attribute(this%u, group_id, 'u')
dset_id = open_dataset(group_id, 'theta')
call this%theta%from_hdf5(dset_id)
call close_dataset(dset_id)
end subroutine evaporation_from_hdf5
function watt_sample(this, E_in) result(E_out)
class(WattEnergy), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8) :: E_out ! sampled outgoing energy
real(8) :: a, b ! Watt spectrum parameters
! Determine Watt parameter 'a' from tabulated function
a = this % a % evaluate(E_in)
! Determine Watt parameter 'b' from tabulated function
b = this % b % evaluate(E_in)
do
! Sample energy-dependent Watt fission spectrum
E_out = watt_spectrum(a, b)
! Accept energy based on restriction energy
if (E_out <= E_in - this%u) exit
end do
end function watt_sample
subroutine watt_from_hdf5(this, group_id)
class(WattEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer(HID_T) :: dset_id
call read_attribute(this%u, group_id, 'u')
dset_id = open_dataset(group_id, 'a')
call this%a%from_hdf5(dset_id)
call close_dataset(dset_id)
dset_id = open_dataset(group_id, 'b')
call this%b%from_hdf5(dset_id)
call close_dataset(dset_id)
end subroutine watt_from_hdf5
end module energy_distribution

View file

@ -94,7 +94,7 @@ contains
p % last_sqrtkT = p % sqrtkT
! Get distributed offset
if (size(c % material) > 1 .or. size(c % sqrtkT) > 1) then
if (c % material_size() > 1 .or. size(c % sqrtkT) > 1) then
! Distributed instances of this cell have different
! materials/temperatures. Determine which instance this is for
! assigning the matching material/temperature.
@ -126,7 +126,7 @@ contains
end if
! Save the material
if (size(c % material) > 1) then
if (c % material_size() > 1) then
p % material = c % material(offset + 1)
else
p % material = c % material(1)

View file

@ -7,7 +7,9 @@
#include "cell.h"
#include "constants.h"
#include "error.h"
#include "geometry.h"
#include "lattice.h"
#include "material.h"
namespace openmc {
@ -15,11 +17,11 @@ namespace openmc {
//==============================================================================
void
adjust_indices_c()
adjust_indices()
{
// Adjust material/fill idices.
for (Cell *c : global_cells) {
if (c->material[0] == C_NONE) {
for (Cell* c : global_cells) {
if (c->fill != C_NONE) {
int32_t id = c->fill;
auto search_univ = universe_map.find(id);
auto search_lat = lattice_map.find(id);
@ -36,13 +38,26 @@ adjust_indices_c()
fatal_error(err_msg);
}
} else {
//TODO: materials
c->type = FILL_MATERIAL;
for (auto it = c->material.begin(); it != c->material.end(); it++) {
int32_t mid = *it;
if (mid != MATERIAL_VOID) {
auto search = material_map.find(mid);
if (search != material_map.end()) {
*it = search->second;
} else {
std::stringstream err_msg;
err_msg << "Could not find material " << mid
<< " specified on cell " << c->id;
fatal_error(err_msg);
}
}
}
}
}
// Change cell.universe values from IDs to indices.
for (Cell *c : global_cells) {
for (Cell* c : global_cells) {
auto search = universe_map.find(c->universe);
if (search != universe_map.end()) {
//TODO: Remove this off-by-one indexing.
@ -56,7 +71,7 @@ adjust_indices_c()
}
// Change all lattice universe values from IDs to indices.
for (Lattice *l : lattices_c) {
for (Lattice* l : lattices_c) {
l->adjust_indices();
}
}
@ -68,12 +83,12 @@ find_root_universe()
{
// Find all the universes listed as a cell fill.
std::unordered_set<int32_t> fill_univ_ids;
for (Cell *c : global_cells) {
for (Cell* c : global_cells) {
fill_univ_ids.insert(c->fill);
}
// Find all the universes contained in a lattice.
for (Lattice *lat : lattices_c) {
for (Lattice* lat : lattices_c) {
for (auto it = lat->begin(); it != lat->end(); ++it) {
fill_univ_ids.insert(*it);
}
@ -109,13 +124,13 @@ find_root_universe()
void
allocate_offset_tables(int n_maps)
{
for (Cell *c : global_cells) {
for (Cell* c : global_cells) {
if (c->type != FILL_MATERIAL) {
c->offset.resize(n_maps, C_NONE);
}
}
for (Lattice *lat : lattices_c) {
for (Lattice* lat : lattices_c) {
lat->allocate_offset_table(n_maps);
}
}
@ -126,7 +141,7 @@ void
count_cell_instances(int32_t univ_indx)
{
for (int32_t cell_indx : global_universes[univ_indx]->cells) {
Cell &c = *global_cells[cell_indx];
Cell& c = *global_cells[cell_indx];
++c.n_instances;
if (c.type == FILL_UNIVERSE) {
@ -135,7 +150,7 @@ count_cell_instances(int32_t univ_indx)
} else if (c.type == FILL_LATTICE) {
// This cell contains a lattice. Recurse into the lattice universes.
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
for (auto it = lat.begin(); it != lat.end(); ++it) {
count_cell_instances(*it);
}
@ -155,14 +170,14 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
int count {0};
for (int32_t cell_indx : global_universes[search_univ]->cells) {
Cell &c = *global_cells[cell_indx];
Cell& c = *global_cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
int32_t next_univ = c.fill;
count += count_universe_instances(next_univ, target_univ_id);
} else if (c.type == FILL_LATTICE) {
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
for (auto it = lat.begin(); it != lat.end(); ++it) {
int32_t next_univ = *it;
count += count_universe_instances(next_univ, target_univ_id);
@ -178,10 +193,10 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
void
fill_offset_tables(int32_t target_univ_id, int map)
{
for (Universe *univ : global_universes) {
for (Universe* univ : global_universes) {
int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation.
for (int32_t cell_indx : univ->cells) {
Cell &c = *global_cells[cell_indx];
Cell& c = *global_cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
c.offset[map] = offset;
@ -189,7 +204,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
offset += count_universe_instances(search_univ, target_univ_id);
} else if (c.type == FILL_LATTICE) {
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
offset = lat.fill_offset_table(offset, target_univ_id, map);
}
}
@ -200,7 +215,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
std::string
distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
const Universe &search_univ, int32_t offset)
const Universe& search_univ, int32_t offset)
{
std::stringstream path;
@ -210,7 +225,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
// write to the path and return.
for (int32_t cell_indx : search_univ.cells) {
if ((cell_indx == target_cell) && (offset == target_offset)) {
Cell &c = *global_cells[cell_indx];
Cell& c = *global_cells[cell_indx];
path << "c" << c.id;
return path.str();
}
@ -222,7 +237,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
std::vector<std::int32_t>::const_reverse_iterator cell_it
{search_univ.cells.crbegin()};
for (; cell_it != search_univ.cells.crend(); ++cell_it) {
Cell &c = *global_cells[*cell_it];
Cell& c = *global_cells[*cell_it];
// Material cells don't contain other cells so ignore them.
if (c.type != FILL_MATERIAL) {
@ -230,7 +245,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
if (c.type == FILL_UNIVERSE) {
temp_offset = offset + c.offset[map];
} else {
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
int32_t indx = lat.universes.size()*map + lat.begin().indx;
temp_offset = offset + lat.offsets[indx];
}
@ -242,7 +257,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
}
// Add the cell to the path string.
Cell &c = *global_cells[*cell_it];
Cell& c = *global_cells[*cell_it];
path << "c" << c.id << "->";
if (c.type == FILL_UNIVERSE) {
@ -253,7 +268,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
return path.str();
} else {
// Recurse into the lattice cell.
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
path << "l" << lat.id;
for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
int32_t indx = lat.universes.size()*map + it.indx;
@ -275,7 +290,7 @@ int
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
int32_t root_univ)
{
Universe &root = *global_universes[root_univ];
Universe& root = *global_universes[root_univ];
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
root, 0)};
return path_.size() + 1;
@ -285,9 +300,9 @@ distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
void
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
int32_t root_univ, char *path)
int32_t root_univ, char* path)
{
Universe &root = *global_universes[root_univ];
Universe& root = *global_universes[root_univ];
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
root, 0)};
path_.copy(path, path_.size());
@ -302,12 +317,12 @@ maximum_levels(int32_t univ)
int levels_below {0};
for (int32_t cell_indx : global_universes[univ]->cells) {
Cell &c = *global_cells[cell_indx];
Cell& c = *global_cells[cell_indx];
if (c.type == FILL_UNIVERSE) {
int32_t next_univ = c.fill;
levels_below = std::max(levels_below, maximum_levels(next_univ));
} else if (c.type == FILL_LATTICE) {
Lattice &lat = *lattices_c[c.fill];
Lattice& lat = *lattices_c[c.fill];
for (auto it = lat.begin(); it != lat.end(); ++it) {
int32_t next_univ = *it;
levels_below = std::max(levels_below, maximum_levels(next_univ));
@ -324,18 +339,20 @@ maximum_levels(int32_t univ)
void
free_memory_geometry_c()
{
for (Cell *c : global_cells) {delete c;}
for (Cell* c : global_cells) {delete c;}
global_cells.clear();
cell_map.clear();
n_cells = 0;
for (Universe *u : global_universes) {delete u;}
for (Universe* u : global_universes) {delete u;}
global_universes.clear();
universe_map.clear();
for (Lattice *lat : lattices_c) {delete lat;}
for (Lattice* lat : lattices_c) {delete lat;}
lattices_c.clear();
lattice_map.clear();
overlap_check_count.clear();
}
} // namespace openmc

View file

@ -13,7 +13,7 @@ namespace openmc {
//! Replace Universe, Lattice, and Material IDs with indices.
//==============================================================================
extern "C" void adjust_indices_c();
extern "C" void adjust_indices();
//==============================================================================
//! Figure out which Universe is the root universe.

View file

@ -4,7 +4,7 @@ module geometry_header
use algorithm, only: find
use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, &
MATERIAL_VOID, NONE
MATERIAL_VOID
use dict_header, only: DictCharInt, DictIntInt
use hdf5_interface, only: HID_T
use material_header, only: Material, materials, material_dict, n_materials
@ -16,11 +16,11 @@ module geometry_header
implicit none
interface
function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr)
function cell_pointer(cell_ind) bind(C) result(ptr)
import C_PTR, C_INT32_T
integer(C_INT32_T), intent(in), value :: cell_ind
type(C_PTR) :: ptr
end function cell_pointer_c
end function cell_pointer
function cell_id_c(cell_ptr) bind(C, name='cell_id') result(id)
import C_PTR, C_INT32_T
@ -40,12 +40,6 @@ module geometry_header
integer(C_INT) :: type
end function cell_type_c
subroutine cell_set_type_c(cell_ptr, type) bind(C, name='cell_set_type')
import C_PTR, C_INT
type(C_PTR), intent(in), value :: cell_ptr
integer(C_INT), intent(in), value :: type
end subroutine cell_set_type_c
function cell_universe_c(cell_ptr) bind(C, name='cell_universe') &
result(universe)
import C_PTR, C_INT32_T
@ -53,25 +47,12 @@ module geometry_header
integer(C_INT32_T) :: universe
end function cell_universe_c
subroutine cell_set_universe_c(cell_ptr, universe) &
bind(C, name='cell_set_universe')
import C_PTR, C_INT32_T
type(C_PTR), intent(in), value :: cell_ptr
integer(C_INT32_T), intent(in), value :: universe
end subroutine cell_set_universe_c
function cell_fill_c(cell_ptr) bind(C, name="cell_fill") result(fill)
import C_PTR, C_INT32_T
type(C_PTR), intent(in), value :: cell_ptr
integer(C_INT32_T) :: fill
end function cell_fill_c
function cell_fill_ptr(cell_ptr) bind(C) result(fill_ptr)
import C_PTR
type(C_PTR), intent(in), value :: cell_ptr
type(C_PTR) :: fill_ptr
end function cell_fill_ptr
function cell_n_instances_c(cell_ptr) bind(C, name='cell_n_instances') &
result(n_instances)
import C_PTR, C_INT32_T
@ -79,6 +60,21 @@ module geometry_header
integer(C_INT32_T) :: n_instances
end function cell_n_instances_c
function cell_material_size_c(cell_ptr) bind(C, name='cell_material_size') &
result(n)
import C_PTR, C_INT
type(C_PTR), intent(in), value :: cell_ptr
integer(C_INT) :: n
end function cell_material_size_c
function cell_material_c(cell_ptr, i) bind(C, name='cell_material') &
result(mat)
import C_PTR, C_INT, C_INT32_T
type(C_PTR), intent(in), value :: cell_ptr
integer(C_INT), intent(in), value :: i
integer(C_INT32_T) :: mat
end function cell_material_c
function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple)
import C_PTR, C_BOOL
type(C_PTR), intent(in), value :: cell_ptr
@ -110,12 +106,11 @@ module geometry_header
integer(HID_T), intent(in), value :: group
end subroutine cell_to_hdf5_c
function lattice_pointer_c(lat_ind) bind(C, name='lattice_pointer') &
result(ptr)
function lattice_pointer(lat_ind) bind(C) result(ptr)
import C_PTR, C_INT32_T
integer(C_INT32_T), intent(in), value :: lat_ind
type(C_PTR) :: ptr
end function lattice_pointer_c
end function lattice_pointer
function lattice_id_c(lat_ptr) bind(C, name='lattice_id') result(id)
import C_PTR, C_INT32_T
@ -254,9 +249,6 @@ module geometry_header
type Cell
type(C_PTR) :: ptr
integer, allocatable :: material(:) ! Material within cell. Multiple
! materials for distribcell
! instances. 0 signifies a universe
integer, allocatable :: region(:) ! Definition of spatial region as
! Boolean expression of half-spaces
integer :: distribcell_index ! Index corresponding to this cell in
@ -275,11 +267,11 @@ module geometry_header
procedure :: id => cell_id
procedure :: set_id => cell_set_id
procedure :: type => cell_type
procedure :: set_type => cell_set_type
procedure :: universe => cell_universe
procedure :: set_universe => cell_set_universe
procedure :: fill => cell_fill
procedure :: n_instances => cell_n_instances
procedure :: material_size => cell_material_size
procedure :: material => cell_material
procedure :: simple => cell_simple
procedure :: distance => cell_distance
procedure :: offset => cell_offset
@ -390,24 +382,12 @@ contains
type = cell_type_c(this % ptr)
end function cell_type
subroutine cell_set_type(this, type)
class(Cell), intent(in) :: this
integer(C_INT), intent(in) :: type
call cell_set_type_c(this % ptr, type)
end subroutine cell_set_type
function cell_universe(this) result(universe)
class(Cell), intent(in) :: this
integer(C_INT32_T) :: universe
universe = cell_universe_c(this % ptr)
end function cell_universe
subroutine cell_set_universe(this, universe)
class(Cell), intent(in) :: this
integer(C_INT32_T), intent(in) :: universe
call cell_set_universe_c(this % ptr, universe)
end subroutine cell_set_universe
function cell_fill(this) result(fill)
class(Cell), intent(in) :: this
integer(C_INT32_T) :: fill
@ -420,6 +400,19 @@ contains
n_instances = cell_n_instances_c(this % ptr)
end function cell_n_instances
function cell_material_size(this) result(n)
class(Cell), intent(in) :: this
integer(C_INT) :: n
n = cell_material_size_c(this % ptr)
end function cell_material_size
function cell_material(this, i) result(mat)
class(Cell), intent(in) :: this
integer, intent(in) :: i
integer(C_INT32_T) :: mat
mat = cell_material_c(this % ptr, i)
end function cell_material
function cell_simple(this) result(simple)
class(Cell), intent(in) :: this
logical(C_BOOL) :: simple
@ -469,10 +462,12 @@ contains
if (present(sab_temps)) allocate(sab_temps(n_sab_tables))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
! Skip any non-material cells and void materials
if (cells(i) % material(j) == NONE .or. &
cells(i) % material(j) == MATERIAL_VOID) cycle
! Skip non-material cells.
if (cells(i) % fill() /= C_NONE) cycle
do j = 1, cells(i) % material_size()
! Skip void materials
if (cells(i) % material(j) == MATERIAL_VOID) cycle
! Get temperature of cell (rounding to nearest integer)
if (size(cells(i) % sqrtkT) > 1) then
@ -571,7 +566,7 @@ contains
! Extend the C++ cells array and get pointers to the C++ objects
call extend_cells_c(n)
do i = n_cells - n, n_cells
cells(i) % ptr = cell_pointer_c(i - 1)
cells(i) % ptr = cell_pointer(i - 1)
end do
err = 0
@ -599,33 +594,6 @@ contains
end function openmc_get_cell_index
function openmc_cell_get_fill(index, type, indices, n) result(err) bind(C)
integer(C_INT32_T), value, intent(in) :: index
integer(C_INT), intent(out) :: type
integer(C_INT32_T), intent(out) :: n
type(C_PTR), intent(out) :: indices
integer(C_INT) :: err
err = 0
if (index >= 1 .and. index <= size(cells)) then
associate (c => cells(index))
type = c % type()
select case (type)
case (FILL_MATERIAL)
n = size(c % material)
indices = C_LOC(c % material(1))
case (FILL_UNIVERSE, FILL_LATTICE)
n = 1
indices = cell_fill_ptr(c % ptr)
end select
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_get_fill
function openmc_cell_get_id(index, id) result(err) bind(C)
! Return the ID of a cell
integer(C_INT32_T), value :: index
@ -642,49 +610,6 @@ contains
end function openmc_cell_get_id
function openmc_cell_set_fill(index, type, n, indices) result(err) bind(C)
! Set the fill for a cell
integer(C_INT32_T), value, intent(in) :: index ! index in cells
integer(C_INT), value, intent(in) :: type
integer(c_INT32_T), value, intent(in) :: n
integer(C_INT32_T), intent(in) :: indices(n)
integer(C_INT) :: err
integer :: i, j
err = 0
if (index >= 1 .and. index <= size(cells)) then
associate (c => cells(index))
select case (type)
case (FILL_MATERIAL)
if (allocated(c % material)) deallocate(c % material)
allocate(c % material(n))
call c % set_type(FILL_MATERIAL)
do i = 1, n
j = indices(i)
if ((j >= 1 .and. j <= n_materials) .or. j == MATERIAL_VOID) then
c % material(i) = j
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index " // trim(to_str(j)) // " in the &
&materials array is out of bounds.")
end if
end do
case (FILL_UNIVERSE)
call c % set_type(FILL_UNIVERSE)
case (FILL_LATTICE)
call c % set_type(FILL_LATTICE)
end select
end associate
else
err = E_OUT_OF_BOUNDS
call set_errmsg("Index in cells array is out of bounds.")
end if
end function openmc_cell_set_fill
function openmc_cell_set_id(index, id) result(err) bind(C)
! Set the ID of a cell
integer(C_INT32_T), value, intent(in) :: index

View file

@ -51,6 +51,35 @@ get_shape(hid_t obj_id, hsize_t* dims)
}
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name)
{
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);
std::vector<hsize_t> shape = object_shape(attr);
H5Aclose(attr);
return shape;
}
std::vector<hsize_t> object_shape(hid_t obj_id)
{
// Get number of dimensions
auto type = H5Iget_type(obj_id);
hid_t dspace;
if (type == H5I_DATASET) {
dspace = H5Dget_space(obj_id);
} else if (type == H5I_ATTR) {
dspace = H5Aget_space(obj_id);
}
int n = H5Sget_simple_extent_ndims(dspace);
// Get shape of array
std::vector<hsize_t> shape(n);
H5Sget_simple_extent_dims(dspace, shape.data(), nullptr);
// Free resources and return
H5Sclose(dspace);
return shape;
}
void
get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims)
{
@ -116,6 +145,18 @@ dataset_typesize(hid_t dset)
}
void
ensure_exists(hid_t group_id, const char* name)
{
if (!object_exists(group_id, name)) {
std::stringstream err_msg;
err_msg << "Object \"" << name << "\" does not exist in group "
<< object_name(group_id);
fatal_error(err_msg);
}
}
hid_t
file_open(const char* filename, char mode, bool parallel)
{
@ -285,6 +326,36 @@ get_groups(hid_t group_id, char* name[])
}
}
std::vector<std::string>
group_names(hid_t group_id)
{
// Determine number of links in the group
H5G_info_t info;
H5Gget_info(group_id, &info);
// Iterate over links to get names
H5O_info_t oinfo;
size_t size;
std::vector<std::string> names;
for (hsize_t i = 0; i < info.nlinks; ++i) {
// Determine type of object (and skip non-group)
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
H5P_DEFAULT);
if (oinfo.type != H5O_TYPE_GROUP) continue;
// Get size of name
size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC,
i, nullptr, 0, H5P_DEFAULT);
// Read name
char buffer[size];
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
buffer, size, H5P_DEFAULT);
names.emplace_back(&buffer[0], size);
}
return names;
}
bool
object_exists(hid_t object_id, const char* name)
@ -299,14 +370,23 @@ object_exists(hid_t object_id, const char* name)
}
std::string
object_name(hid_t obj_id)
{
// Determine size and create buffer
size_t size = 1 + H5Iget_name(obj_id, nullptr, 0);
char buffer[size];
// Read and return name
H5Iget_name(obj_id, buffer, size);
return {buffer, size};
}
hid_t
open_dataset(hid_t group_id, const char* name)
{
if (!object_exists(group_id, name)) {
std::stringstream err_msg;
err_msg << "Group \"" << name << "\" does not exist";
fatal_error(err_msg);
}
ensure_exists(group_id, name);
return H5Dopen(group_id, name, H5P_DEFAULT);
}
@ -314,11 +394,7 @@ open_dataset(hid_t group_id, const char* name)
hid_t
open_group(hid_t group_id, const char* name)
{
if (!object_exists(group_id, name)) {
std::stringstream err_msg;
err_msg << "Group \"" << name << "\" does not exist";
fatal_error(err_msg);
}
ensure_exists(group_id, name);
return H5Gopen(group_id, name, H5P_DEFAULT);
}
@ -425,7 +501,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde
void
read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep)
read_complex(hid_t obj_id, const char* name, std::complex<double>* buffer, bool indep)
{
// Create compound datatype for complex numbers
struct complex_t {

View file

@ -1,19 +1,20 @@
#ifndef OPENMC_HDF5_INTERFACE_H
#define OPENMC_HDF5_INTERFACE_H
#include "hdf5.h"
#include "hdf5_hl.h"
#include <array>
#include <complex>
#include <cstddef>
#include <string>
#include <sstream>
#include <vector>
#include <complex.h>
#include "hdf5.h"
#include "hdf5_hl.h"
#include "xtensor/xadapt.hpp"
#include "xtensor/xarray.hpp"
#include "position.h"
namespace openmc {
//==============================================================================
@ -21,7 +22,7 @@ namespace openmc {
//==============================================================================
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id,
const void* buffer);
void* buffer);
void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
hid_t mem_type_id, const void* buffer);
void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id,
@ -72,6 +73,12 @@ read_nd_vector(hid_t obj_id, const char* name,
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
bool must_have = false);
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name);
void ensure_exists(hid_t group_id, const char* name);
std::vector<std::string> group_names(hid_t group_id);
std::vector<hsize_t> object_shape(hid_t obj_id);
std::string object_name(hid_t obj_id);
//==============================================================================
// Fortran compatibility functions
//==============================================================================
@ -101,7 +108,7 @@ extern "C" {
void read_attr_string(hid_t obj_id, const char* name, size_t slen,
char* buffer);
void read_complex(hid_t obj_id, const char* name,
double _Complex* buffer, bool indep);
std::complex<double>* buffer, bool indep);
void read_double(hid_t obj_id, const char* name, double* buffer,
bool indep);
void read_int(hid_t obj_id, const char* name, int* buffer,
@ -142,7 +149,127 @@ template<typename T>
struct H5TypeMap { static const hid_t type_id; };
//==============================================================================
// Template functions used to provide simple interface to lower-level functions
// Templates/overloads for read_attribute
//==============================================================================
// Scalar version
template<typename T>
void read_attribute(hid_t obj_id, const char* name, T& buffer)
{
read_attr(obj_id, name, H5TypeMap<T>::type_id, &buffer);
}
// vector version
template<typename T>
void read_attribute(hid_t obj_id, const char* name, std::vector<T>& vec)
{
// Get shape of attribute array
auto shape = attribute_shape(obj_id, name);
// Allocate new array to read data into
std::size_t size = 1;
for (const auto x : shape)
size *= x;
vec.resize(size);
// Read data from attribute
read_attr(obj_id, name, H5TypeMap<T>::type_id, vec.data());
}
// Generic array version
template<typename T>
void read_attribute(hid_t obj_id, const char* name, xt::xarray<T>& arr)
{
// Get shape of attribute array
auto shape = attribute_shape(obj_id, name);
// Allocate new array to read data into
std::size_t size = 1;
for (const auto x : shape)
size *= x;
T* buffer = new T[size];
// Read data from attribute
read_attr(obj_id, name, H5TypeMap<T>::type_id, buffer);
// Adapt array into xarray
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
}
// overload for std::string
inline void
read_attribute(hid_t obj_id, const char* name, std::string& str)
{
// Create buffer to read data into
auto n = attribute_typesize(obj_id, name);
char buffer[n];
// Read attribute and set string
read_attr_string(obj_id, name, n, buffer);
str = std::string{buffer, n};
}
//==============================================================================
// Templates/overloads for read_dataset
//==============================================================================
template<typename T>
void read_dataset(hid_t obj_id, const char* name, T buffer, bool indep=false)
{
read_dataset(obj_id, name, H5TypeMap<T>::type_id, &buffer, indep);
}
template <typename T>
void read_dataset(hid_t dset, std::vector<T>& vec, bool indep=false)
{
// Get shape of dataset
std::vector<hsize_t> shape = object_shape(dset);
// Resize vector to appropriate size
vec.resize(shape[0]);
// Read data into vector
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, vec.data(), indep);
}
template <typename T>
void read_dataset(hid_t obj_id, const char* name, std::vector<T>& vec, bool indep=false)
{
hid_t dset = open_dataset(obj_id, name);
read_dataset(dset, vec, indep);
close_dataset(dset);
}
template <typename T>
void read_dataset(hid_t dset, xt::xarray<T>& arr, bool indep=false)
{
// Get shape of dataset
std::vector<hsize_t> shape = object_shape(dset);
// Allocate new array to read data into
std::size_t size = 1;
for (const auto x : shape)
size *= x;
T* buffer = new T[size];
// Read data from attribute
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, buffer, indep);
// Adapt into xarray
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
}
template <typename T>
void read_dataset(hid_t obj_id, const char* name, xt::xarray<T>& arr, bool indep=false)
{
// Open dataset and read array
hid_t dset = open_dataset(obj_id, name);
read_dataset(dset, arr, indep);
close_dataset(dset);
}
//==============================================================================
// Templates/overloads for write_attribute
//==============================================================================
template<typename T> inline void
@ -151,8 +278,8 @@ write_attribute(hid_t obj_id, const char* name, T buffer)
write_attr(obj_id, name, 0, nullptr, H5TypeMap<T>::type_id, &buffer);
}
template<> inline void
write_attribute<const char*>(hid_t obj_id, const char* name, const char* buffer)
inline void
write_attribute(hid_t obj_id, const char* name, const char* buffer)
{
write_attr_string(obj_id, name, buffer);
}
@ -164,14 +291,18 @@ write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
}
//==============================================================================
// Templates/overloads for write_dataset
//==============================================================================
template<typename T> inline void
write_dataset(hid_t obj_id, const char* name, T buffer)
{
write_dataset(obj_id, 0, nullptr, name, H5TypeMap<T>::type_id, &buffer, false);
}
template<> inline void
write_dataset<const char*>(hid_t obj_id, const char* name, const char* buffer)
inline void
write_dataset(hid_t obj_id, const char* name, const char* buffer)
{
write_string(obj_id, name, buffer, false);
}

View file

@ -48,8 +48,8 @@ module input_xml
save
interface
subroutine adjust_indices_c() bind(C)
end subroutine adjust_indices_c
subroutine adjust_indices() bind(C)
end subroutine adjust_indices
subroutine allocate_offset_tables(n_maps) bind(C)
import C_INT
@ -87,6 +87,11 @@ module input_xml
type(C_PTR) :: node_ptr
end subroutine read_settings
subroutine read_materials(node_ptr) bind(C)
import C_PTR
type(C_PTR) :: node_ptr
end subroutine read_materials
function find_root_universe() bind(C) result(root)
import C_INT32_T
integer(C_INT32_T) :: root
@ -1051,7 +1056,7 @@ contains
allocate(surfaces(n_surfaces))
do i = 1, n_surfaces
surfaces(i) % ptr = surface_pointer_c(i - 1);
surfaces(i) % ptr = surface_pointer(i - 1);
if (surfaces(i) % bc() /= BC_TRANSMIT) boundary_exists = .true.
@ -1090,7 +1095,7 @@ contains
do i = 1, n_cells
c => cells(i)
c % ptr = cell_pointer_c(i - 1)
c % ptr = cell_pointer(i - 1)
! Initialize distribcell instances and distribcell index
c % distribcell_index = NONE
@ -1104,42 +1109,6 @@ contains
// to_str(c % id()))
end if
! Read material
if (check_for_node(node_cell, "material")) then
n_mats = node_word_count(node_cell, "material")
if (n_mats > 0) then
allocate(sarray(n_mats))
call get_node_array(node_cell, "material", sarray)
allocate(c % material(n_mats))
do j = 1, n_mats
select case(trim(to_lower(sarray(j))))
case ('void')
c % material(j) = MATERIAL_VOID
case default
c % material(j) = int(str_to_int(sarray(j)), 4)
! Check for error
if (c % material(j) == ERROR_INT) then
call fatal_error("Invalid material specified on cell " &
// to_str(c % id()))
end if
end select
end do
deallocate(sarray)
else
allocate(c % material(1))
c % material(1) = NONE
end if
else
allocate(c % material(1))
c % material(1) = NONE
end if
! Check for region specification (also under deprecated name surfaces)
if (check_for_node(node_cell, "surfaces")) then
call warning("The use of 'surfaces' is deprecated and will be &
@ -1241,7 +1210,7 @@ contains
n = node_word_count(node_cell, "temperature")
if (n > 0) then
! Make sure this is a "normal" cell.
if (c % material(1) == NONE) call fatal_error("Cell " &
if (c % fill() /= C_NONE) call fatal_error("Cell " &
// trim(to_str(c % id())) // " was specified with a temperature &
&but no material. Temperature specification is only valid for &
&cells filled with a material.")
@ -1304,7 +1273,7 @@ contains
RECT_LATTICES: do i = 1, n_rlats
allocate(RectLattice::lattices(i) % obj)
lat => lattices(i) % obj
lat % ptr = lattice_pointer_c(i - 1)
lat % ptr = lattice_pointer(i - 1)
select type(lat)
type is (RectLattice)
@ -1320,7 +1289,7 @@ contains
HEX_LATTICES: do i = 1, n_hlats
allocate(HexLattice::lattices(n_rlats + i) % obj)
lat => lattices(n_rlats + i) % obj
lat % ptr = lattice_pointer_c(n_rlats + i - 1)
lat % ptr = lattice_pointer(n_rlats + i - 1)
select type (lat)
type is (HexLattice)
@ -1535,10 +1504,12 @@ contains
call doc % load_file(filename)
root = doc % document_element()
call read_materials(root % ptr)
! Get pointer to list of XML <material>
call get_node_list(root, "material", node_mat_list)
! Allocate cells array
! Allocate materials array
n_materials = size(node_mat_list)
allocate(materials(n_materials))
allocate(material_temps(n_materials))
@ -1551,16 +1522,11 @@ contains
do i = 1, n_materials
mat => materials(i)
mat % ptr = material_pointer(i - 1)
! Get pointer to i-th material node
node_mat = node_mat_list(i)
! Copy material id
if (check_for_node(node_mat, "id")) then
call get_node_value(node_mat, "id", mat % id)
else
call fatal_error("Must specify id of material in materials XML file")
end if
! Check if material is depletable
if (check_for_node(node_mat, "depletable")) then
call get_node_value(node_mat, "depletable", temp_str)
@ -1568,12 +1534,6 @@ contains
mat % depletable = .true.
end if
! Check to make sure 'id' hasn't been used
if (material_dict % has(mat % id)) then
call fatal_error("Two or more materials use the same unique ID: " &
// to_str(mat % id))
end if
! Copy material name
if (check_for_node(node_mat, "name")) then
call get_node_value(node_mat, "name", mat % name)
@ -1591,7 +1551,7 @@ contains
node_dens = node_mat % child("density")
else
call fatal_error("Must specify density element in material " &
// trim(to_str(mat % id)))
// trim(to_str(mat % id())))
end if
! Copy units
@ -1621,7 +1581,7 @@ contains
sum_density = .false.
if (val <= ZERO) then
call fatal_error("Need to specify a positive density on material " &
// trim(to_str(mat % id)) // ".")
// trim(to_str(mat % id())) // ".")
end if
! Adjust material density based on specified units
@ -1636,7 +1596,7 @@ contains
mat % density = 1.0e-24_8 * val
case default
call fatal_error("Unkwown units '" // trim(units) &
// "' specified on material " // trim(to_str(mat % id)))
// "' specified on material " // trim(to_str(mat % id())))
end select
end if
@ -1645,7 +1605,7 @@ contains
if (size(node_ele_list) > 0) then
call fatal_error("Unable to add an element to material " &
// trim(to_str(mat % id)) // " since the element option has &
// trim(to_str(mat % id())) // " since the element option has &
&been removed from the xml input. Elements can only be added via &
&the Python API, which will expand elements into their natural &
&nuclides.")
@ -1658,7 +1618,7 @@ contains
if (.not. check_for_node(node_mat, "nuclide") .and. &
.not. check_for_node(node_mat, "macroscopic")) then
call fatal_error("No macroscopic data or nuclides specified on &
&material " // trim(to_str(mat % id)))
&material " // trim(to_str(mat % id())))
end if
! Create list of macroscopic x/s based on those specified, just treat
@ -1672,7 +1632,7 @@ contains
& mode!")
else if (size(node_macro_list) > 1) then
call fatal_error("Only one macroscopic object permitted per material, " &
// trim(to_str(mat % id)))
// trim(to_str(mat % id())))
else if (size(node_macro_list) == 1) then
node_nuc = node_macro_list(1)
@ -1680,7 +1640,7 @@ contains
! Check for empty name on nuclide
if (.not. check_for_node(node_nuc, "name")) then
call fatal_error("No name specified on macroscopic data in material " &
// trim(to_str(mat % id)))
// trim(to_str(mat % id())))
end if
! store nuclide name
@ -1710,7 +1670,7 @@ contains
! Check for empty name on nuclide
if (.not. check_for_node(node_nuc, "name")) then
call fatal_error("No name specified on nuclide in material " &
// trim(to_str(mat % id)))
// trim(to_str(mat % id())))
end if
! store nuclide name
@ -1849,7 +1809,7 @@ contains
if (.not. (all(mat % atom_density >= ZERO) .or. &
all(mat % atom_density <= ZERO))) then
call fatal_error("Cannot mix atom and weight percents in material " &
// to_str(mat % id))
// to_str(mat % id()))
end if
! Determine density if it is a sum value
@ -1930,7 +1890,7 @@ contains
end if
! Add material to dictionary
call material_dict % set(mat % id, i)
call material_dict % set(mat % id(), i)
end do
! Set total number of nuclides and S(a,b) tables
@ -3249,7 +3209,7 @@ contains
! Check if the specified tally mesh exists
if (mesh_dict % has(meshid)) then
pl % meshlines_mesh => meshes(mesh_dict % get(meshid))
if (meshes(meshid) % type /= LATTICE_RECT) then
if (meshes(meshid) % type /= MESH_REGULAR) then
call fatal_error("Non-rectangular mesh specified in &
&meshlines for plot " // trim(to_str(pl % id)))
end if
@ -3849,15 +3809,15 @@ contains
do i = 1, n_cells
! Ignore non-normal cells and cells with defined temperature.
if (cells(i) % material(1) == NONE) cycle
if (cells(i) % fill() /= C_NONE) cycle
if (cells(i) % sqrtkT(1) >= ZERO) cycle
! Set the number of temperatures equal to the number of materials.
deallocate(cells(i) % sqrtkT)
allocate(cells(i) % sqrtkT(size(cells(i) % material)))
allocate(cells(i) % sqrtkT(cells(i) % material_size()))
! Check each of the cell materials for temperature data.
do j = 1, size(cells(i) % material)
do j = 1, cells(i) % material_size()
! Arbitrarily set void regions to 0K.
if (cells(i) % material(j) == MATERIAL_VOID) then
cells(i) % sqrtkT(j) = ZERO
@ -3925,45 +3885,6 @@ contains
end subroutine read_multipole_data
!===============================================================================
! ADJUST_INDICES changes the values for 'surfaces' for each cell and the
! material index assigned to each to the indices in the surfaces and material
! array rather than the unique IDs assigned to each surface and material. Also
! assigns boundary conditions to surfaces based on those read into the bc_dict
! dictionary
!===============================================================================
subroutine adjust_indices()
integer :: i ! index for various purposes
integer :: j ! index for various purposes
integer :: id ! user-specified id
call adjust_indices_c()
do i = 1, n_cells
associate (c => cells(i))
! =======================================================================
! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL
if (c % material(1) /= NONE) then
do j = 1, size(c % material)
id = c % material(j)
if (id == MATERIAL_VOID) then
else if (material_dict % has(id)) then
c % material(j) = material_dict % get(id)
else
call fatal_error("Could not find material " // trim(to_str(id)) &
// " specified on cell " // trim(to_str(c % id())))
end if
end do
end if
end associate
end do
end subroutine adjust_indices
!===============================================================================
! PREPARE_DISTRIBCELL initializes any distribcell filters present and sets the
! offsets for distribcells
@ -3987,7 +3908,7 @@ contains
! Find all cells with multiple (distributed) materials or temperatures.
do i = 1, n_cells
if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then
if (cells(i) % material_size() > 1 .or. size(cells(i) % sqrtkT) > 1) then
call cell_list % add(i)
end if
end do
@ -3996,10 +3917,10 @@ contains
! number of respective cell instances.
do i = 1, n_cells
associate (c => cells(i))
if (size(c % material) > 1) then
if (size(c % material) /= c % n_instances()) then
if (c % material_size() > 1) then
if (c % material_size() /= c % n_instances()) then
call fatal_error("Cell " // trim(to_str(c % id())) // " was &
&specified with " // trim(to_str(size(c % material))) &
&specified with " // trim(to_str(c % material_size())) &
// " materials but has " // trim(to_str(c % n_instances())) &
// " distributed instances. The number of materials must &
&equal one or the number of instances.")

View file

@ -29,7 +29,7 @@ std::unordered_map<int32_t, int32_t> lattice_map;
Lattice::Lattice(pugi::xml_node lat_node)
{
if (check_for_node(lat_node, "id")) {
id = stoi(get_node_value(lat_node, "id"));
id = std::stoi(get_node_value(lat_node, "id"));
} else {
fatal_error("Must specify id of lattice in geometry XML file.");
}
@ -39,7 +39,7 @@ Lattice::Lattice(pugi::xml_node lat_node)
}
if (check_for_node(lat_node, "outer")) {
outer = stoi(get_node_value(lat_node, "outer"));
outer = std::stoi(get_node_value(lat_node, "outer"));
}
}
@ -141,14 +141,14 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
std::string dimension_str {get_node_value(lat_node, "dimension")};
std::vector<std::string> dimension_words {split(dimension_str)};
if (dimension_words.size() == 2) {
n_cells[0] = stoi(dimension_words[0]);
n_cells[1] = stoi(dimension_words[1]);
n_cells[0] = std::stoi(dimension_words[0]);
n_cells[1] = std::stoi(dimension_words[1]);
n_cells[2] = 1;
is_3d = false;
} else if (dimension_words.size() == 3) {
n_cells[0] = stoi(dimension_words[0]);
n_cells[1] = stoi(dimension_words[1]);
n_cells[2] = stoi(dimension_words[2]);
n_cells[0] = std::stoi(dimension_words[0]);
n_cells[1] = std::stoi(dimension_words[1]);
n_cells[2] = std::stoi(dimension_words[2]);
is_3d = true;
} else {
fatal_error("Rectangular lattice must be two or three dimensions.");
@ -195,7 +195,7 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
for (int ix = 0; ix < nx; ix++) {
int indx1 = nx*ny*iz + nx*(ny-iy-1) + ix;
int indx2 = nx*ny*iz + nx*iy + ix;
universes[indx1] = stoi(univ_words[indx2]);
universes[indx1] = std::stoi(univ_words[indx2]);
}
}
}
@ -400,9 +400,9 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
: Lattice {lat_node}
{
// Read the number of lattice cells in each dimension.
n_rings = stoi(get_node_value(lat_node, "n_rings"));
n_rings = std::stoi(get_node_value(lat_node, "n_rings"));
if (check_for_node(lat_node, "n_axial")) {
n_axial = stoi(get_node_value(lat_node, "n_axial"));
n_axial = std::stoi(get_node_value(lat_node, "n_axial"));
is_3d = true;
} else {
n_axial = 1;
@ -476,7 +476,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
int indx = (2*n_rings-1)*(2*n_rings-1) * m
+ (2*n_rings-1) * (i_a+n_rings-1)
+ (i_x+n_rings-1);
universes[indx] = stoi(univ_words[input_index]);
universes[indx] = std::stoi(univ_words[input_index]);
input_index++;
// Walk the index to the right neighbor (which is not adjacent).
i_x += 2;
@ -505,7 +505,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
int indx = (2*n_rings-1)*(2*n_rings-1) * m
+ (2*n_rings-1) * (i_a+n_rings-1)
+ (i_x+n_rings-1);
universes[indx] = stoi(univ_words[input_index]);
universes[indx] = std::stoi(univ_words[input_index]);
input_index++;
// Walk the index to the right neighbor (which is not adjacent).
i_x += 2;
@ -528,7 +528,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
int indx = (2*n_rings-1)*(2*n_rings-1) * m
+ (2*n_rings-1) * (i_a+n_rings-1)
+ (i_x+n_rings-1);
universes[indx] = stoi(univ_words[input_index]);
universes[indx] = std::stoi(univ_words[input_index]);
input_index++;
// Walk the index to the right neighbor (which is not adjacent).
i_x += 2;

91
src/material.cpp Normal file
View file

@ -0,0 +1,91 @@
#include "material.h"
#include <string>
#include <sstream>
#include "error.h"
#include "xml_interface.h"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
std::vector<Material*> global_materials;
std::unordered_map<int32_t, int32_t> material_map;
//==============================================================================
// Material implementation
//==============================================================================
Material::Material(pugi::xml_node material_node)
{
if (check_for_node(material_node, "id")) {
id = std::stoi(get_node_value(material_node, "id"));
} else {
fatal_error("Must specify id of material in materials XML file.");
}
}
//==============================================================================
// Non-method functions
//==============================================================================
extern "C" void
read_materials(pugi::xml_node* node)
{
// Loop over XML material elements and populate the array.
for (pugi::xml_node material_node : node->children("material")) {
global_materials.push_back(new Material(material_node));
}
global_materials.shrink_to_fit();
// Populate the material map.
for (int i = 0; i < global_materials.size(); i++) {
int32_t mid = global_materials[i]->id;
auto search = material_map.find(mid);
if (search == material_map.end()) {
material_map[mid] = i;
} else {
std::stringstream err_msg;
err_msg << "Two or more materials use the same unique ID: " << mid;
fatal_error(err_msg);
}
}
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" {
Material* material_pointer(int32_t indx) {return global_materials[indx];}
int32_t material_id(Material* mat) {return mat->id;}
void material_set_id(Material* mat, int32_t id, int32_t index)
{
mat->id = id;
//TODO: off-by-one
material_map[id] = index - 1;
}
void extend_materials_c(int32_t n)
{
global_materials.reserve(global_materials.size() + n);
for (int32_t i = 0; i < n; i++) {
global_materials.push_back(new Material());
}
}
void free_memory_material_c()
{
for (Material *mat : global_materials) {delete mat;}
global_materials.clear();
material_map.clear();
}
}
} // namespace openmc

35
src/material.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef OPENMC_MATERIAL_H
#define OPENMC_MATERIAL_H
#include <unordered_map>
#include <vector>
#include "pugixml.hpp"
namespace openmc {
//==============================================================================
// Global variables
//==============================================================================
class Material;
extern std::vector<Material*> global_materials;
extern std::unordered_map<int32_t, int32_t> material_map;
//==============================================================================
//! A substance with constituent nuclides and thermal scattering data
//==============================================================================
class Material
{
public:
int32_t id; //!< Unique ID
Material() {};
explicit Material(pugi::xml_node material_node);
};
} // namespace openmc
#endif // OPENMC_MATERIAL_H

View file

@ -27,13 +27,41 @@ module material_header
public :: openmc_material_set_density
public :: openmc_material_set_densities
public :: openmc_material_set_id
public :: material_pointer
interface
function material_pointer(mat_ind) bind(C) result(ptr)
import C_PTR, C_INT32_T
integer(C_INT32_T), intent(in), value :: mat_ind
type(C_PTR) :: ptr
end function material_pointer
function material_id_c(mat_ptr) bind(C, name='material_id') result(id)
import C_PTR, C_INT32_T
type(C_PTR), intent(in), value :: mat_ptr
integer(C_INT32_T) :: id
end function material_id_c
subroutine material_set_id_c(mat_ptr, id, index) &
bind(C, name='material_set_id')
import C_PTR, C_INT32_T
type(C_PTR), intent(in), value :: mat_ptr
integer(C_INT32_T), intent(in), value :: id
integer(C_INT32_T), intent(in), value :: index
end subroutine material_set_id_c
subroutine extend_materials_c(n) bind(C)
import C_INT32_t
integer(C_INT32_T), intent(in), value :: n
end subroutine extend_materials_c
end interface
!===============================================================================
! MATERIAL describes a material by its constituent nuclides
!===============================================================================
type, public :: Material
integer :: id ! unique identifier
type(C_PTR) :: ptr
character(len=104) :: name = "" ! User-defined name
integer :: n_nuclides = 0 ! number of nuclides
integer, allocatable :: nuclide(:) ! index in nuclides array
@ -67,6 +95,8 @@ module material_header
logical, allocatable :: p0(:)
contains
procedure :: id => material_id
procedure :: set_id => material_set_id
procedure :: set_density => material_set_density
procedure :: init_nuclide_index => material_init_nuclide_index
procedure :: assign_sab_tables => material_assign_sab_tables
@ -88,6 +118,19 @@ contains
! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm.
!===============================================================================
function material_id(this) result(id)
class(Material), intent(in) :: this
integer(C_INT32_T) :: id
id = material_id_c(this % ptr)
end function material_id
subroutine material_set_id(this, id, index)
class(Material), intent(in) :: this
integer(C_INT32_T), intent(in) :: id
integer(C_INT32_T), intent(in) :: index
call material_set_id_c(this % ptr, id, index)
end subroutine material_set_id
function material_set_density(this, density) result(err)
class(Material), intent(inout) :: this
real(8), intent(in) :: density
@ -185,7 +228,7 @@ contains
if (.not. found) then
call fatal_error("S(a,b) table " // trim(this % &
sab_names(k)) // " did not match any nuclide on material " &
// trim(to_str(this % id)))
// trim(to_str(this % id())))
end if
end do ASSIGN_SAB
@ -195,7 +238,7 @@ contains
if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then
call fatal_error(trim( &
nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) &
// " in material " // trim(to_str(this % id)) // " was found &
// " in material " // trim(to_str(this % id())) // " was found &
&in multiple S(a,b) tables. Each nuclide can only appear in &
&one S(a,b) table per material.")
end if
@ -444,6 +487,11 @@ contains
!===============================================================================
subroutine free_memory_material()
interface
subroutine free_memory_material_c() bind(C)
end subroutine free_memory_material_c
end interface
call free_memory_material_c()
n_materials = 0
if (allocated(materials)) deallocate(materials)
call material_dict % clear()
@ -460,6 +508,7 @@ contains
integer(C_INT32_T), optional, intent(out) :: index_end
integer(C_INT) :: err
integer :: i
type(Material), allocatable :: temp(:) ! temporary materials array
if (n_materials == 0) then
@ -481,6 +530,12 @@ contains
if (present(index_end)) index_end = n_materials + n
n_materials = n_materials + n
! Extend the C++ materials array and get pointers to the C++ objects
call extend_materials_c(n)
do i = n_materials - n, n_materials
materials(i) % ptr = material_pointer(i - 1)
end do
err = 0
end function openmc_extend_materials
@ -606,7 +661,7 @@ contains
integer(C_INT) :: err
if (index >= 1 .and. index <= size(materials)) then
id = materials(index) % id
id = materials(index) % id()
err = 0
else
err = E_OUT_OF_BOUNDS
@ -622,7 +677,7 @@ contains
integer(C_INT) :: err
if (index >= 1 .and. index <= n_materials) then
materials(index) % id = id
call materials(index) % set_id(id, index)
call material_dict % set(id, index)
err = 0
else

View file

@ -11,6 +11,7 @@ module math
public :: calc_pn
public :: calc_rn
public :: calc_zn
public :: calc_zn_rad
public :: evaluate_legendre
public :: rotate_angle
public :: maxwell_spectrum
@ -68,6 +69,14 @@ module math
real(C_DOUBLE), intent(out) :: zn(((n + 1) * (n + 2)) / 2)
end subroutine calc_zn
pure subroutine calc_zn_rad(n, rho, zn_rad) bind(C, name='calc_zn_rad_c')
use ISO_C_BINDING
implicit none
integer(C_INT), value, intent(in) :: n
real(C_DOUBLE), value, intent(in) :: rho
real(C_DOUBLE), intent(out) :: zn_rad((n / 2) + 1)
end subroutine calc_zn_rad
subroutine rotate_angle_c_intfc(uvw, mu, phi) bind(C, name='rotate_angle_c')
use ISO_C_BINDING
implicit none

View file

@ -587,6 +587,33 @@ void calc_zn_c(int n, double rho, double phi, double zn[]) {
}
void calc_zn_rad_c(int n, double rho, double zn_rad[]) {
// Calculate R_p0(rho) as Zn_p0(rho)
// Set up the array of the coefficients
double q = 0;
// R_00 is always 1
zn_rad[0] = 1;
// Fill in the rest of the array (Eq 3.8 and Eq 3.10 in Chong)
for (int p = 2; p <= n; p += 2) {
int index = int(p/2);
if (p == 2) {
// Setting up R_22 to calculate R_20 (Eq 3.10 in Chong)
double R_22 = rho * rho;
zn_rad[index] = 2 * R_22 - zn_rad[0];
} else {
double k1 = ((p + q) * (p - q) * (p - 2)) / 2.;
double k2 = 2 * p * (p - 1) * (p - 2);
double k3 = -q * q * (p - 1) - p * (p - 1) * (p - 2);
double k4 = (-p * (p + q - 2) * (p - q - 2)) / 2.;
zn_rad[index] =
((k2 * rho * rho + k3) * zn_rad[index-1] + k4 * zn_rad[index-2]) / k1;
}
}
}
void rotate_angle_c(double uvw[3], double mu, double* phi) {
// Copy original directional cosines
@ -623,6 +650,14 @@ void rotate_angle_c(double uvw[3], double mu, double* phi) {
}
Direction rotate_angle(Direction u, double mu, double* phi)
{
double uvw[] {u.x, u.y, u.z};
rotate_angle_c(uvw, mu, phi);
return {uvw[0], uvw[1], uvw[2]};
}
double maxwell_spectrum_c(double T) {
// Set the random numbers
double r1 = prn();

View file

@ -1,13 +1,14 @@
//! \file math_functions.h
//! A collection of elementary math functions.
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
#ifndef OPENMC_MATH_FUNCTIONS_H
#define OPENMC_MATH_FUNCTIONS_H
#include <cmath>
#include <cstdlib>
#include "constants.h"
#include "position.h"
#include "random_lcg.h"
@ -91,6 +92,24 @@ extern "C" void calc_rn_c(int n, const double uvw[3], double rn[]);
extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]);
//==============================================================================
//! Calculate only the even order components of n-th order modified Zernike
//! polynomial moment with azimuthal dependency m = 0 for a given radial (rho)
//! location on the unit disk.
//!
//! Since m = 0, n could only be even orders. Z_q0 = R_q0
//!
//! See calc_zn_c for methodology.
//!
//! @param n The maximum order requested
//! @param rho The radial parameter to specify location on the unit disk
//! @param phi The angle parameter to specify location on the unit disk
//! @param zn_rad The requested moments of order 0 to n (inclusive)
//! evaluated at rho and phi when m = 0.
//==============================================================================
extern "C" void calc_zn_rad_c(int n, double rho, double zn_rad[]);
//==============================================================================
//! Rotate the direction cosines through a polar angle whose cosine is mu and
//! through an azimuthal angle sampled uniformly.
@ -106,6 +125,8 @@ extern "C" void calc_zn_c(int n, double rho, double phi, double zn[]);
extern "C" void rotate_angle_c(double uvw[3], double mu, double* phi);
Direction rotate_angle(Direction u, double mu, double* phi);
//==============================================================================
//! Samples an energy from the Maxwell fission distribution based on a direct
//! sampling scheme.
@ -202,4 +223,4 @@ extern "C" double spline_integrate_c(int n, const double x[], const double y[],
const double z[], double xa, double xb);
} // namespace openmc
#endif // MATH_FUNCTIONS_H
#endif // OPENMC_MATH_FUNCTIONS_H

View file

@ -151,11 +151,13 @@ contains
allocate(kTs(size(materials)))
do i = 1, size(cells)
do j = 1, size(cells(i) % material)
! Skip non-material cells
if (cells(i) % fill() /= C_NONE) cycle
! Skip any non-material cells and void materials
if (cells(i) % material(j) == NONE .or. &
cells(i) % material(j) == MATERIAL_VOID) cycle
do j = 1, cells(i) % material_size()
! Skip void materials
if (cells(i) % material(j) == MATERIAL_VOID) cycle
! Get temperature of cell (rounding to nearest integer)
if (size(cells(i) % sqrtkT) > 1) then

View file

@ -17,11 +17,9 @@ module nuclide_header
FIT_T, FIT_A, FIT_F, MultipoleArray
use message_passing
use multipole_header, only: MultipoleArray
use product_header, only: AngleEnergyContainer
use random_lcg, only: prn, future_prn, prn_set_stream
use reaction_header, only: Reaction
use sab_header, only: SAlphaBeta, sab_tables
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
use settings
use stl_vector, only: VectorInt, VectorReal
use string
@ -642,29 +640,33 @@ contains
if (rx % MT >= N_2N0 .and. rx % MT <= N_2NC .and. find(MTs, N_2N) /= -1) cycle
do t = 1, n_temperature
j = rx % xs(t) % threshold
n = size(rx % xs(t) % value)
j = rx % xs_threshold(t)
n = rx % xs_size(t)
! Add contribution to total cross section
this % xs(t) % value(XS_TOTAL,j:j+n-1) = this % xs(t) % &
value(XS_TOTAL,j:j+n-1) + rx % xs(t) % value
do k = j, j + n - 1
this % xs(t) % value(XS_TOTAL,k) = this % xs(t) % &
value(XS_TOTAL,k) + rx % xs(t, k - j + 1)
end do
! Calculate photon production cross section
do k = 1, size(rx % products)
if (rx % products(k) % particle == PHOTON) then
do k = 1, rx % products_size()
if (rx % product_particle(k) == PHOTON) then
do l = 1, n
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) = &
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) + &
rx % xs(t) % value(l) * rx % products(k) % &
yield % evaluate(this % grid(t) % energy(l+j-1))
rx % xs(t, l) * rx % product_yield(k, &
this % grid(t) % energy(l+j-1))
end do
end if
end do
! Add contribution to absorption cross section
if (is_disappearance(rx % MT)) then
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
do k = j, j + n - 1
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
end do
end if
! Information about fission reactions
@ -680,39 +682,20 @@ contains
! Add contribution to fission cross section
if (is_fission(rx % MT)) then
this % fissionable = .true.
this % xs(t) % value(XS_FISSION,j:j+n-1) = this % xs(t) % &
value(XS_FISSION,j:j+n-1) + rx % xs(t) % value
do k = j, j + n - 1
this % xs(t) % value(XS_FISSION,k) = this % xs(t) % &
value(XS_FISSION,k) + rx % xs(t, k - j + 1)
! Also need to add fission cross sections to absorption
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
! Also need to add fission cross sections to absorption
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
end do
! Keep track of this reaction for easy searching later
if (t == 1) then
i_fission = i_fission + 1
this % index_fission(i_fission) = i
this % n_fission = this % n_fission + 1
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
! Before the secondary distribution refactor, when the angle/energy
! distribution was uncorrelated, no angle was actually sampled. With
! the refactor, an angle is always sampled for an uncorrelated
! distribution even when no angle distribution exists in the ACE file
! (isotropic is assumed). To preserve the RNG stream, we explicitly
! mark fission reactions so that we avoid the angle sampling.
do k = 1, size(rx % products)
if (rx % products(k) % particle == NEUTRON) then
do m = 1, size(rx % products(k) % distribution)
associate (aedist => rx % products(k) % distribution(m) % obj)
select type (aedist)
type is (UncorrelatedAngleEnergy)
aedist % fission = .true.
end select
end associate
end do
end if
end do
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
end if
end if ! fission
end do ! temperature
@ -721,12 +704,13 @@ contains
! Determine number of delayed neutron precursors
if (this % fissionable) then
do i = 1, size(this % reactions(this % index_fission(1)) % products)
if (this % reactions(this % index_fission(1)) % products(i) % &
emission_mode == EMISSION_DELAYED) then
this % n_precursor = this % n_precursor + 1
end if
end do
associate (rx => this % reactions(this % index_fission(1)))
do i = 1, rx % products_size()
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
this % n_precursor = this % n_precursor + 1
end if
end do
end associate
end if
! Calculate nu-fission cross section
@ -761,36 +745,30 @@ contains
select case (emission_mode)
case (EMISSION_PROMPT)
associate (product => this % reactions(this % index_fission(1)) % products(1))
nu = product % yield % evaluate(E)
associate (rx => this % reactions(this % index_fission(1)))
nu = rx % product_yield(1, E)
end associate
case (EMISSION_DELAYED)
if (this % n_precursor > 0) then
if (present(group) .and. group < &
size(this % reactions(this % index_fission(1)) % products)) then
! If delayed group specified, determine yield immediately
associate(p => this % reactions(this % index_fission(1)) % products(1 + group))
nu = p % yield % evaluate(E)
end associate
associate(rx => this % reactions(this % index_fission(1)))
if (present(group) .and. group < rx % products_size()) then
! If delayed group specified, determine yield immediately
nu = rx % product_yield(1 + group, E)
else
nu = ZERO
else
nu = ZERO
do i = 2, rx % products_size()
! Skip any non-neutron products
if (rx % product_particle(i) /= NEUTRON) exit
associate (rx => this % reactions(this % index_fission(1)))
do i = 2, size(rx % products)
associate (product => rx % products(i))
! Skip any non-neutron products
if (product % particle /= NEUTRON) exit
! Evaluate yield
if (product % emission_mode == EMISSION_DELAYED) then
nu = nu + product % yield % evaluate(E)
end if
end associate
! Evaluate yield
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
nu = nu + rx % product_yield(i, E)
end if
end do
end associate
end if
end if
end associate
else
nu = ZERO
end if
@ -799,8 +777,8 @@ contains
if (allocated(this % total_nu)) then
nu = this % total_nu % evaluate(E)
else
associate (product => this % reactions(this % index_fission(1)) % products(1))
nu = product % yield % evaluate(E)
associate (rx => this % reactions(this % index_fission(1)))
nu = rx % product_yield(1, E)
end associate
end if
end select
@ -868,6 +846,7 @@ contains
integer :: i_high ! upper logarithmic mapping index
integer :: i_rxn ! reaction index
integer :: j ! index in DEPLETION_RX
integer :: threshold ! threshold energy index
real(8) :: f ! interp factor on nuclide energy grid
real(8) :: kT ! temperature in eV
real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables
@ -1009,10 +988,11 @@ contains
! need to specifically check its threshold index
i_rxn = this % reaction_index(DEPLETION_RX(1))
if (i_rxn > 0) then
associate (xs => this % reactions(i_rxn) % xs(i_temp))
associate (rx => this % reactions(i_rxn))
threshold = rx % xs_threshold(i_temp)
micro_xs % reaction(1) = (ONE - f) * &
xs % value(i_grid - xs % threshold + 1) + &
f * xs % value(i_grid - xs % threshold + 2)
rx % xs(i_temp, i_grid - threshold + 1) + &
f * rx % xs(i_temp, i_grid - threshold + 2)
end associate
end if
@ -1022,11 +1002,12 @@ contains
! reaction xs appropriately
i_rxn = this % reaction_index(DEPLETION_RX(j))
if (i_rxn > 0) then
associate (xs => this % reactions(i_rxn) % xs(i_temp))
if (i_grid >= xs % threshold) then
associate (rx => this % reactions(i_rxn))
threshold = rx % xs_threshold(i_temp)
if (i_grid >= threshold) then
micro_xs % reaction(j) = (ONE - f) * &
xs % value(i_grid - xs % threshold + 1) + &
f * xs % value(i_grid - xs % threshold + 2)
rx % xs(i_temp, i_grid - threshold + 1) + &
f * rx % xs(i_temp, i_grid - threshold + 2)
elseif (j >= 4) then
! One can show that the the threshold for (n,(x+1)n) is always
! higher than the threshold for (n,xn). Thus, if we are below
@ -1091,8 +1072,9 @@ contains
f = micro_xs % interp_factor
if (i_temp > 0) then
associate (xs => this % reactions(1) % xs(i_temp) % value)
micro_xs % elastic = (ONE - f) * xs(i_grid) + f * xs(i_grid + 1)
associate (rx => this % reactions(1))
micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + &
f * rx % xs(i_temp, i_grid + 1)
end associate
else
! For multipole, elastic is total - absorption
@ -1459,6 +1441,7 @@ contains
integer :: i_energy ! index for energy
integer :: i_low ! band index at lower bounding energy
integer :: i_up ! band index at upper bounding energy
integer :: threshold ! threshold energy index
real(8) :: f ! interpolation factor
real(8) :: r ! pseudo-random number
real(8) :: elastic ! elastic cross section
@ -1551,10 +1534,11 @@ contains
f = micro_xs % interp_factor
! Determine inelastic scattering cross section
associate (xs => this % reactions(this % urr_inelastic) % xs(i_temp))
if (i_energy >= xs % threshold) then
inelastic = (ONE - f) * xs % value(i_energy - xs % threshold + 1) + &
f * xs % value(i_energy - xs % threshold + 2)
associate (rx => this % reactions(this % urr_inelastic))
threshold = rx % xs_threshold(i_temp)
if (i_energy >= threshold) then
inelastic = (ONE - f) * rx % xs(i_temp, i_energy - threshold + 1) + &
f * rx % xs(i_temp, i_energy - threshold + 2)
end if
end associate
end if

View file

@ -62,7 +62,7 @@ Particle::initialize()
clear();
// Set particle to neutron that's alive
type = NEUTRON;
type = static_cast<int>(ParticleType::neutron);
alive = true;
// clear attributes

View file

@ -15,12 +15,27 @@ namespace openmc {
// Constants
//==============================================================================
// Since cross section libraries come with different numbers of delayed groups
// (e.g. ENDF/B-VII.1 has 6 and JEFF 3.1.1 has 8 delayed groups) and we don't
// yet know what cross section library is being used when the tallies.xml file
// is read in, we want to have an upper bound on the size of the array we
// use to store the bins for delayed group tallies.
constexpr int MAX_DELAYED_GROUPS {8};
// Maximum number of secondary particles created
constexpr int MAX_SECONDARY {1000};
constexpr int NEUTRON {1};
// Maximum number of lost particles
constexpr int MAX_LOST_PARTICLES {10};
// Maximum number of lost particles, relative to the total number of particles
constexpr double REL_MAX_LOST_PARTICLES {1.0e-6};
//! Particle types
enum class ParticleType {
neutron, photon, electron, positron
};
extern "C" {
struct LocalCoord {

View file

@ -18,7 +18,6 @@ module physics
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
use reaction_header, only: Reaction
use sab_header, only: sab_tables
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
use settings
use simulation_header
use string, only: to_str
@ -506,6 +505,7 @@ contains
integer :: i
integer :: i_grid
integer :: i_temp
integer :: threshold
real(8) :: f
real(8) :: prob
real(8) :: cutoff
@ -545,13 +545,14 @@ contains
FISSION_REACTION_LOOP: do i = 1, nuc % n_fission
i_reaction = nuc % index_fission(i)
associate (xs => nuc % reactions(i_reaction) % xs(i_temp))
associate (rx => nuc % reactions(i_reaction))
! if energy is below threshold for this reaction, skip it
if (i_grid < xs % threshold) cycle
threshold = rx % xs_threshold(i_temp)
if (i_grid < threshold) cycle
! add to cumulative probability
prob = prob + ((ONE - f) * xs % value(i_grid - xs % threshold + 1) &
+ f*(xs % value(i_grid - xs % threshold + 2)))
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
end associate
! Create fission bank sites if fission occurs
@ -593,17 +594,17 @@ contains
! Loop through each reaction type
REACTION_LOOP: do i_reaction = 1, size(nuc % reactions)
associate (rx => nuc % reactions(i_reaction))
threshold = rx % xs(i_temp) % threshold
threshold = rx % xs_threshold(i_temp)
! if energy is below threshold for this reaction, skip it
if (i_grid < threshold) cycle
do i_product = 1, size(rx % products)
if (rx % products(i_product) % particle == PHOTON) then
do i_product = 1, rx % products_size()
if (rx % product_particle(i_product) == PHOTON) then
! add to cumulative probability
yield = rx % products(i_product) % yield % evaluate(E)
prob = prob + ((ONE - f) * rx % xs(i_temp) % value(i_grid - threshold + 1) &
+ f*(rx % xs(i_temp) % value(i_grid - threshold + 2))) * yield
yield = rx % product_yield(i_product, E)
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
+ f*(rx % xs(i_temp, i_grid - threshold + 2))) * yield
if (prob > cutoff) return
last_valid_reaction = i_reaction
@ -672,6 +673,7 @@ contains
integer :: j
integer :: i_temp
integer :: i_grid
integer :: threshold
real(8) :: f
real(8) :: prob
real(8) :: cutoff
@ -750,14 +752,14 @@ contains
&// trim(nuc % name))
end if
associate (rx => nuc % reactions(i), &
xs => nuc % reactions(i) % xs(i_temp))
associate (rx => nuc % reactions(i))
! if energy is below threshold for this reaction, skip it
if (i_grid < xs % threshold) cycle
threshold = rx % xs_threshold(i_temp)
if (i_grid < threshold) cycle
! add to cumulative probability
prob = prob + ((ONE - f)*xs % value(i_grid - xs % threshold + 1) &
+ f*(xs % value(i_grid - xs % threshold + 2)))
prob = prob + ((ONE - f)*rx % xs(i_temp, i_grid - threshold + 1) &
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
end associate
end do
@ -839,14 +841,7 @@ contains
vel = sqrt(dot_product(v_n, v_n))
! Sample scattering angle
select type (dist => rxn % products(1) % distribution(1) % obj)
type is (UncorrelatedAngleEnergy)
if (allocated(dist % angle % energy)) then
mu_cm = dist % angle % sample(E)
else
mu_cm = TWO*prn() - ONE
end if
end select
mu_cm = rxn % sample_elastic_mu(E)
! Determine direction cosines in CM
uvw_cm = v_n/vel
@ -1581,7 +1576,7 @@ contains
do group = 1, nuc % n_precursor
! determine delayed neutron precursor yield for group j
yield = rxn % products(1 + group) % yield % evaluate(E_in)
yield = rxn % product_yield(1 + group, E_in)
! Check if this group is sampled
prob = prob + yield
@ -1600,7 +1595,7 @@ contains
do
! sample from energy/angle distribution -- note that mu has already been
! sampled above and doesn't need to be resampled
call rxn % products(1 + group) % sample(E_in, site % E, mu)
call rxn % product_sample(1 + group, E_in, site % E, mu)
! resample if energy is greater than maximum neutron energy
if (site % E < energy_max(NEUTRON)) exit
@ -1624,7 +1619,7 @@ contains
! sample from prompt neutron energy distribution
n_sample = 0
do
call rxn % products(1) % sample(E_in, site % E, mu)
call rxn % product_sample(1, E_in, site % E, mu)
! resample if energy is greater than maximum neutron energy
if (site % E < energy_max(NEUTRON)) exit
@ -1663,7 +1658,7 @@ contains
E_in = p % E
! sample outgoing energy and scattering cosine
call rxn % products(1) % sample(E_in, E, mu)
call rxn % product_sample(1, E_in, E, mu)
! if scattering system is in center-of-mass, transfer cosine of scattering
! angle and outgoing energy from CM to LAB
@ -1692,7 +1687,7 @@ contains
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu)
! evaluate yield
yield = rxn % products(1) % yield % evaluate(E_in)
yield = rxn % product_yield(1, E_in)
if (mod(yield, ONE) == ZERO) then
! If yield is integral, create exactly that many secondary particles
do i = 1, nint(yield) - 1
@ -1740,8 +1735,8 @@ contains
call sample_photon_product(i_nuclide, p % E, i_reaction, i_product)
! Sample the outgoing energy and angle
call nuclides(i_nuclide) % reactions(i_reaction) % products(i_product) &
% sample(p % E, E, mu)
call nuclides(i_nuclide) % reactions(i_reaction) % &
product_sample(i_product, p % E, E, mu)
! Sample the new direction
uvw = rotate_angle(p % coord(1) % uvw, mu)

View file

@ -95,7 +95,7 @@ contains
id = -1
else
rgb = pl % colors(p % material) % rgb
id = materials(p % material) % id
id = materials(p % material) % id()
end if
end associate
else if (pl % color_by == PLOT_COLOR_CELLS) then

View file

@ -1,153 +0,0 @@
module product_header
use angleenergy_header, only: AngleEnergyContainer
use constants, only: ZERO, MAX_WORD_LEN, EMISSION_PROMPT, EMISSION_DELAYED, &
EMISSION_TOTAL, NEUTRON, PHOTON
use endf_header, only: Tabulated1D, Function1D, Polynomial
use hdf5_interface, only: read_attribute, open_group, close_group, &
open_dataset, close_dataset, read_dataset, HID_T
use random_lcg, only: prn
use secondary_correlated, only: CorrelatedAngleEnergy
use secondary_kalbach, only: KalbachMann
use secondary_nbody, only: NBodyPhaseSpace
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
use string, only: to_str
!===============================================================================
! REACTIONPRODUCT stores a data for a reaction product including its yield and
! angle-energy distributions, each of which has a given probability of occurring
! for a given incoming energy. In general, most products only have one
! angle-energy distribution, but for some cases (e.g., (n,2n) in certain
! nuclides) multiple distinct distributions exist.
!===============================================================================
type :: ReactionProduct
integer :: particle
integer :: emission_mode ! prompt, delayed, or total emission
real(8) :: decay_rate ! Decay rate for delayed neutron precursors
class(Function1D), pointer :: yield => null() ! Energy-dependent neutron yield
type(Tabulated1D), allocatable :: applicability(:)
type(AngleEnergyContainer), allocatable :: distribution(:)
contains
procedure :: sample => reactionproduct_sample
procedure :: from_hdf5 => reactionproduct_from_hdf5
end type ReactionProduct
contains
subroutine reactionproduct_sample(this, E_in, E_out, mu)
class(ReactionProduct), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8), intent(out) :: E_out ! sampled outgoing energy
real(8), intent(out) :: mu ! sampled scattering cosine
integer :: i ! loop counter
integer :: n ! number of angle-energy distributions
real(8) :: prob ! cumulative probability
real(8) :: c ! sampled cumulative probability
n = size(this%applicability)
if (n > 1) then
prob = ZERO
c = prn()
do i = 1, n
! Determine probability that i-th energy distribution is sampled
prob = prob + this % applicability(i) % evaluate(E_in)
! If i-th distribution is sampled, sample energy from the distribution
if (c <= prob) then
call this%distribution(i)%obj%sample(E_in, E_out, mu)
exit
end if
end do
else
! If only one distribution is present, go ahead and sample it
call this%distribution(1)%obj%sample(E_in, E_out, mu)
end if
end subroutine reactionproduct_sample
subroutine reactionproduct_from_hdf5(this, group_id)
class(ReactionProduct), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer :: i
integer :: n
integer(HID_T) :: dgroup
integer(HID_T) :: app
integer(HID_T) :: yield
character(MAX_WORD_LEN) :: temp
! Read particle type
call read_attribute(temp, group_id, 'particle')
select case (temp)
case ('neutron')
this % particle = NEUTRON
case ('photon')
this % particle = PHOTON
end select
! Read emission mode and decay rate
call read_attribute(temp, group_id, 'emission_mode')
select case (temp)
case ('prompt')
this % emission_mode = EMISSION_PROMPT
case ('delayed')
this % emission_mode = EMISSION_DELAYED
case ('total')
this % emission_mode = EMISSION_TOTAL
end select
! Read decay rate for delayed emission
if (this % emission_mode == EMISSION_DELAYED) then
call read_attribute(this % decay_rate, group_id, 'decay_rate')
end if
! Read secondary particle yield
yield = open_dataset(group_id, 'yield')
call read_attribute(temp, yield, 'type')
select case (temp)
case ('Tabulated1D')
allocate(Tabulated1D :: this % yield)
case ('Polynomial')
allocate(Polynomial :: this % yield)
end select
call this % yield % from_hdf5(yield)
call close_dataset(yield)
call read_attribute(n, group_id, 'n_distribution')
allocate(this%applicability(n))
allocate(this%distribution(n))
do i = 1, n
dgroup = open_group(group_id, trim('distribution_' // to_str(i - 1)))
! Read applicability
if (n > 1) then
app = open_dataset(dgroup, 'applicability')
call this%applicability(i)%from_hdf5(app)
call close_dataset(app)
end if
! Read type of distribution and allocate accordingly
call read_attribute(temp, dgroup, 'type')
select case (temp)
case ('uncorrelated')
allocate(UncorrelatedAngleEnergy :: this%distribution(i)%obj)
case ('correlated')
allocate(CorrelatedAngleEnergy :: this%distribution(i)%obj)
case ('nbody')
allocate(NBodyPhaseSpace :: this%distribution(i)%obj)
case ('kalbach-mann')
allocate(KalbachMann :: this%distribution(i)%obj)
end select
! Read distribution data
call this%distribution(i)%obj%from_hdf5(dgroup)
call close_group(dgroup)
end do
end subroutine reactionproduct_from_hdf5
end module product_header

161
src/reaction.cpp Normal file
View file

@ -0,0 +1,161 @@
#include "reaction.h"
#include <string>
#include <utility> // for move
#include "hdf5_interface.h"
#include "endf.h"
#include "random_lcg.h"
#include "secondary_uncorrelated.h"
namespace openmc {
Reaction::Reaction(hid_t group, const std::vector<int>& temperatures)
{
read_attribute(group, "Q_value", q_value_);
read_attribute(group, "mt", mt_);
int cm;
read_attribute(group, "center_of_mass", cm);
scatter_in_cm_ = (cm == 1);
// Read cross section and threshold_idx data
for (auto t : temperatures) {
// Get group corresponding to temperature
std::string temp_str {std::to_string(t) + "K"};
hid_t temp_group = open_group(group, temp_str.c_str());
hid_t dset = open_dataset(temp_group, "xs");
// Get threshold index
TemperatureXS xs;
read_attribute(dset, "threshold_idx", xs.threshold);
// Read cross section values
read_dataset(dset, xs.value);
close_dataset(dset);
close_group(temp_group);
// create new entry in xs vector
xs_.push_back(std::move(xs));
}
// Read products
for (const auto& name : group_names(group)) {
if (name.rfind("product_", 0) == 0) {
hid_t pgroup = open_group(group, name.c_str());
products_.emplace_back(pgroup);
close_group(pgroup);
}
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
// Before the secondary distribution refactor, when the angle/energy
// distribution was uncorrelated, no angle was actually sampled. With
// the refactor, an angle is always sampled for an uncorrelated
// distribution even when no angle distribution exists in the ACE file
// (isotropic is assumed). To preserve the RNG stream, we explicitly
// mark fission reactions so that we avoid the angle sampling.
if (is_fission(mt_)) {
for (auto& p : products_) {
if (p.particle_ == ParticleType::neutron) {
for (auto& d : p.distribution_) {
auto d_ = dynamic_cast<UncorrelatedAngleEnergy*>(d.get());
if (d_) d_->fission() = true;
}
}
}
}
// <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
}
//==============================================================================
// Fortran compatibility functions
//==============================================================================
Reaction* reaction_from_hdf5(hid_t group, int* temperatures, int n)
{
std::vector<int> temps {temperatures, temperatures + n};
return new Reaction{group, temps};
}
void reaction_delete(Reaction* rx) { delete rx; }
int reaction_mt(Reaction* rx) { return rx->mt_; }
double reaction_q_value(Reaction* rx) { return rx->q_value_; }
bool reaction_scatter_in_cm(Reaction* rx) { return rx->scatter_in_cm_; }
double reaction_product_decay_rate(Reaction* rx, int product)
{
return rx->products_[product - 1].decay_rate_;
}
int reaction_product_emission_mode(Reaction* rx, int product)
{
switch (rx->products_[product - 1].emission_mode_) {
case ReactionProduct::EmissionMode::prompt:
return 1;
case ReactionProduct::EmissionMode::delayed:
return 2;
case ReactionProduct::EmissionMode::total:
return 3;
}
}
int reaction_product_particle(Reaction* rx, int product)
{
switch (rx->products_[product - 1].particle_) {
case ParticleType::neutron:
return 1;
case ParticleType::photon:
return 2;
case ParticleType::electron:
return 3;
case ParticleType::positron:
return 4;
}
}
void reaction_product_sample(Reaction* rx, int product, double E_in, double* E_out, double* mu)
{
rx->products_[product - 1].sample(E_in, *E_out, *mu);
}
double reaction_product_yield(Reaction* rx, int product, double E)
{
return (*rx->products_[product - 1].yield_)(E);
}
int reaction_products_size(Reaction* rx) { return rx->products_.size(); }
double reaction_xs(Reaction* rx, int temperature, int energy)
{
return rx->xs_[temperature - 1].value[energy - 1];
}
double reaction_sample_elastic_mu(Reaction* rx, double E)
{
// Get elastic scattering distribution
auto& d = rx->products_[0].distribution_[0];
// Check if it is an uncorrelated angle-energy distribution
auto d_ = dynamic_cast<UncorrelatedAngleEnergy*>(d.get());
if (d_) {
return d_->angle().sample(E);
} else {
return 2.0*prn() - 1.0;
}
}
int reaction_xs_size(Reaction* rx, int temperature)
{
return rx->xs_[temperature - 1].value.size();
}
int reaction_xs_threshold(Reaction* rx, int temperature)
{
return rx->xs_[temperature - 1].threshold;
}
}

65
src/reaction.h Normal file
View file

@ -0,0 +1,65 @@
//! \file reaction.h
//! Data for an incident neutron reaction
#ifndef OPENMC_REACTION_H
#define OPENMC_REACTION_H
#include <vector>
#include "hdf5.h"
#include "reaction_product.h"
namespace openmc {
//==============================================================================
//! Data for a single reaction including cross sections (possibly at multiple
//! temperatures) and reaction products (with secondary angle-energy
//! distributions)
//==============================================================================
class Reaction {
public:
//! Construct reaction from HDF5 data
//! \param[in] group HDF5 group containing reaction data
//! \param[in] temperatures Desired temperatures for cross sections
explicit Reaction(hid_t group, const std::vector<int>& temperatures);
//! Cross section at a single temperature
struct TemperatureXS {
int threshold;
std::vector<double> value;
};
int mt_; //!< ENDF MT value
double q_value_; //!< Reaction Q value in [eV]
bool scatter_in_cm_; //!< scattering system in center-of-mass?
std::vector<TemperatureXS> xs_; //!< Cross section at each temperature
std::vector<ReactionProduct> products_; //!< Reaction products
};
//==============================================================================
// Fortran compatibility functions
//==============================================================================
extern "C" {
Reaction* reaction_from_hdf5(hid_t group, int* temperatures, int n);
void reaction_delete(Reaction* rx);
int reaction_mt(Reaction* rx);
double reaction_q_value(Reaction* rx);
bool reaction_scatter_in_cm(Reaction* rx);
double reaction_product_decay_rate(Reaction* rx, int product);
int reaction_product_emission_mode(Reaction* rx, int product);
int reaction_product_particle(Reaction* rx, int product);
void reaction_product_sample(Reaction* rx, int product, double E_in,
double* E_out, double* mu);
int reaction_products_size(Reaction* rx);
double reaction_product_yield(Reaction* rx, int product, double E);
double reaction_sample_elastic_mu(Reaction* rx, double E);
double reaction_xs(Reaction* xs, int temperature, int energy);
int reaction_xs_size(Reaction* xs, int temperature);
int reaction_xs_threshold(Reaction* xs, int temperature);
}
} // namespace openmc
#endif // OPENMC_REACTION_H

View file

@ -1,83 +1,268 @@
module reaction_header
use, intrinsic :: ISO_C_BINDING
use constants, only: MAX_WORD_LEN
use hdf5_interface
use product_header, only: ReactionProduct
use stl_vector, only: VectorInt
use string, only: to_str, starts_with
implicit none
private
!===============================================================================
! REACTION contains the cross-section and secondary energy and angle
! distributions for a single reaction in a continuous-energy ACE-format table
!===============================================================================
type TemperatureXS
integer :: threshold ! Energy grid index of threshold
real(8), allocatable :: value(:) ! Cross section values
end type TemperatureXS
type Reaction
integer :: MT ! ENDF MT value
real(8) :: Q_value ! Reaction Q value
logical :: scatter_in_cm ! scattering system in center-of-mass?
type(TemperatureXS), allocatable :: xs(:)
type(ReactionProduct), allocatable :: products(:)
type, public :: Reaction
type(C_PTR) :: ptr
integer(C_INT) :: MT ! ENDF MT value
real(C_DOUBLE) :: Q_value ! Reaction Q value
logical(C_BOOL) :: scatter_in_cm ! scattering system in center-of-mass?
contains
procedure :: from_hdf5 => reaction_from_hdf5
procedure :: from_hdf5
procedure :: mt_
procedure :: q_value_
procedure :: scatter_in_cm_
procedure :: product_decay_rate
procedure :: product_emission_mode
procedure :: product_particle
procedure :: product_sample
procedure :: product_yield
procedure :: products_size
procedure :: sample_elastic_mu
procedure :: xs
procedure :: xs_size
procedure :: xs_threshold
end type Reaction
interface
function reaction_from_hdf5(group, temperatures, n) result(ptr) bind(C)
import C_PTR, HID_T, C_INT
integer(HID_T), value :: group
integer(C_INT), intent(in) :: temperatures
integer(C_INT), value :: n
type(C_PTR) :: ptr
end function
function reaction_mt(ptr) result(mt) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT) :: mt
end function
function reaction_q_value(ptr) result(q_value) bind(C)
import C_PTR, C_DOUBLE
type(C_PTR), value :: ptr
real(C_DOUBLE) :: q_value
end function
function reaction_scatter_in_cm(ptr) result(b) bind(C)
import C_PTR, C_BOOL
type(C_PTR), value :: ptr
logical(C_BOOL) :: b
end function
pure function reaction_product_decay_rate(ptr, product) result(rate) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: product
real(C_DOUBLE) :: rate
end function
pure function reaction_product_emission_mode(ptr, product) result(m) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: product
integer(C_INT) :: m
end function
pure function reaction_product_particle(ptr, product) result(particle) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: product
integer(C_INT) :: particle
end function
subroutine reaction_product_sample(ptr, product, E_in, E_out, mu) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: product
real(C_DOUBLE), value :: E_in
real(C_DOUBLE), intent(out) :: E_out
real(C_DOUBLE), intent(out) :: mu
end subroutine
pure function reaction_product_yield(ptr, product, E) result(val) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: product
real(C_DOUBLE), value :: E
real(C_DOUBLE) :: val
end function
pure function reaction_products_size(ptr) result(sz) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT) :: sz
end function
function reaction_sample_elastic_mu(ptr, E) result(mu) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
real(C_DOUBLE), value :: E
real(C_DOUBLE) :: mu
end function
function reaction_xs(ptr, temperature, energy) result(xs) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
integer(C_INT), value :: temperature
integer(C_INT), value :: energy
real(C_DOUBLE) :: xs
end function
function reaction_xs_size(ptr, temperature) result(sz) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: temperature
integer(C_INT) :: sz
end function
function reaction_xs_threshold(ptr, temperature) result(threshold) bind(C)
import C_PTR, C_INT
type(C_PTR), value :: ptr
integer(C_INT), value :: temperature
integer(C_INT) :: threshold
end function
end interface
contains
subroutine reaction_from_hdf5(this, group_id, temperatures)
subroutine from_hdf5(this, group_id, temperatures)
class(Reaction), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
type(VectorInt), intent(in) :: temperatures
integer :: i
integer :: cm
integer :: n_product
integer(HID_T) :: pgroup
integer(HID_T) :: xs, temp_group
integer(HSIZE_T) :: dims(1)
integer(HSIZE_T) :: j
character(MAX_WORD_LEN) :: temp_str ! temperature dataset name, e.g. '294K'
character(MAX_WORD_LEN), allocatable :: grp_names(:)
integer(C_INT) :: dummy
integer(C_INT) :: n
call read_attribute(this % Q_value, group_id, 'Q_value')
call read_attribute(this % MT, group_id, 'mt')
call read_attribute(cm, group_id, 'center_of_mass')
this % scatter_in_cm = (cm == 1)
n = temperatures % size()
if (n > 0) then
this % ptr = reaction_from_hdf5(group_id, temperatures % data(1), n)
else
! In this case, temperatures % data(1) doesn't exist, so we just pass a
! dummy value
this % ptr = reaction_from_hdf5(group_id, dummy, n)
end if
this % MT = reaction_mt(this % ptr)
this % Q_value = reaction_q_value(this % ptr)
this % scatter_in_cm = reaction_scatter_in_cm(this % ptr)
end subroutine from_hdf5
! Read cross section and threshold_idx data
allocate(this % xs(temperatures % size()))
do i = 1, temperatures % size()
temp_str = trim(to_str(temperatures % data(i))) // "K"
temp_group = open_group(group_id, temp_str)
xs = open_dataset(temp_group, 'xs')
call read_attribute(this % xs(i) % threshold, xs, 'threshold_idx')
call get_shape(xs, dims)
allocate(this % xs(i) % value(dims(1)))
call read_dataset(this % xs(i) % value, xs)
call close_dataset(xs)
call close_group(temp_group)
end do
function mt_(this) result(mt)
class(Reaction), intent(in) :: this
integer(C_INT) :: MT
! Determine number of products
n_product = 0
call get_groups(group_id, grp_names)
do j = 1, size(grp_names)
if (starts_with(grp_names(j), "product_")) n_product = n_product + 1
end do
mt = reaction_mt(this % ptr)
end function
! Read products
allocate(this % products(n_product))
do i = 1, n_product
pgroup = open_group(group_id, 'product_' // trim(to_str(i - 1)))
call this % products(i) % from_hdf5(pgroup)
call close_group(pgroup)
end do
end subroutine reaction_from_hdf5
function q_value_(this) result(q_value)
class(Reaction), intent(in) :: this
real(C_DOUBLE) :: q_value
q_value = reaction_q_value(this % ptr)
end function
function scatter_in_cm_(this) result(cm)
class (Reaction), intent(in) :: this
logical(C_BOOL) :: cm
cm = reaction_scatter_in_cm(this % ptr)
end function
pure function product_decay_rate(this, product) result(rate)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: product
real(C_DOUBLE) :: rate
rate = reaction_product_decay_rate(this % ptr, product)
end function
pure function product_emission_mode(this, product) result(m)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: product
integer(C_INT) :: m
m = reaction_product_emission_mode(this % ptr, product)
end function
pure function product_particle(this, product) result(p)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: product
integer(C_INT) :: p
p = reaction_product_particle(this % ptr, product)
end function
subroutine product_sample(this, product, E_in, E_out, mu)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: product
real(C_DOUBLE), intent(in) :: E_in
real(C_DOUBLE), intent(out) :: E_out
real(C_DOUBLE), intent(out) :: mu
call reaction_product_sample(this % ptr, product, E_in, E_out, mu)
end subroutine
pure function product_yield(this, product, E) result(val)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: product
real(C_DOUBLE), intent(in) :: E
real(C_DOUBLE) :: val
val = reaction_product_yield(this % ptr, product, E)
end function
pure function products_size(this) result(sz)
class(Reaction), intent(in) :: this
integer(C_INT) :: sz
sz = reaction_products_size(this % ptr)
end function
function sample_elastic_mu(this, E) result(mu)
class(Reaction), intent(in) :: this
real(C_DOUBLE), intent(in) :: E
real(C_DOUBLE) :: mu
mu = reaction_sample_elastic_mu(this % ptr, E)
end function
function xs(this, temperature, energy) result(val)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: temperature
integer(C_INT), intent(in) :: energy
real(C_DOUBLE) :: val
val = reaction_xs(this % ptr, temperature, energy)
end function
function xs_size(this, temperature) result(sz)
class(Reaction), intent(in) :: this
integer(C_INT) :: temperature
integer(C_INT) :: sz
sz = reaction_xs_size(this % ptr, temperature)
end function
function xs_threshold(this, temperature) result(val)
class(Reaction), intent(in) :: this
integer(C_INT), intent(in) :: temperature
integer(C_INT) :: val
val = reaction_xs_threshold(this % ptr, temperature)
end function
end module reaction_header

107
src/reaction_product.cpp Normal file
View file

@ -0,0 +1,107 @@
#include "reaction_product.h"
#include <memory> // for unique_ptr
#include <string> // for string
#include "hdf5_interface.h"
#include "random_lcg.h"
#include "secondary_correlated.h"
#include "secondary_kalbach.h"
#include "secondary_nbody.h"
#include "secondary_uncorrelated.h"
namespace openmc {
//==============================================================================
// ReactionProduct implementation
//==============================================================================
ReactionProduct::ReactionProduct(hid_t group)
{
// Read particle type
std::string temp;
read_attribute(group, "particle", temp);
if (temp == "neutron") {
particle_ = ParticleType::neutron;
} else if (temp == "photon") {
particle_ = ParticleType::photon;
}
// Read emission mode and decay rate
read_attribute(group, "emission_mode", temp);
if (temp == "prompt") {
emission_mode_ = EmissionMode::prompt;
} else if (temp == "delayed") {
emission_mode_ = EmissionMode::delayed;
} else if (temp == "total") {
emission_mode_ = EmissionMode::total;
}
// Read decay rate for delayed emission
if (emission_mode_ == EmissionMode::delayed)
read_attribute(group, "decay_rate", decay_rate_);
// Read secondary particle yield
hid_t yield = open_dataset(group, "yield");
read_attribute(yield, "type", temp);
if (temp == "Tabulated1D") {
yield_ = std::unique_ptr<Function1D>{new Tabulated1D{yield}};
} else if (temp == "Polynomial") {
yield_ = std::unique_ptr<Function1D>{new Polynomial{yield}};
}
close_dataset(yield);
int n;
read_attribute(group, "n_distribution", n);
for (int i = 0; i < n; ++i) {
std::string s {"distribution_"};
s.append(std::to_string(i));
hid_t dgroup = open_group(group, s.c_str());
// Read applicability
if (n > 1) {
hid_t app = open_dataset(dgroup, "applicability");
applicability_.emplace_back(app);
close_dataset(app);
}
// Determine distribution type and read data
read_attribute(dgroup, "type", temp);
if (temp == "uncorrelated") {
distribution_.emplace_back(new UncorrelatedAngleEnergy{dgroup});
} else if (temp == "correlated") {
distribution_.emplace_back(new CorrelatedAngleEnergy{dgroup});
} else if (temp == "nbody") {
distribution_.emplace_back(new NBodyPhaseSpace{dgroup});
} else if (temp == "kalbach-mann") {
distribution_.emplace_back(new KalbachMann{dgroup});
}
close_group(dgroup);
}
}
void ReactionProduct::sample(double E_in, double& E_out, double& mu) const
{
auto n = applicability_.size();
if (n > 1) {
double prob = 0.0;
double c = prn();
for (int i = 0; i < n; ++i) {
// Determine probability that i-th energy distribution is sampled
prob += applicability_[i](E_in);
// If i-th distribution is sampled, sample energy from the distribution
if (c <= prob) {
distribution_[i]->sample(E_in, E_out, mu);
break;
}
}
} else {
// If only one distribution is present, go ahead and sample it
distribution_[0]->sample(E_in, E_out, mu);
}
}
}

56
src/reaction_product.h Normal file
View file

@ -0,0 +1,56 @@
//! \file reaction_product.h
//! Data for a reaction product
#ifndef OPENMC_REACTION_PRODUCT_H
#define OPENMC_REACTION_PRODUCT_H
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "hdf5.h"
#include "angle_energy.h"
#include "endf.h"
#include "particle.h"
namespace openmc {
//==============================================================================
//! Data for a reaction product including its yield and angle-energy
//! distributions, each of which has a given probability of occurring for a
//! given incoming energy. In general, most products only have one angle-energy
//! distribution, but for some cases (e.g., (n,2n) in certain nuclides) multiple
//! distinct distributions exist.
//==============================================================================
class ReactionProduct {
public:
//! Emission mode for product
enum class EmissionMode {
prompt, // Prompt emission of secondary particle
total, // Delayed emission of secondary particle
delayed // Yield represents total emission (prompt + delayed)
};
using Secondary = std::unique_ptr<AngleEnergy>;
//! Construct reaction product from HDF5 data
//! \param[in] group HDF5 group containing data
explicit ReactionProduct(hid_t group);
//! Sample an outgoing angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
ParticleType particle_; //!< Particle type
EmissionMode emission_mode_; //!< Emission mode
double decay_rate_; //!< Decay rate (for delayed neutron precursors) in [1/s]
std::unique_ptr<Function1D> yield_; //!< Yield as a function of energy
std::vector<Tabulated1D> applicability_; //!< Applicability of distribution
std::vector<Secondary> distribution_; //!< Secondary angle-energy distribution
};
} // namespace opemc
#endif // OPENMC_REACTION_PRODUCT_H

View file

@ -18,6 +18,8 @@ class ScattDataTabular;
//==============================================================================
class ScattData {
public:
virtual ~ScattData() = default;
protected:
//! \brief Initializes the attributes of the base class.
void

23
src/search.h Normal file
View file

@ -0,0 +1,23 @@
//! \file search.h
//! Search algorithms
#ifndef OPENMC_SEARCH_H
#define OPENMC_SEARCH_H
#include <algorithm> // for lower_bound
namespace openmc {
//! Perform binary search
template<class It, class T>
typename std::iterator_traits<It>::difference_type
lower_bound_index(It first, It last, const T& value)
{
It index = std::lower_bound(first, last, value) - 1;
return (index == last) ? -1 : index - first;
}
} // namespace openmc
#endif // OPENMC_SEARCH_H

View file

@ -0,0 +1,244 @@
#include "secondary_correlated.h"
#include <algorithm> // for copy
#include <cmath>
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include "hdf5_interface.h"
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
#include "endf.h"
#include "random_lcg.h"
#include "search.h"
namespace openmc {
//==============================================================================
//! CorrelatedAngleEnergy implementation
//==============================================================================
CorrelatedAngleEnergy::CorrelatedAngleEnergy(hid_t group)
{
// Open incoming energy dataset
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
interpolation_.push_back(int2interp(i));
n_region_ = breakpoints_.size();
// Get incoming energies
read_dataset(dset, energy_);
std::size_t n_energy = energy_.size();
close_dataset(dset);
// Get outgoing energy distribution data
dset = open_dataset(group, "energy_out");
std::vector<int> offsets;
std::vector<int> interp;
std::vector<int> n_discrete;
read_attribute(dset, "offsets", offsets);
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
// Read angle distributions
xt::xarray<double> mu;
read_dataset(group, "mu", mu);
for (int i = 0; i < n_energy; ++i) {
// Determine number of outgoing energies
int j = offsets[i];
int n;
if (i < n_energy - 1) {
n = offsets[i+1] - j;
} else {
n = eout.shape()[1] - j;
}
// Assign interpolation scheme and number of discrete lines
CorrTable d;
d.interpolation = int2interp(interp[i]);
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j+n));
d.p = xt::view(eout, 1, xt::range(j, j+n));
d.c = xt::view(eout, 2, xt::range(j, j+n));
// To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF
if (false) {
// Calculate cumulative distribution function -- discrete portion
for (int k = 0; k < d.n_discrete; ++k) {
if (k == 0) {
d.c[k] = d.p[k];
} else {
d.c[k] = d.c[k-1] + d.p[k];
}
}
// Continuous portion
for (int k = d.n_discrete; k < n; ++k) {
if (k == d.n_discrete) {
d.c[k] = d.c[k-1] + d.p[k];
} else {
if (d.interpolation == Interpolation::histogram) {
d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]);
} else if (d.interpolation == Interpolation::lin_lin) {
d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) *
(d.e_out[k] - d.e_out[k-1]);
}
}
}
// Normalize density and distribution functions
d.p /= d.c[n - 1];
d.c /= d.c[n - 1];
}
for (j = 0; j < n; ++j) {
// Get interpolation scheme
int interp_mu = std::lround(eout(3, offsets[i] + j));
// Determine offset and size of distribution
int offset_mu = std::lround(eout(4, offsets[i] + j));
int m;
if (offsets[i] + j + 1 < eout.shape()[1]) {
m = std::lround(eout(4, offsets[i]+j+1)) - offset_mu;
} else {
m = mu.shape()[1] - offset_mu;
}
auto interp = int2interp(interp_mu);
auto xs = xt::view(mu, 0, xt::range(offset_mu, offset_mu + m));
auto ps = xt::view(mu, 1, xt::range(offset_mu, offset_mu + m));
auto cs = xt::view(mu, 2, xt::range(offset_mu, offset_mu + m));
std::vector<double> x {xs.begin(), xs.end()};
std::vector<double> p {ps.begin(), ps.end()};
std::vector<double> c {cs.begin(), cs.end()};
// To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF
Tabular* mudist = new Tabular{x.data(), p.data(), m, interp, c.data()};
d.angle.emplace_back(mudist);
} // outgoing energies
distribution_.push_back(std::move(d));
} // incoming energies
}
void CorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
{
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Before the secondary distribution refactor, an isotropic polar cosine was
// always sampled but then overwritten with the polar cosine sampled from the
// correlated distribution. To preserve the random number stream, we keep
// this dummy sampling here but can remove it later (will change answers)
mu = 2.0*prn() - 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Find energy bin and calculate interpolation factor -- if the energy is
// outside the range of the tabulated energies, choose the first or last bins
auto n_energy_in = energy_.size();
int i;
double r;
if (E_in < energy_[0]) {
i = 0;
r = 0.0;
} else if (E_in > energy_[n_energy_in - 1]) {
i = n_energy_in - 2;
r = 1.0;
} else {
i = lower_bound_index(energy_.begin(), energy_.end(), E_in);
r = (E_in - energy_[i]) / (energy_[i+1] - energy_[i]);
}
// Sample between the ith and [i+1]th bin
int l = r > prn() ? i + 1 : i;
// Interpolation for energy E1 and EK
int n_energy_out = distribution_[i].e_out.size();
double E_i_1 = distribution_[i].e_out[0];
double E_i_K = distribution_[i].e_out[n_energy_out - 1];
n_energy_out = distribution_[i+1].e_out.size();
double E_i1_1 = distribution_[i+1].e_out[0];
double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1];
double E_1 = E_i_1 + r*(E_i1_1 - E_i_1);
double E_K = E_i_K + r*(E_i1_K - E_i_K);
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
double r1 = prn();
double c_k = distribution_[l].c[0];
double c_k1;
int k;
for (k = 0; k < n_energy_out - 2; ++k) {
c_k1 = distribution_[l].c[k+1];
if (r1 < c_k1) break;
c_k = c_k1;
}
// Check to make sure 1 <= k <= NP - 1
k = std::max(0, std::min(k, n_energy_out - 2));
double E_l_k = distribution_[l].e_out[k];
double p_l_k = distribution_[l].p[k];
if (distribution_[l].interpolation == Interpolation::histogram) {
// Histogram interpolation
if (p_l_k > 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k;
}
} else if (distribution_[l].interpolation == Interpolation::lin_lin) {
// Linear-linear interpolation
double E_l_k1 = distribution_[l].e_out[k+1];
double p_l_k1 = distribution_[l].p[k+1];
double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k);
if (frac == 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k +
2.0*frac*(r1 - c_k))) - p_l_k)/frac;
}
}
// Now interpolate between incident energy bins i and i + 1
if (l == i) {
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1);
} else {
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1);
}
// Find correlated angular distribution for closest outgoing energy bin
if (r1 - c_k < c_k1 - r1) {
mu = distribution_[l].angle[k]->sample();
} else {
mu = distribution_[l].angle[k + 1]->sample();
}
}
} // namespace openmc

View file

@ -0,0 +1,52 @@
//! \file secondary_correlated.h
//! Correlated angle-energy distribution
#ifndef OPENMC_SECONDARY_CORRELATED_H
#define OPENMC_SECONDARY_CORRELATED_H
#include <vector>
#include "hdf5.h"
#include "xtensor/xtensor.hpp"
#include "angle_energy.h"
#include "endf.h"
#include "distribution.h"
namespace openmc {
//==============================================================================
//! Correlated angle-energy distribution corresponding to ACE law 61 and ENDF
//! File 6, LAW=1, LANG!=2.
//==============================================================================
class CorrelatedAngleEnergy : public AngleEnergy {
public:
explicit CorrelatedAngleEnergy(hid_t group);
//! Sample distribution for an angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
private:
//! Outgoing energy/angle at a single incoming energy
struct CorrTable {
int n_discrete; //!< Number of discrete lines
Interpolation interpolation; //!< Interpolation law
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV]
xt::xtensor<double, 1> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution
std::vector<UPtrDist> angle; //!< Angle distribution
};
int n_region_; //!< Number of interpolation regions
std::vector<int> breakpoints_; //!< Breakpoints between regions
std::vector<Interpolation> interpolation_; //!< Interpolation laws
std::vector<double> energy_; //!< Energies [eV] at which distributions
//!< are tabulated
std::vector<CorrTable> distribution_; //!< Distribution at each energy
};
} // namespace openmc
#endif // OPENMC_SECONDARY_CORRELATED_H

View file

@ -1,278 +0,0 @@
module secondary_kalbach
use algorithm, only: binary_search
use angleenergy_header, only: AngleEnergy
use constants, only: ZERO, HALF, ONE, TWO, HISTOGRAM, LINEAR_LINEAR
use hdf5_interface
use random_lcg, only: prn
!===============================================================================
! KalbachMann represents a correlated angle-energy distribution with the angular
! distribution represented using Kalbach-Mann systematics. This corresponds to
! ACE law 44 and ENDF File 6, LAW=1, LANG=2.
!===============================================================================
type KalbachMannTable
integer :: n_discrete
integer :: interpolation
real(8), allocatable :: e_out(:)
real(8), allocatable :: p(:)
real(8), allocatable :: c(:)
real(8), allocatable :: r(:)
real(8), allocatable :: a(:)
end type KalbachMannTable
type, extends(AngleEnergy) :: KalbachMann
integer :: n_region ! number of interpolation regions
integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions
integer, allocatable :: interpolation(:) ! interpolation region codes
real(8), allocatable :: energy(:) ! incoming energies
type(KalbachMannTable), allocatable :: distribution(:) ! outgoing E/mu parameters
contains
procedure :: sample => kalbachmann_sample
procedure :: from_hdf5 => kalbachmann_from_hdf5
end type KalbachMann
contains
subroutine kalbachmann_sample(this, E_in, E_out, mu)
class(KalbachMann), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8), intent(out) :: E_out ! sampled outgoing energy
real(8), intent(out) :: mu ! sampled scattering cosine
integer :: i, k, l ! indices
integer :: n_energy_in ! number of incoming energies
integer :: n_energy_out ! number of outgoing energies
real(8) :: r ! interpolation factor on incoming energy
real(8) :: r1 ! random number on [0,1)
real(8) :: frac ! interpolation factor on outgoing energy
real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i
real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1
real(8) :: E_1, E_K ! endpoints interpolated between i and i+1
real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l
real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l
real(8) :: c_k, c_k1 ! cumulative probability
real(8) :: km_r, km_a ! Kalbach-Mann parameters
real(8) :: T
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
! Before the secondary distribution refactor, an isotropic polar cosine was
! always sampled but then overwritten with the polar cosine sampled from the
! correlated distribution. To preserve the random number stream, we keep
! this dummy sampling here but can remove it later (will change answers)
mu = TWO*prn() - ONE
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
! find energy bin and calculate interpolation factor -- if the energy is
! outside the range of the tabulated energies, choose the first or last bins
n_energy_in = size(this%energy)
if (E_in < this%energy(1)) then
i = 1
r = ZERO
elseif (E_in > this%energy(n_energy_in)) then
i = n_energy_in - 1
r = ONE
else
i = binary_search(this%energy, n_energy_in, E_in)
r = (E_in - this%energy(i)) / &
(this%energy(i+1) - this%energy(i))
end if
! Sample between the ith and (i+1)th bin
if (r > prn()) then
l = i + 1
else
l = i
end if
! interpolation for energy E1 and EK
n_energy_out = size(this%distribution(i)%e_out)
E_i_1 = this%distribution(i)%e_out(1)
E_i_K = this%distribution(i)%e_out(n_energy_out)
n_energy_out = size(this%distribution(i+1)%e_out)
E_i1_1 = this%distribution(i+1)%e_out(1)
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
E_K = E_i_K + r*(E_i1_K - E_i_K)
! determine outgoing energy bin
n_energy_out = size(this%distribution(l)%e_out)
r1 = prn()
c_k = this%distribution(l)%c(1)
do k = 1, n_energy_out - 1
c_k1 = this%distribution(l)%c(k+1)
if (r1 < c_k1) exit
c_k = c_k1
end do
! check to make sure k is <= NP - 1
k = min(k, n_energy_out - 1)
E_l_k = this%distribution(l)%e_out(k)
p_l_k = this%distribution(l)%p(k)
if (this%distribution(l)%interpolation == HISTOGRAM) then
! Histogram interpolation
if (p_l_k > ZERO) then
E_out = E_l_k + (r1 - c_k)/p_l_k
else
E_out = E_l_k
end if
! Determine Kalbach-Mann parameters
km_r = this%distribution(l)%r(k)
km_a = this%distribution(l)%a(k)
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
! Linear-linear interpolation
E_l_k1 = this%distribution(l)%e_out(k+1)
p_l_k1 = this%distribution(l)%p(k+1)
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
if (frac == ZERO) then
E_out = E_l_k + (r1 - c_k)/p_l_k
else
E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + &
TWO*frac*(r1 - c_k))) - p_l_k)/frac
end if
! Determine Kalbach-Mann parameters
km_r = this%distribution(l)%r(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
(this%distribution(l)%r(k+1) - this%distribution(l)%r(k))
km_a = this%distribution(l)%a(k) + (E_out - E_l_k)/(E_l_k1 - E_l_k) * &
(this%distribution(l)%a(k+1) - this%distribution(l)%a(k))
end if
! Now interpolate between incident energy bins i and i + 1
if (l == i) then
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1)
else
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1)
end if
! Sampled correlated angle from Kalbach-Mann parameters
if (prn() > km_r) then
T = (TWO*prn() - ONE) * sinh(km_a)
mu = log(T + sqrt(T*T + ONE))/km_a
else
r1 = prn()
mu = log(r1*exp(km_a) + (ONE - r1)*exp(-km_a))/km_a
end if
end subroutine kalbachmann_sample
subroutine kalbachmann_from_hdf5(this, group_id)
class(KalbachMann), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer :: i, j, k
integer :: n
integer :: n_energy
integer(HID_T) :: dset_id
integer(HSIZE_T) :: dims(1), dims2(2)
integer, allocatable :: temp(:,:)
integer, allocatable :: offsets(:)
integer, allocatable :: interp(:)
integer, allocatable :: n_discrete(:)
real(8), allocatable :: eout(:,:)
! Open incoming energy dataset
dset_id = open_dataset(group_id, 'energy')
! Get interpolation parameters
call read_attribute(temp, dset_id, 'interpolation')
allocate(this%breakpoints(size(temp, 1)))
allocate(this%interpolation(size(temp, 1)))
this%breakpoints(:) = temp(:, 1)
this%interpolation(:) = temp(:, 2)
this%n_region = size(this%breakpoints)
! Get incoming energies
call get_shape(dset_id, dims)
n_energy = int(dims(1), 4)
allocate(this%energy(n_energy))
allocate(this%distribution(n_energy))
call read_dataset(this%energy, dset_id)
call close_dataset(dset_id)
! Get outgoing energy distribution data
dset_id = open_dataset(group_id, 'distribution')
call read_attribute(offsets, dset_id, 'offsets')
call read_attribute(interp, dset_id, 'interpolation')
call read_attribute(n_discrete, dset_id, 'n_discrete_lines')
call get_shape(dset_id, dims2)
allocate(eout(dims2(1), dims2(2)))
call read_dataset(eout, dset_id)
call close_dataset(dset_id)
do i = 1, n_energy
! Determine number of outgoing energies
j = offsets(i)
if (i < n_energy) then
n = offsets(i+1) - j
else
n = size(eout, 1) - j
end if
associate (d => this%distribution(i))
! Assign interpolation scheme and number of discrete lines
d % interpolation = interp(i)
d % n_discrete = n_discrete(i)
! Allocate arrays for energies and PDF/CDF
allocate(d % e_out(n))
allocate(d % p(n))
allocate(d % c(n))
allocate(d % r(n))
allocate(d % a(n))
! Copy data
d % e_out(:) = eout(j+1:j+n, 1)
d % p(:) = eout(j+1:j+n, 2)
d % c(:) = eout(j+1:j+n, 3)
d % r(:) = eout(j+1:j+n, 4)
d % a(:) = eout(j+1:j+n, 5)
! To get answers that match ACE data, for now we still use the tabulated
! CDF values that were passed through to the HDF5 library. At a later
! time, we can remove the CDF values from the HDF5 library and
! reconstruct them using the PDF
if (.false.) then
! Calculate cumulative distribution function -- discrete portion
do k = 1, d % n_discrete
if (k == 1) then
d % c(k) = d % p(k)
else
d % c(k) = d % c(k-1) + d % p(k)
end if
end do
! Continuous portion
do k = d % n_discrete + 1, n
if (k == d % n_discrete + 1) then
d % c(k) = sum(d % p(1:d % n_discrete))
else
if (d % interpolation == HISTOGRAM) then
d % c(k) = d % c(k-1) + d % p(k-1) * &
(d % e_out(k) - d % e_out(k-1))
elseif (d % interpolation == LINEAR_LINEAR) then
d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * &
(d % e_out(k) - d % e_out(k-1))
end if
end if
end do
! Normalize density and distribution functions
d % p(:) = d % p(:)/d % c(n)
d % c(:) = d % c(:)/d % c(n)
end if
end associate
j = j + n
end do
end subroutine kalbachmann_from_hdf5
end module secondary_kalbach

223
src/secondary_kalbach.cpp Normal file
View file

@ -0,0 +1,223 @@
#include "secondary_kalbach.h"
#include <algorithm> // for copy, move
#include <cmath> // for log, sqrt, sinh
#include <cstddef> // for size_t
#include <iterator> // for back_inserter
#include <vector>
#include "xtensor/xarray.hpp"
#include "xtensor/xview.hpp"
#include "hdf5_interface.h"
#include "random_lcg.h"
#include "search.h"
namespace openmc {
//==============================================================================
//! KalbachMann implementation
//==============================================================================
KalbachMann::KalbachMann(hid_t group)
{
// Open incoming energy dataset
hid_t dset = open_dataset(group, "energy");
// Get interpolation parameters
xt::xarray<int> temp;
read_attribute(dset, "interpolation", temp);
auto temp_b = xt::view(temp, 0); // view of breakpoints
auto temp_i = xt::view(temp, 1); // view of interpolation parameters
std::copy(temp_b.begin(), temp_b.end(), std::back_inserter(breakpoints_));
for (const auto i : temp_i)
interpolation_.push_back(int2interp(i));
n_region_ = breakpoints_.size();
// Get incoming energies
read_dataset(dset, energy_);
std::size_t n_energy = energy_.size();
close_dataset(dset);
// Get outgoing energy distribution data
dset = open_dataset(group, "distribution");
std::vector<int> offsets;
std::vector<int> interp;
std::vector<int> n_discrete;
read_attribute(dset, "offsets", offsets);
read_attribute(dset, "interpolation", interp);
read_attribute(dset, "n_discrete_lines", n_discrete);
xt::xarray<double> eout;
read_dataset(dset, eout);
close_dataset(dset);
for (int i = 0; i < n_energy; ++i) {
// Determine number of outgoing energies
int j = offsets[i];
int n;
if (i < n_energy - 1) {
n = offsets[i+1] - j;
} else {
n = eout.shape()[1] - j;
}
// Assign interpolation scheme and number of discrete lines
KMTable d;
d.interpolation = int2interp(interp[i]);
d.n_discrete = n_discrete[i];
// Copy data
d.e_out = xt::view(eout, 0, xt::range(j, j+n));
d.p = xt::view(eout, 1, xt::range(j, j+n));
d.c = xt::view(eout, 2, xt::range(j, j+n));
d.r = xt::view(eout, 3, xt::range(j, j+n));
d.a = xt::view(eout, 4, xt::range(j, j+n));
// To get answers that match ACE data, for now we still use the tabulated
// CDF values that were passed through to the HDF5 library. At a later
// time, we can remove the CDF values from the HDF5 library and
// reconstruct them using the PDF
if (false) {
// Calculate cumulative distribution function -- discrete portion
for (int k = 0; k < d.n_discrete; ++k) {
if (k == 0) {
d.c[k] = d.p[k];
} else {
d.c[k] = d.c[k-1] + d.p[k];
}
}
// Continuous portion
for (int k = d.n_discrete; k < n; ++k) {
if (k == d.n_discrete) {
d.c[k] = d.c[k-1] + d.p[k];
} else {
if (d.interpolation == Interpolation::histogram) {
d.c[k] = d.c[k-1] + d.p[k-1]*(d.e_out[k] - d.e_out[k-1]);
} else if (d.interpolation == Interpolation::lin_lin) {
d.c[k] = d.c[k-1] + 0.5*(d.p[k-1] + d.p[k]) *
(d.e_out[k] - d.e_out[k-1]);
}
}
}
// Normalize density and distribution functions
d.p /= d.c[n - 1];
d.c /= d.c[n - 1];
}
distribution_.push_back(std::move(d));
} // incoming energies
}
void KalbachMann::sample(double E_in, double& E_out, double& mu) const
{
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Before the secondary distribution refactor, an isotropic polar cosine was
// always sampled but then overwritten with the polar cosine sampled from the
// correlated distribution. To preserve the random number stream, we keep
// this dummy sampling here but can remove it later (will change answers)
mu = 2.0*prn() - 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Find energy bin and calculate interpolation factor -- if the energy is
// outside the range of the tabulated energies, choose the first or last bins
auto n_energy_in = energy_.size();
int i;
double r;
if (E_in < energy_[0]) {
i = 0;
r = 0.0;
} else if (E_in > energy_[n_energy_in - 1]) {
i = n_energy_in - 2;
r = 1.0;
} else {
i = lower_bound_index(energy_.begin(), energy_.end(), E_in);
r = (E_in - energy_[i]) / (energy_[i+1] - energy_[i]);
}
// Sample between the ith and [i+1]th bin
int l = r > prn() ? i + 1 : i;
// Interpolation for energy E1 and EK
int n_energy_out = distribution_[i].e_out.size();
double E_i_1 = distribution_[i].e_out[0];
double E_i_K = distribution_[i].e_out[n_energy_out - 1];
n_energy_out = distribution_[i+1].e_out.size();
double E_i1_1 = distribution_[i+1].e_out[0];
double E_i1_K = distribution_[i+1].e_out[n_energy_out - 1];
double E_1 = E_i_1 + r*(E_i1_1 - E_i_1);
double E_K = E_i_K + r*(E_i1_K - E_i_K);
// Determine outgoing energy bin
n_energy_out = distribution_[l].e_out.size();
double r1 = prn();
double c_k = distribution_[l].c[0];
double c_k1;
int k;
for (k = 0; k < n_energy_out - 2; ++k) {
c_k1 = distribution_[l].c[k+1];
if (r1 < c_k1) break;
c_k = c_k1;
}
// Check to make sure 1 <= k <= NP - 1
k = std::max(0, std::min(k, n_energy_out - 2));
double E_l_k = distribution_[l].e_out[k];
double p_l_k = distribution_[l].p[k];
double km_r, km_a;
if (distribution_[l].interpolation == Interpolation::histogram) {
// Histogram interpolation
if (p_l_k > 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k;
}
// Determine Kalbach-Mann parameters
km_r = distribution_[l].r[k];
km_a = distribution_[l].a[k];
} else if (distribution_[l].interpolation == Interpolation::lin_lin) {
// Linear-linear interpolation
double E_l_k1 = distribution_[l].e_out[k+1];
double p_l_k1 = distribution_[l].p[k+1];
double frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k);
if (frac == 0.0) {
E_out = E_l_k + (r1 - c_k)/p_l_k;
} else {
E_out = E_l_k + (std::sqrt(std::max(0.0, p_l_k*p_l_k +
2.0*frac*(r1 - c_k))) - p_l_k)/frac;
}
// Determine Kalbach-Mann parameters
km_r = distribution_[l].r[k] + (E_out - E_l_k)/(E_l_k1 - E_l_k) *
(distribution_[l].r[k+1] - distribution_[l].r[k]);
km_a = distribution_[l].a[k] + (E_out - E_l_k)/(E_l_k1 - E_l_k) *
(distribution_[l].a[k+1] - distribution_[l].a[k]);
}
// Now interpolate between incident energy bins i and i + 1
if (l == i) {
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1);
} else {
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1);
}
// Sampled correlated angle from Kalbach-Mann parameters
if (prn() > km_r) {
double T = (2.0*prn() - 1.0) * std::sinh(km_a);
mu = std::log(T + std::sqrt(T*T + 1.0))/km_a;
} else {
double r1 = prn();
mu = std::log(r1*std::exp(km_a) + (1.0 - r1)*std::exp(-km_a))/km_a;
}
}
}

54
src/secondary_kalbach.h Normal file
View file

@ -0,0 +1,54 @@
//! \file secondary_kalbach.h
//! Kalbach-Mann angle-energy distribution
#ifndef OPENMC_SECONDARY_KALBACH_H
#define OPENMC_SECONDARY_KALBACH_H
#include <vector>
#include "hdf5.h"
#include "xtensor/xtensor.hpp"
#include "angle_energy.h"
#include "constants.h"
#include "endf.h"
namespace openmc {
//==============================================================================
//! Correlated angle-energy distribution with the angular distribution
//! represented using Kalbach-Mann systematics. This corresponds to ACE law 44
//! and ENDF File 6, LAW=1, LANG=2.
//==============================================================================
class KalbachMann : public AngleEnergy {
public:
explicit KalbachMann(hid_t group);
//! Sample distribution for an angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
private:
//! Outgoing energy/angle at a single incoming energy
struct KMTable {
int n_discrete; //!< Number of discrete lines
Interpolation interpolation; //!< Interpolation law
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV]
xt::xtensor<double, 1> p; //!< Probability density
xt::xtensor<double, 1> c; //!< Cumulative distribution
xt::xtensor<double, 1> r; //!< Pre-compound fraction
xt::xtensor<double, 1> a; //!< Parameterized function
};
int n_region_; //!< Number of interpolation regions
std::vector<int> breakpoints_; //!< Breakpoints between regions
std::vector<Interpolation> interpolation_; //!< Interpolation laws
std::vector<double> energy_; //!< Energies [eV] at which distributions
//!< are tabulated
std::vector<KMTable> distribution_; //!< Distribution at each energy
};
} // namespace openmc
#endif // OPENMC_SECONDARY_KALBACH_H

View file

@ -1,82 +0,0 @@
module secondary_nbody
use angleenergy_header, only: AngleEnergy
use constants, only: ONE, TWO, PI
use hdf5_interface, only: read_attribute, HID_T
use math, only: maxwell_spectrum
use random_lcg, only: prn
!===============================================================================
! NBODYPHASESPACE gives the energy distribution for particles emitted from
! neutron and charged-particle reactions. This corresponds to ACE law 66 and
! ENDF File 6, LAW=6.
!===============================================================================
type, extends(AngleEnergy) :: NBodyPhaseSpace
integer :: n_bodies
real(8) :: mass_ratio
real(8) :: A
real(8) :: Q
contains
procedure :: sample => nbody_sample
procedure :: from_hdf5 => nbody_from_hdf5
end type NBodyPhaseSpace
contains
subroutine nbody_sample(this, E_in, E_out, mu)
class(NBodyPhaseSpace), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8), intent(out) :: E_out ! sampled outgoing energy
real(8), intent(out) :: mu ! sampled outgoing energy
real(8) :: Ap ! total mass of particles in neutron masses
real(8) :: E_max ! maximum possible COM energy
real(8) :: x, y, v
real(8) :: r1, r2, r3, r4, r5, r6
! By definition, the distribution of the angle is isotropic for an N-body
! phase space distribution
mu = TWO*prn() - ONE
! Determine E_max parameter
Ap = this%mass_ratio
E_max = (Ap - ONE)/Ap * (this%A/(this%A + ONE)*E_in + this%Q)
! x is essentially a Maxwellian distribution
x = maxwell_spectrum(ONE)
select case (this%n_bodies)
case (3)
y = maxwell_spectrum(ONE)
case (4)
r1 = prn()
r2 = prn()
r3 = prn()
y = -log(r1*r2*r3)
case (5)
r1 = prn()
r2 = prn()
r3 = prn()
r4 = prn()
r5 = prn()
r6 = prn()
y = -log(r1*r2*r3*r4) - log(r5) * cos(PI/TWO*r6)**2
end select
! Now determine v and E_out
v = x/(x+y)
E_out = E_max * v
end subroutine nbody_sample
subroutine nbody_from_hdf5(this, group_id)
class(NBodyPhaseSpace), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
call read_attribute(this%mass_ratio, group_id, 'total_mass')
call read_attribute(this%n_bodies, group_id, 'n_particles')
call read_attribute(this%A, group_id, 'atomic_weight_ratio')
call read_attribute(this%Q, group_id, 'q_value')
end subroutine nbody_from_hdf5
end module secondary_nbody

65
src/secondary_nbody.cpp Normal file
View file

@ -0,0 +1,65 @@
#include "secondary_nbody.h"
#include <cmath> // for log
#include "constants.h"
#include "hdf5_interface.h"
#include "math_functions.h"
#include "random_lcg.h"
namespace openmc {
//==============================================================================
// NBodyPhaseSpace implementation
//==============================================================================
NBodyPhaseSpace::NBodyPhaseSpace(hid_t group)
{
read_attribute(group, "n_particles", n_bodies_);
read_attribute(group, "total_mass", mass_ratio_);
read_attribute(group, "atomic_weight_ratio", A_);
read_attribute(group, "q_value", Q_);
}
void NBodyPhaseSpace::sample(double E_in, double& E_out, double& mu) const
{
// By definition, the distribution of the angle is isotropic for an N-body
// phase space distribution
mu = 2.0*prn() - 1.0;
// Determine E_max parameter
double Ap = mass_ratio_;
double E_max = (Ap - 1.0)/Ap * (A_/(A_ + 1.0)*E_in + Q_);
// x is essentially a Maxwellian distribution
double x = maxwell_spectrum_c(1.0);
double y;
double r1, r2, r3, r4, r5, r6;
switch (n_bodies_) {
case 3:
y = maxwell_spectrum_c(1.0);
break;
case 4:
r1 = prn();
r2 = prn();
r3 = prn();
y = -std::log(r1*r2*r3);
break;
case 5:
r1 = prn();
r2 = prn();
r3 = prn();
r4 = prn();
r5 = prn();
r6 = prn();
y = -std::log(r1*r2*r3*r4) - std::log(r5) * std::pow(std::cos(PI/2.0*r6), 2);
break;
}
// Now determine v and E_out
double v = x/(x + y);
E_out = E_max * v;
}
} // namespace openmc

37
src/secondary_nbody.h Normal file
View file

@ -0,0 +1,37 @@
//! \file secondary_nbody.h
//! N-body phase space distribution
#ifndef OPENMC_SECONDARY_NBODY_H
#define OPENMC_SECONDARY_NBODY_H
#include "hdf5.h"
#include "angle_energy.h"
namespace openmc {
//==============================================================================
//! Angle-energy distribution for particles emitted from neutron and
//! charged-particle reactions. This corresponds to ACE law 66 and ENDF File 6,
//! LAW=6.
//==============================================================================
class NBodyPhaseSpace : public AngleEnergy {
public:
explicit NBodyPhaseSpace(hid_t group);
//! Sample distribution for an angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
private:
int n_bodies_; //!< Number of particles distributed
double mass_ratio_; //!< Total mass of particles [neutron mass]
double A_; //!< Atomic weight ratio
double Q_; //!< Reaction Q-value [eV]
};
} // namespace openmc
#endif // OPENMC_SECONDARY_NBODY_H

View file

@ -1,98 +0,0 @@
module secondary_uncorrelated
use angle_distribution, only: AngleDistribution
use angleenergy_header, only: AngleEnergy
use constants, only: ONE, TWO, MAX_WORD_LEN
use energy_distribution, only: EnergyDistribution, LevelInelastic, &
ContinuousTabular, MaxwellEnergy, Evaporation, WattEnergy, DiscretePhoton
use error, only: warning
use hdf5_interface, only: read_attribute, open_group, close_group, &
object_exists, HID_T
use random_lcg, only: prn
!===============================================================================
! UNCORRELATEDANGLEENERGY represents an uncorrelated angle-energy
! distribution. This corresponds to when an energy distribution is given in ENDF
! File 5/6 and an angular distribution is given in ENDF File 4.
!===============================================================================
type, extends(AngleEnergy) :: UncorrelatedAngleEnergy
logical :: fission = .false.
type(AngleDistribution) :: angle
class(EnergyDistribution), allocatable :: energy
contains
procedure :: sample => uncorrelated_sample
procedure :: from_hdf5 => uncorrelated_from_hdf5
end type UncorrelatedAngleEnergy
contains
subroutine uncorrelated_sample(this, E_in, E_out, mu)
class(UncorrelatedAngleEnergy), intent(in) :: this
real(8), intent(in) :: E_in ! incoming energy
real(8), intent(out) :: E_out ! sampled outgoing energy
real(8), intent(out) :: mu ! sampled scattering cosine
! Sample cosine of scattering angle
if (this%fission) then
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
! For fission, the angle is not used, so just assign a dummy value
mu = ONE
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
elseif (allocated(this%angle%energy)) then
mu = this%angle%sample(E_in)
else
! no angle distribution given => assume isotropic for all energies
mu = TWO*prn() - ONE
end if
! Sample outgoing energy
E_out = this%energy%sample(E_in)
end subroutine uncorrelated_sample
subroutine uncorrelated_from_hdf5(this, group_id)
class(UncorrelatedAngleEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer(HID_T) :: energy_group
integer(HID_T) :: angle_group
character(MAX_WORD_LEN) :: type
! Check if angle group is present & read
if (object_exists(group_id, 'angle')) then
angle_group = open_group(group_id, 'angle')
call this%angle%from_hdf5(angle_group)
call close_group(angle_group)
end if
! Check if energy group is present & read
if (object_exists(group_id, 'energy')) then
energy_group = open_group(group_id, 'energy')
call read_attribute(type, energy_group, 'type')
select case (type)
case ('discrete_photon')
allocate(DiscretePhoton :: this%energy)
case ('level')
allocate(LevelInelastic :: this%energy)
case ('continuous')
allocate(ContinuousTabular :: this%energy)
case ('maxwell')
allocate(MaxwellEnergy :: this%energy)
case ('evaporation')
allocate(Evaporation :: this%energy)
case ('watt')
allocate(WattEnergy :: this%energy)
case default
call warning("Energy distribution type '" // trim(type) &
// "' not implemented.")
end select
if (allocated(this % energy)) then
call this%energy%from_hdf5(energy_group)
end if
call close_group(energy_group)
end if
end subroutine uncorrelated_from_hdf5
end module secondary_uncorrelated

View file

@ -0,0 +1,74 @@
#include "secondary_uncorrelated.h"
#include <sstream> // for stringstream
#include <string> // for string
#include "error.h"
#include "hdf5_interface.h"
#include "random_lcg.h"
namespace openmc {
//==============================================================================
// UncorrelatedAngleEnergy implementation
//==============================================================================
UncorrelatedAngleEnergy::UncorrelatedAngleEnergy(hid_t group)
{
// Check if angle group is present & read
if (object_exists(group, "angle")) {
hid_t angle_group = open_group(group, "angle");
angle_ = AngleDistribution{angle_group};
close_group(angle_group);
}
// Check if energy group is present & read
if (object_exists(group, "energy")) {
hid_t energy_group = open_group(group, "energy");
std::string type;
read_attribute(energy_group, "type", type);
using UPtrEDist = std::unique_ptr<EnergyDistribution>;
if (type == "discrete_photon") {
energy_ = UPtrEDist{new DiscretePhoton{energy_group}};
} else if (type == "level") {
energy_ = UPtrEDist{new LevelInelastic{energy_group}};
} else if (type == "continuous") {
energy_ = UPtrEDist{new ContinuousTabular{energy_group}};
} else if (type == "maxwell") {
energy_ = UPtrEDist{new MaxwellEnergy{energy_group}};
} else if (type == "evaporation") {
energy_ = UPtrEDist{new Evaporation{energy_group}};
} else if (type == "watt") {
energy_ = UPtrEDist{new WattEnergy{energy_group}};
} else {
std::stringstream msg;
msg << "Energy distribution type '" << type << "' not implemented.";
warning(msg);
}
close_group(energy_group);
}
}
void
UncorrelatedAngleEnergy::sample(double E_in, double& E_out, double& mu) const
{
// Sample cosine of scattering angle
if (fission_) {
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// For fission, the angle is not used, so just assign a dummy value
mu = 1.0;
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
} else if (!angle_.empty()) {
mu = angle_.sample(E_in);
} else {
// no angle distribution given => assume isotropic for all energies
mu = 2.0*prn() - 1.0;
}
// Sample outgoing energy
E_out = energy_->sample(E_in);
}
} // namespace openmc

View file

@ -0,0 +1,44 @@
//! \file secondary_uncorrelated.h
//! Uncorrelated angle-energy distribution
#ifndef OPENMC_SECONDARY_UNCORRELATED_H
#define OPENMC_SECONDARY_UNCORRELATED_H
#include <memory>
#include <vector>
#include "hdf5.h"
#include "angle_energy.h"
#include "distribution_angle.h"
#include "distribution_energy.h"
namespace openmc {
//==============================================================================
//! Uncorrelated angle-energy distribution. This corresponds to when an energy
//! distribution is given in ENDF File 5/6 and an angular distribution is given
//! in ENDF File 4.
//==============================================================================
class UncorrelatedAngleEnergy : public AngleEnergy {
public:
explicit UncorrelatedAngleEnergy(hid_t group);
//! Sample distribution for an angle and energy
//! \param[in] E_in Incoming energy in [eV]
//! \param[out] E_out Outgoing energy in [eV]
//! \param[out] mu Outgoing cosine with respect to current direction
void sample(double E_in, double& E_out, double& mu) const;
// Accessors
AngleDistribution& angle() { return angle_; }
bool& fission() { return fission_; }
private:
AngleDistribution angle_; //!< Angle distribution
std::unique_ptr<EnergyDistribution> energy_; //!< Energy distribution
bool fission_ {false}; //!< Whether distribution is use for fission
};
} // namespace openmc
#endif // OPENMC_SECONDARY_UNCORRELATED_H

View file

@ -7,7 +7,7 @@ module simulation
#endif
use bank_header, only: source_bank
use cmfd_execute, only: cmfd_init_batch, execute_cmfd
use cmfd_execute, only: cmfd_init_batch, cmfd_tally_init, execute_cmfd
use cmfd_header, only: cmfd_on
use constants, only: ZERO
use eigenvalue, only: count_source_for_ufs, calculate_average_keff, &
@ -317,6 +317,7 @@ contains
#ifdef OPENMC_MPI
integer :: mpi_err ! MPI error code
#endif
character(MAX_FILE_LEN) :: filename
! Reduce tallies onto master process and accumulate
call time_tallies % start()
@ -349,13 +350,22 @@ contains
! Write out state point if it's been specified for this batch
if (statepoint_batch % contains(current_batch)) then
err = openmc_statepoint_write()
if (sourcepoint_batch % contains(current_batch) .and. source_write &
.and. .not. source_separate) then
err = openmc_statepoint_write(write_source=.true._C_BOOL)
else
err = openmc_statepoint_write(write_source=.false._C_BOOL)
end if
end if
! Write out source point if it's been specified for this batch
if ((sourcepoint_batch % contains(current_batch) .or. source_latest) .and. &
source_write) then
call write_source_point()
! Write out a separate source point if it's been specified for this batch
if (sourcepoint_batch % contains(current_batch) .and. source_write &
.and. source_separate) call write_source_point()
! Write a continously-overwritten source point if requested.
if (source_latest) then
filename = trim(path_output) // 'source' // '.h5'
call write_source_point(filename)
end if
end subroutine finalize_batch
@ -428,6 +438,9 @@ contains
! Allocate tally results arrays if they're not allocated yet
call configure_tallies()
! Activate the CMFD tallies
call cmfd_tally_init()
! Set up material nuclide index mapping
do i = 1, n_materials
call materials(i) % init_nuclide_index()
@ -568,6 +581,13 @@ contains
! Write tally results to tallies.out
if (output_tallies .and. master) call write_tallies()
! Deactivate all tallies
if (allocated(tallies)) then
do i = 1, n_tallies
tallies(i) % obj % active = .false.
end do
end if
! Stop timers and show timing statistics
call time_finalize%stop()
call time_total%stop()

View file

@ -58,10 +58,12 @@ contains
! OPENMC_STATEPOINT_WRITE writes an HDF5 statepoint file to disk
!===============================================================================
function openmc_statepoint_write(filename) result(err) bind(C)
type(C_PTR), intent(in), optional :: filename
function openmc_statepoint_write(filename, write_source) result(err) bind(C)
type(C_PTR), intent(in), optional :: filename
logical(C_BOOL), intent(in), optional :: write_source
integer(C_INT) :: err
logical :: write_source_
integer :: i, j, k
integer :: i_xs
integer, allocatable :: id_array(:)
@ -69,13 +71,17 @@ contains
integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, &
filters_group, filter_group, derivs_group, &
deriv_group, runtime_group
integer(C_INT) :: ignored_err
real(C_DOUBLE) :: k_combined(2)
character(MAX_WORD_LEN), allocatable :: str_array(:)
character(C_CHAR), pointer :: string(:)
character(len=:, kind=C_CHAR), allocatable :: filename_
character(MAX_WORD_LEN, kind=C_CHAR) :: temp_name
logical :: parallel
err = 0
! Set the filename
if (present(filename)) then
call c_f_pointer(filename, string, [MAX_FILE_LEN])
filename_ = to_f_string(string)
@ -86,6 +92,13 @@ contains
filename_ = trim(filename_) // '.h5'
end if
! Determine whether or not to write the source bank
if (present(write_source)) then
write_source_ = write_source
else
write_source_ = .true.
end if
! Write message
call write_message("Creating state point " // trim(filename_) // "...", 5)
@ -138,10 +151,10 @@ contains
call write_dataset(file_id, "current_batch", current_batch)
! Indicate whether source bank is stored in statepoint
if (source_separate) then
call write_attribute(file_id, "source_present", 0)
else
if (write_source_) then
call write_attribute(file_id, "source_present", 1)
else
call write_attribute(file_id, "source_present", 0)
end if
! Write out information for eigenvalue run
@ -156,7 +169,7 @@ contains
call write_dataset(file_id, "k_col_abs", k_col_abs)
call write_dataset(file_id, "k_col_tra", k_col_tra)
call write_dataset(file_id, "k_abs_tra", k_abs_tra)
err = openmc_get_keff(k_combined)
ignored_err = openmc_get_keff(k_combined)
call write_dataset(file_id, "k_combined", k_combined)
! Write out CMFD info
@ -432,17 +445,33 @@ contains
call file_close(file_id)
end if
#ifdef PHDF5
parallel = .true.
#else
parallel = .false.
#endif
! Write the source bank if desired
if (write_source_) then
if (master .or. parallel) then
file_id = file_open(filename_, 'a', parallel=.true.)
end if
call write_source_bank(file_id, work_index, source_bank)
if (master .or. parallel) call file_close(file_id)
end if
end function openmc_statepoint_write
!===============================================================================
! WRITE_SOURCE_POINT
!===============================================================================
subroutine write_source_point()
subroutine write_source_point(filename)
character(MAX_FILE_LEN), intent(in), optional :: filename
logical :: parallel
integer(HID_T) :: file_id
character(MAX_FILE_LEN) :: filename
character(MAX_FILE_LEN) :: filename_
! When using parallel HDF5, the file is written to collectively by all
! processes. With MPI-only, the file is opened and written by the master
@ -454,47 +483,20 @@ contains
parallel = .false.
#endif
! Check to write out source for a specified batch
if (sourcepoint_batch%contains(current_batch)) then
if (source_separate) then
filename = trim(path_output) // 'source.' // &
& zero_padded(current_batch, count_digits(n_max_batches))
filename = trim(filename) // '.h5'
call write_message("Creating source file " // trim(filename) &
// "...", 5)
! Create separate source file
if (master .or. parallel) then
file_id = file_open(filename, 'w', parallel=.true.)
call write_attribute(file_id, "filetype", 'source')
end if
else
filename = trim(path_output) // 'statepoint.' // &
zero_padded(current_batch, count_digits(n_max_batches))
filename = trim(filename) // '.h5'
if (master .or. parallel) then
file_id = file_open(filename, 'a', parallel=.true.)
end if
end if
call write_source_bank(file_id, work_index, source_bank)
if (master .or. parallel) call file_close(file_id)
if (present(filename)) then
filename_ = filename
else
filename_ = trim(path_output) // 'source.' // &
& zero_padded(current_batch, count_digits(n_max_batches))
filename_ = trim(filename_) // '.h5'
end if
! Also check to write source separately in overwritten file
if (source_latest) then
filename = trim(path_output) // 'source' // '.h5'
call write_message("Creating source file " // trim(filename) // "...", 5)
if (master .or. parallel) then
file_id = file_open(filename, 'w', parallel=.true.)
call write_attribute(file_id, "filetype", 'source')
end if
call write_source_bank(file_id, work_index, source_bank)
if (master .or. parallel) call file_close(file_id)
if (master .or. parallel) then
file_id = file_open(filename_, 'w', parallel=.true.)
call write_attribute(file_id, "filetype", 'source')
end if
call write_source_bank(file_id, work_index, source_bank)
if (master .or. parallel) call file_close(file_id)
end subroutine write_source_point

View file

@ -198,20 +198,20 @@ contains
case (FILL_MATERIAL)
call write_dataset(cell_group, "fill_type", "material")
if (size(c % material) == 1) then
if (c % material_size() == 1) then
if (c % material(1) == MATERIAL_VOID) then
call write_dataset(cell_group, "material", MATERIAL_VOID)
else
call write_dataset(cell_group, "material", &
materials(c % material(1)) % id)
materials(c % material(1)) % id())
end if
else
allocate(cell_materials(size(c % material)))
do j = 1, size(c % material)
allocate(cell_materials(c % material_size()))
do j = 1, c % material_size()
if (c % material(j) == MATERIAL_VOID) then
cell_materials(j) = MATERIAL_VOID
else
cell_materials(j) = materials(c % material(j)) % id
cell_materials(j) = materials(c % material(j)) % id()
end if
end do
call write_dataset(cell_group, "material", cell_materials)
@ -333,7 +333,7 @@ contains
do i = 1, n_materials
m => materials(i)
material_group = create_group(materials_group, "material " // &
trim(to_str(m%id)))
trim(to_str(m%id())))
if (m % depletable) then
call write_attribute(material_group, "depletable", 1)

View file

@ -27,7 +27,7 @@ extern "C" const int BC_PERIODIC {3};
int32_t n_surfaces;
Surface **surfaces_c;
std::vector<Surface*> global_surfaces;
std::map<int, int> surface_map;
@ -141,17 +141,17 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2,
Surface::Surface(pugi::xml_node surf_node)
{
if (check_for_node(surf_node, "id")) {
id = stoi(get_node_value(surf_node, "id"));
id = std::stoi(get_node_value(surf_node, "id"));
} else {
fatal_error("Must specify id of surface in geometry XML file.");
}
if (check_for_node(surf_node, "name")) {
name = get_node_value(surf_node, "name");
name = get_node_value(surf_node, "name", false);
}
if (check_for_node(surf_node, "boundary")) {
std::string surf_bc = get_node_value(surf_node, "boundary");
std::string surf_bc = get_node_value(surf_node, "boundary", true, true);
if (surf_bc == "transmission" || surf_bc == "transmit" ||surf_bc.empty()) {
bc = BC_TRANSMIT;
@ -247,7 +247,7 @@ PeriodicSurface::PeriodicSurface(pugi::xml_node surf_node)
: Surface {surf_node}
{
if (check_for_node(surf_node, "periodic_surface_id")) {
i_periodic = stoi(get_node_value(surf_node, "periodic_surface_id"));
i_periodic = std::stoi(get_node_value(surf_node, "periodic_surface_id"));
}
}
@ -298,8 +298,8 @@ void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const
write_dataset(group_id, "coefficients", coeffs);
}
bool SurfaceXPlane::periodic_translate(const PeriodicSurface *other, Position& r,
Direction& u) const
bool SurfaceXPlane::periodic_translate(const PeriodicSurface* other,
Position& r, Direction& u) const
{
Direction other_n = other->normal(r);
if (other_n.x == 1 and other_n.y == 0 and other_n.z == 0) {
@ -359,8 +359,8 @@ void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const
write_dataset(group_id, "coefficients", coeffs);
}
bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other, Position& r,
Direction& u) const
bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other,
Position& r, Direction& u) const
{
Direction other_n = other->normal(r);
if (other_n.x == 0 and other_n.y == 1 and other_n.z == 0) {
@ -421,8 +421,8 @@ void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const
write_dataset(group_id, "coefficients", coeffs);
}
bool SurfaceZPlane::periodic_translate(const PeriodicSurface *other, Position& r,
Direction& u) const
bool SurfaceZPlane::periodic_translate(const PeriodicSurface* other,
Position& r, Direction& u) const
{
// Assume the other plane is aligned along z. Just change the z coord.
r.z = z0;
@ -478,7 +478,7 @@ void SurfacePlane::to_hdf5_inner(hid_t group_id) const
write_dataset(group_id, "coefficients", coeffs);
}
bool SurfacePlane::periodic_translate(const PeriodicSurface *other, Position& r,
bool SurfacePlane::periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const
{
// This function assumes the other plane shares this plane's normal direction.
@ -582,18 +582,18 @@ axis_aligned_cylinder_normal(Position r, double offset1, double offset2)
SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, y0, z0, r);
read_coeffs(surf_node, id, y0, z0, radius);
}
double SurfaceXCylinder::evaluate(Position r) const
{
return axis_aligned_cylinder_evaluate<1, 2>(r, y0, z0, this->r);
return axis_aligned_cylinder_evaluate<1, 2>(r, y0, z0, radius);
}
double SurfaceXCylinder::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cylinder_distance<0, 1, 2>(r, u, coincident, y0, z0,
this->r);
radius);
}
Direction SurfaceXCylinder::normal(Position r) const
@ -605,7 +605,7 @@ Direction SurfaceXCylinder::normal(Position r) const
void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "x-cylinder", false);
std::array<double, 3> coeffs {{y0, z0, r}};
std::array<double, 3> coeffs {{y0, z0, radius}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -616,18 +616,18 @@ void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const
SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, z0, r);
read_coeffs(surf_node, id, x0, z0, radius);
}
double SurfaceYCylinder::evaluate(Position r) const
{
return axis_aligned_cylinder_evaluate<0, 2>(r, x0, z0, this->r);
return axis_aligned_cylinder_evaluate<0, 2>(r, x0, z0, radius);
}
double SurfaceYCylinder::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cylinder_distance<1, 0, 2>(r, u, coincident, x0, z0,
this->r);
radius);
}
Direction SurfaceYCylinder::normal(Position r) const
@ -638,7 +638,7 @@ Direction SurfaceYCylinder::normal(Position r) const
void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "y-cylinder", false);
std::array<double, 3> coeffs {{x0, z0, r}};
std::array<double, 3> coeffs {{x0, z0, radius}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -649,18 +649,18 @@ void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const
SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, y0, r);
read_coeffs(surf_node, id, x0, y0, radius);
}
double SurfaceZCylinder::evaluate(Position r) const
{
return axis_aligned_cylinder_evaluate<0, 1>(r, x0, y0, this->r);
return axis_aligned_cylinder_evaluate<0, 1>(r, x0, y0, radius);
}
double SurfaceZCylinder::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cylinder_distance<2, 0, 1>(r, u, coincident, x0, y0,
this->r);
radius);
}
Direction SurfaceZCylinder::normal(Position r) const
@ -671,7 +671,7 @@ Direction SurfaceZCylinder::normal(Position r) const
void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "z-cylinder", false);
std::array<double, 3> coeffs {{x0, y0, r}};
std::array<double, 3> coeffs {{x0, y0, radius}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -682,7 +682,7 @@ void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const
SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, y0, z0, r);
read_coeffs(surf_node, id, x0, y0, z0, radius);
}
double SurfaceSphere::evaluate(Position r) const
@ -690,7 +690,7 @@ double SurfaceSphere::evaluate(Position r) const
const double x = r.x - x0;
const double y = r.y - y0;
const double z = r.z - z0;
return x*x + y*y + z*z - this->r*this->r;
return x*x + y*y + z*z - radius*radius;
}
double SurfaceSphere::distance(Position r, Direction u, bool coincident) const
@ -699,7 +699,7 @@ double SurfaceSphere::distance(Position r, Direction u, bool coincident) const
const double y = r.y - y0;
const double z = r.z - z0;
const double k = x*u.x + y*u.y + z*u.z;
const double c = x*x + y*y + z*z - this->r*this->r;
const double c = x*x + y*y + z*z - radius*radius;
const double quad = k*k - c;
if (quad < 0.0) {
@ -739,7 +739,7 @@ Direction SurfaceSphere::normal(Position r) const
void SurfaceSphere::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "sphere", false);
std::array<double, 4> coeffs {{x0, y0, z0, r}};
std::array<double, 4> coeffs {{x0, y0, z0, radius}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -835,29 +835,29 @@ axis_aligned_cone_normal(Position r, double offset1, double offset2,
SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
}
double SurfaceXCone::evaluate(Position r) const
{
return axis_aligned_cone_evaluate<0, 1, 2>(r, x0, y0, z0, r_sq);
return axis_aligned_cone_evaluate<0, 1, 2>(r, x0, y0, z0, radius_sq);
}
double SurfaceXCone::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cone_distance<0, 1, 2>(r, u, coincident, x0, y0, z0,
r_sq);
radius_sq);
}
Direction SurfaceXCone::normal(Position r) const
{
return axis_aligned_cone_normal<0, 1, 2>(r, x0, y0, z0, r_sq);
return axis_aligned_cone_normal<0, 1, 2>(r, x0, y0, z0, radius_sq);
}
void SurfaceXCone::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "x-cone", false);
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -868,29 +868,29 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const
SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
}
double SurfaceYCone::evaluate(Position r) const
{
return axis_aligned_cone_evaluate<1, 0, 2>(r, y0, x0, z0, r_sq);
return axis_aligned_cone_evaluate<1, 0, 2>(r, y0, x0, z0, radius_sq);
}
double SurfaceYCone::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cone_distance<1, 0, 2>(r, u, coincident, y0, x0, z0,
r_sq);
radius_sq);
}
Direction SurfaceYCone::normal(Position r) const
{
return axis_aligned_cone_normal<1, 0, 2>(r, y0, x0, z0, r_sq);
return axis_aligned_cone_normal<1, 0, 2>(r, y0, x0, z0, radius_sq);
}
void SurfaceYCone::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "y-cone", false);
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -901,29 +901,29 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const
SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node)
: Surface(surf_node)
{
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
}
double SurfaceZCone::evaluate(Position r) const
{
return axis_aligned_cone_evaluate<2, 0, 1>(r, z0, x0, y0, r_sq);
return axis_aligned_cone_evaluate<2, 0, 1>(r, z0, x0, y0, radius_sq);
}
double SurfaceZCone::distance(Position r, Direction u, bool coincident) const
{
return axis_aligned_cone_distance<2, 0, 1>(r, u, coincident, z0, x0, y0,
r_sq);
radius_sq);
}
Direction SurfaceZCone::normal(Position r) const
{
return axis_aligned_cone_normal<2, 0, 1>(r, z0, x0, y0, r_sq);
return axis_aligned_cone_normal<2, 0, 1>(r, z0, x0, y0, radius_sq);
}
void SurfaceZCone::to_hdf5_inner(hid_t group_id) const
{
write_string(group_id, "type", "z-cone", false);
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
write_dataset(group_id, "coefficients", coeffs);
}
@ -1023,7 +1023,7 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const
//==============================================================================
extern "C" void
read_surfaces(pugi::xml_node *node)
read_surfaces(pugi::xml_node* node)
{
// Count the number of surfaces.
for (pugi::xml_node surf_node: node->children("surface")) {n_surfaces++;}
@ -1031,52 +1031,50 @@ read_surfaces(pugi::xml_node *node)
fatal_error("No surfaces found in geometry.xml!");
}
// Allocate the array of Surface pointers.
surfaces_c = new Surface* [n_surfaces];
// Loop over XML surface elements and populate the array.
global_surfaces.reserve(n_surfaces);
{
pugi::xml_node surf_node;
int i_surf;
for (surf_node = node->child("surface"), i_surf = 0; surf_node;
surf_node = surf_node.next_sibling("surface"), i_surf++) {
std::string surf_type = get_node_value(surf_node, "type");
std::string surf_type = get_node_value(surf_node, "type", true, true);
if (surf_type == "x-plane") {
surfaces_c[i_surf] = new SurfaceXPlane(surf_node);
global_surfaces.push_back(new SurfaceXPlane(surf_node));
} else if (surf_type == "y-plane") {
surfaces_c[i_surf] = new SurfaceYPlane(surf_node);
global_surfaces.push_back(new SurfaceYPlane(surf_node));
} else if (surf_type == "z-plane") {
surfaces_c[i_surf] = new SurfaceZPlane(surf_node);
global_surfaces.push_back(new SurfaceZPlane(surf_node));
} else if (surf_type == "plane") {
surfaces_c[i_surf] = new SurfacePlane(surf_node);
global_surfaces.push_back(new SurfacePlane(surf_node));
} else if (surf_type == "x-cylinder") {
surfaces_c[i_surf] = new SurfaceXCylinder(surf_node);
global_surfaces.push_back(new SurfaceXCylinder(surf_node));
} else if (surf_type == "y-cylinder") {
surfaces_c[i_surf] = new SurfaceYCylinder(surf_node);
global_surfaces.push_back(new SurfaceYCylinder(surf_node));
} else if (surf_type == "z-cylinder") {
surfaces_c[i_surf] = new SurfaceZCylinder(surf_node);
global_surfaces.push_back(new SurfaceZCylinder(surf_node));
} else if (surf_type == "sphere") {
surfaces_c[i_surf] = new SurfaceSphere(surf_node);
global_surfaces.push_back(new SurfaceSphere(surf_node));
} else if (surf_type == "x-cone") {
surfaces_c[i_surf] = new SurfaceXCone(surf_node);
global_surfaces.push_back(new SurfaceXCone(surf_node));
} else if (surf_type == "y-cone") {
surfaces_c[i_surf] = new SurfaceYCone(surf_node);
global_surfaces.push_back(new SurfaceYCone(surf_node));
} else if (surf_type == "z-cone") {
surfaces_c[i_surf] = new SurfaceZCone(surf_node);
global_surfaces.push_back(new SurfaceZCone(surf_node));
} else if (surf_type == "quadric") {
surfaces_c[i_surf] = new SurfaceQuadric(surf_node);
global_surfaces.push_back(new SurfaceQuadric(surf_node));
} else {
std::stringstream err_msg;
@ -1088,7 +1086,7 @@ read_surfaces(pugi::xml_node *node)
// Fill the surface map.
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
int id = surfaces_c[i_surf]->id;
int id = global_surfaces[i_surf]->id;
auto in_map = surface_map.find(id);
if (in_map == surface_map.end()) {
surface_map[id] = i_surf;
@ -1104,10 +1102,10 @@ read_surfaces(pugi::xml_node *node)
zmin {INFTY}, zmax {-INFTY};
int i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax;
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
if (surfaces_c[i_surf]->bc == BC_PERIODIC) {
if (global_surfaces[i_surf]->bc == BC_PERIODIC) {
// Downcast to the PeriodicSurface type.
Surface *surf_base = surfaces_c[i_surf];
PeriodicSurface *surf = dynamic_cast<PeriodicSurface *>(surf_base);
Surface* surf_base = global_surfaces[i_surf];
PeriodicSurface* surf = dynamic_cast<PeriodicSurface*>(surf_base);
// Make sure this surface inherits from PeriodicSurface.
if (!surf) {
@ -1149,14 +1147,14 @@ read_surfaces(pugi::xml_node *node)
// Set i_periodic for periodic BC surfaces.
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
if (surfaces_c[i_surf]->bc == BC_PERIODIC) {
if (global_surfaces[i_surf]->bc == BC_PERIODIC) {
// Downcast to the PeriodicSurface type.
Surface *surf_base = surfaces_c[i_surf];
PeriodicSurface *surf = dynamic_cast<PeriodicSurface *>(surf_base);
Surface* surf_base = global_surfaces[i_surf];
PeriodicSurface* surf = dynamic_cast<PeriodicSurface*>(surf_base);
// Also try downcasting to the SurfacePlane type (which must be handled
// differently).
SurfacePlane *surf_p = dynamic_cast<SurfacePlane *>(surf);
SurfacePlane* surf_p = dynamic_cast<SurfacePlane*>(surf);
if (!surf_p) {
// This is not a SurfacePlane.
@ -1198,7 +1196,7 @@ read_surfaces(pugi::xml_node *node)
}
// Make sure the opposite surface is also periodic.
if (surfaces_c[surf->i_periodic]->bc != BC_PERIODIC) {
if (global_surfaces[surf->i_periodic]->bc != BC_PERIODIC) {
std::stringstream err_msg;
err_msg << "Could not find matching surface for periodic boundary "
"condition on surface " << surf->id;
@ -1213,13 +1211,13 @@ read_surfaces(pugi::xml_node *node)
//==============================================================================
extern "C" {
Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];}
Surface* surface_pointer(int surf_ind) {return global_surfaces[surf_ind];}
int surface_id(Surface *surf) {return surf->id;}
int surface_id(Surface* surf) {return surf->id;}
int surface_bc(Surface *surf) {return surf->bc;}
int surface_bc(Surface* surf) {return surf->bc;}
void surface_reflect(Surface *surf, double xyz[3], double uvw[3])
void surface_reflect(Surface* surf, double xyz[3], double uvw[3])
{
Position r {xyz};
Direction u {uvw};
@ -1230,7 +1228,7 @@ extern "C" {
uvw[2] = u.z;
}
void surface_normal(Surface *surf, double xyz[3], double uvw[3])
void surface_normal(Surface* surf, double xyz[3], double uvw[3])
{
Position r {xyz};
Direction u = surf->normal(r);
@ -1239,12 +1237,12 @@ extern "C" {
uvw[2] = u.z;
}
void surface_to_hdf5(Surface *surf, hid_t group) {surf->to_hdf5(group);}
void surface_to_hdf5(Surface* surf, hid_t group) {surf->to_hdf5(group);}
int surface_i_periodic(PeriodicSurface *surf) {return surf->i_periodic;}
int surface_i_periodic(PeriodicSurface* surf) {return surf->i_periodic;}
bool
surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3],
surface_periodic(PeriodicSurface* surf, PeriodicSurface* other, double xyz[3],
double uvw[3])
{
Position r {xyz};
@ -1264,9 +1262,8 @@ extern "C" {
void free_memory_surfaces_c()
{
for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];}
delete surfaces_c;
surfaces_c = nullptr;
for (Surface* surf : global_surfaces) {delete surf;}
global_surfaces.clear();
n_surfaces = 0;
surface_map.clear();
}

View file

@ -4,6 +4,7 @@
#include <map>
#include <limits> // For numeric_limits
#include <string>
#include <vector>
#include "hdf5.h"
#include "pugixml.hpp"
@ -18,6 +19,7 @@ namespace openmc {
// Module constant declarations (defined in .cpp)
//==============================================================================
// TODO: Convert to enum
extern "C" const int BC_TRANSMIT;
extern "C" const int BC_VACUUM;
extern "C" const int BC_REFLECT;
@ -30,7 +32,7 @@ extern "C" const int BC_PERIODIC;
extern "C" int32_t n_surfaces;
class Surface;
extern Surface **surfaces_c;
extern std::vector<Surface*> global_surfaces;
extern std::map<int, int> surface_map;
@ -74,10 +76,9 @@ public:
bool sense(Position r, Direction u) const;
//! Determine the direction of a ray reflected from the surface.
//! \param r The point at which the ray is incident.
//! \param u A direction. This is both an input and an output parameter.
//! It specifies the icident direction on input and the reflected direction
//! on output.
//! \param[in] r The point at which the ray is incident.
//! \param[in] u Incident direction of the ray
//! \return Outgoing direction of the ray
Direction reflect(Position r, Direction u) const;
//! Evaluate the equation describing the surface.
@ -131,7 +132,7 @@ public:
//! periodicity.
//! \return true if this surface and its partner make a rotationally-periodic
//! boundary condition.
virtual bool periodic_translate(const PeriodicSurface *other, Position& r,
virtual bool periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const = 0;
//! Get the bounding box for this surface.
@ -153,8 +154,8 @@ public:
double distance(Position r, Direction u, bool coincident) const;
Direction normal(Position r) const;
void to_hdf5_inner(hid_t group_id) const;
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
const;
bool periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const;
BoundingBox bounding_box() const;
};
@ -173,8 +174,8 @@ public:
double distance(Position r, Direction u, bool coincident) const;
Direction normal(Position r) const;
void to_hdf5_inner(hid_t group_id) const;
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
const;
bool periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const;
BoundingBox bounding_box() const;
};
@ -193,8 +194,8 @@ public:
double distance(Position r, Direction u, bool coincident) const;
Direction normal(Position r) const;
void to_hdf5_inner(hid_t group_id) const;
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
const;
bool periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const;
BoundingBox bounding_box() const;
};
@ -213,8 +214,8 @@ public:
double distance(Position r, Direction u, bool coincident) const;
Direction normal(Position r) const;
void to_hdf5_inner(hid_t group_id) const;
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
const;
bool periodic_translate(const PeriodicSurface* other, Position& r,
Direction& u) const;
BoundingBox bounding_box() const;
};
@ -227,7 +228,7 @@ public:
class SurfaceXCylinder : public Surface
{
double y0, z0, r;
double y0, z0, radius;
public:
explicit SurfaceXCylinder(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -245,7 +246,7 @@ public:
class SurfaceYCylinder : public Surface
{
double x0, z0, r;
double x0, z0, radius;
public:
explicit SurfaceYCylinder(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -263,7 +264,7 @@ public:
class SurfaceZCylinder : public Surface
{
double x0, y0, r;
double x0, y0, radius;
public:
explicit SurfaceZCylinder(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -281,7 +282,7 @@ public:
class SurfaceSphere : public Surface
{
double x0, y0, z0, r;
double x0, y0, z0, radius;
public:
explicit SurfaceSphere(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -299,7 +300,7 @@ public:
class SurfaceXCone : public Surface
{
double x0, y0, z0, r_sq;
double x0, y0, z0, radius_sq;
public:
explicit SurfaceXCone(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -317,7 +318,7 @@ public:
class SurfaceYCone : public Surface
{
double x0, y0, z0, r_sq;
double x0, y0, z0, radius_sq;
public:
explicit SurfaceYCone(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -335,7 +336,7 @@ public:
class SurfaceZCone : public Surface
{
double x0, y0, z0, r_sq;
double x0, y0, z0, radius_sq;
public:
explicit SurfaceZCone(pugi::xml_node surf_node);
double evaluate(Position r) const;
@ -368,16 +369,16 @@ public:
extern "C" {
Surface* surface_pointer(int surf_ind);
int surface_id(Surface *surf);
int surface_bc(Surface *surf);
bool surface_sense(Surface *surf, double xyz[3], double uvw[3]);
void surface_reflect(Surface *surf, double xyz[3], double uvw[3]);
double surface_distance(Surface *surf, double xyz[3], double uvw[3],
int surface_id(Surface* surf);
int surface_bc(Surface* surf);
bool surface_sense(Surface* surf, double xyz[3], double uvw[3]);
void surface_reflect(Surface* surf, double xyz[3], double uvw[3]);
double surface_distance(Surface* surf, double xyz[3], double uvw[3],
bool coincident);
void surface_normal(Surface *surf, double xyz[3], double uvw[3]);
void surface_to_hdf5(Surface *surf, hid_t group);
int surface_i_periodic(PeriodicSurface *surf);
bool surface_periodic(PeriodicSurface *surf, PeriodicSurface *other,
void surface_normal(Surface* surf, double xyz[3], double uvw[3]);
void surface_to_hdf5(Surface* surf, hid_t group);
int surface_i_periodic(PeriodicSurface* surf);
bool surface_periodic(PeriodicSurface* surf, PeriodicSurface* other,
double xyz[3], double uvw[3]);
void free_memory_surfaces_c();
}

View file

@ -8,13 +8,12 @@ module surface_header
implicit none
interface
pure function surface_pointer_c(surf_ind) &
bind(C, name='surface_pointer') result(ptr)
pure function surface_pointer(surf_ind) bind(C) result(ptr)
use ISO_C_BINDING
implicit none
integer(C_INT), intent(in), value :: surf_ind
type(C_PTR) :: ptr
end function surface_pointer_c
end function surface_pointer
pure function surface_id_c(surf_ptr) bind(C, name='surface_id') result(id)
use ISO_C_BINDING

View file

@ -94,6 +94,7 @@ contains
integer :: k ! loop index for bank sites
integer :: d_bin ! delayed group bin index
integer :: dg_filter ! index of delayed group filter
integer :: threshold ! threshold energy index
real(8) :: yield ! delayed neutron yield
real(8) :: atom_density_ ! atom/b-cm
real(8) :: f ! interpolation factor
@ -227,7 +228,7 @@ contains
! Get yield and apply to score
associate (rxn => nuclides(p % event_nuclide) % reactions(m))
score = p % last_wgt * flux &
* rxn % products(1) % yield % evaluate(E)
* rxn % product_yield(1, E)
end associate
end if
@ -633,7 +634,7 @@ contains
score = p % absorb_wgt * yield * &
micro_xs(p % event_nuclide) % fission &
/ micro_xs(p % event_nuclide) % absorption &
* rxn % products(1 + d) % decay_rate * flux
* rxn % product_decay_rate(1 + d) * flux
end associate
! Tally to bin
@ -657,9 +658,8 @@ contains
! rxn % products array to be exceeded. Hence, we use the size
! of this array and not the MAX_DELAYED_GROUPS constant for
! this loop.
do d = 1, size(rxn % products) - 2
score = score + rxn % products(1 + d) % decay_rate * &
do d = 1, rxn % products_size() - 2
score = score + rxn % product_decay_rate(1 + d) * &
p % absorb_wgt &
* micro_xs(p % event_nuclide) % fission &
* nuclides(p % event_nuclide) % &
@ -699,7 +699,7 @@ contains
! determine score based on bank site weight and keff.
score = score + keff * fission_bank(n_bank - p % n_bank + k) &
% wgt * rxn % products(1 + g) % decay_rate * flux
% wgt * rxn % product_decay_rate(1 + g) * flux
end associate
! if the delayed group filter is present, tally to corresponding
@ -755,7 +755,7 @@ contains
! Compute the score and tally to bin
score = micro_xs(i_nuclide) % fission * yield * flux * &
atom_density * rxn % products(1 + d) % decay_rate
atom_density * rxn % product_decay_rate(1 + d)
end associate
! Tally to bin
@ -778,11 +778,10 @@ contains
! groups since this could cause the range of the rxn % products
! array to be exceeded. Hence, we use the size of this array
! and not the MAX_DELAYED_GROUPS constant for this loop.
do d = 1, size(rxn % products) - 2
do d = 1, rxn % products_size() - 2
score = score + micro_xs(i_nuclide) % fission * flux * &
nuclides(i_nuclide) % nu(E, EMISSION_DELAYED) * &
atom_density * rxn % products(1 + d) % decay_rate
atom_density * rxn % product_decay_rate(1 + d)
end do
end associate
end if
@ -824,7 +823,7 @@ contains
! Compute the score
score = micro_xs(i_nuc) % fission * yield * flux * &
atom_density_ &
* rxn % products(1 + d) % decay_rate
* rxn % product_decay_rate(1 + d)
end associate
! Tally to bin
@ -860,13 +859,13 @@ contains
! rxn % products array to be exceeded. Hence, we use the
! size of this array and not the MAX_DELAYED_GROUPS
! constant for this loop.
do d = 1, size(rxn % products) - 2
do d = 1, rxn % products_size() - 2
! Accumulate the contribution from each nuclide
score = score + micro_xs(i_nuc) % fission &
* nuclides(i_nuc) % nu(E, EMISSION_DELAYED) &
* atom_density_ * flux &
* rxn % products(1 + d) % decay_rate
* rxn % product_decay_rate(1 + d)
end do
end associate
end if
@ -1132,12 +1131,12 @@ contains
i_energy = micro_xs(i_nuclide) % index_grid
f = micro_xs(i_nuclide) % interp_factor
associate (xs => nuclides(i_nuclide) % reactions(m) &
% xs(i_temp))
if (i_energy >= xs % threshold) then
score = ((ONE - f) * xs % value(i_energy - &
xs % threshold + 1) + f * xs % value(i_energy - &
xs % threshold + 2)) * atom_density * flux
associate (rx => nuclides(i_nuclide) % reactions(m))
threshold = rx % xs_threshold(i_temp)
if (i_energy >= threshold) then
score = ((ONE - f) * rx % xs(i_temp, i_energy - &
threshold + 1) + f * rx % xs(i_temp, i_energy - &
threshold + 2)) * atom_density * flux
end if
end associate
else
@ -1165,12 +1164,12 @@ contains
i_energy = micro_xs(i_nuc) % index_grid
f = micro_xs(i_nuc) % interp_factor
associate (xs => nuclides(i_nuc) % reactions(m) &
% xs(i_temp))
if (i_energy >= xs % threshold) then
score = score + ((ONE - f) * xs % value(i_energy - &
xs % threshold + 1) + f * xs % value(i_energy - &
xs % threshold + 2)) * atom_density_ * flux
associate (rx => nuclides(i_nuc) % reactions(m))
threshold = rx % xs_threshold(i_temp)
if (i_energy >= threshold) then
score = score + ((ONE - f) * rx % xs(i_temp, i_energy - &
threshold + 1) + f * rx % xs(i_temp, i_energy - &
threshold + 2)) * atom_density_ * flux
end if
end associate
else
@ -3044,7 +3043,7 @@ contains
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
SCORE_NU_FISSION)
if (materials(p % material) % id == deriv % diff_material) then
if (materials(p % material) % id() == deriv % diff_material) then
score = score * (flux_deriv + ONE &
/ materials(p % material) % density_gpcc)
else
@ -3065,7 +3064,7 @@ contains
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
SCORE_NU_FISSION)
if (materials(p % material) % id == deriv % diff_material) then
if (materials(p % material) % id() == deriv % diff_material) then
score = score * (flux_deriv + ONE &
/ materials(p % material) % density_gpcc)
else
@ -3107,7 +3106,7 @@ contains
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
SCORE_NU_FISSION)
if (materials(p % material) % id == deriv % diff_material &
if (materials(p % material) % id() == deriv % diff_material &
.and. p % event_nuclide == deriv % diff_nuclide) then
associate(mat => materials(p % material))
! Search for the index of the perturbed nuclide.
@ -3129,7 +3128,7 @@ contains
case (ESTIMATOR_COLLISION)
scoring_diff_nuclide = &
(materials(p % material) % id == deriv % diff_material) &
(materials(p % material) % id() == deriv % diff_material) &
.and. (i_nuclide == deriv % diff_nuclide)
select case (score_bin)
@ -3139,7 +3138,7 @@ contains
case (SCORE_TOTAL)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % total /= ZERO) then
score = score * (flux_deriv &
+ micro_xs(deriv % diff_nuclide) % total &
@ -3153,7 +3152,7 @@ contains
case (SCORE_SCATTER)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % total - material_xs % absorption /= ZERO) then
score = score * (flux_deriv &
+ (micro_xs(deriv % diff_nuclide) % total &
@ -3169,7 +3168,7 @@ contains
case (SCORE_ABSORPTION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % absorption /= ZERO) then
score = score * (flux_deriv &
+ micro_xs(deriv % diff_nuclide) % absorption &
@ -3183,7 +3182,7 @@ contains
case (SCORE_FISSION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % fission /= ZERO) then
score = score * (flux_deriv &
+ micro_xs(deriv % diff_nuclide) % fission &
@ -3197,7 +3196,7 @@ contains
case (SCORE_NU_FISSION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % nu_fission /= ZERO) then
score = score * (flux_deriv &
+ micro_xs(deriv % diff_nuclide) % nu_fission &
@ -3243,7 +3242,7 @@ contains
score = score * flux_deriv
case (SCORE_TOTAL)
if (materials(p % material) % id == deriv % diff_material .and. &
if (materials(p % material) % id() == deriv % diff_material .and. &
micro_xs(p % event_nuclide) % total > ZERO) then
associate(mat => materials(p % material))
! Search for the index of the perturbed nuclide.
@ -3268,7 +3267,7 @@ contains
end if
case (SCORE_SCATTER)
if (materials(p % material) % id == deriv % diff_material .and. &
if (materials(p % material) % id() == deriv % diff_material .and. &
(micro_xs(p % event_nuclide) % total &
- micro_xs(p % event_nuclide) % absorption) > ZERO) then
associate(mat => materials(p % material))
@ -3296,7 +3295,7 @@ contains
end if
case (SCORE_ABSORPTION)
if (materials(p % material) % id == deriv % diff_material .and. &
if (materials(p % material) % id() == deriv % diff_material .and. &
micro_xs(p % event_nuclide) % absorption > ZERO) then
associate(mat => materials(p % material))
! Search for the index of the perturbed nuclide.
@ -3321,7 +3320,7 @@ contains
end if
case (SCORE_FISSION)
if (materials(p % material) % id == deriv % diff_material .and. &
if (materials(p % material) % id() == deriv % diff_material .and. &
micro_xs(p % event_nuclide) % fission > ZERO) then
associate(mat => materials(p % material))
! Search for the index of the perturbed nuclide.
@ -3346,7 +3345,7 @@ contains
end if
case (SCORE_NU_FISSION)
if (materials(p % material) % id == deriv % diff_material .and. &
if (materials(p % material) % id() == deriv % diff_material .and. &
micro_xs(p % event_nuclide) % nu_fission > ZERO) then
associate(mat => materials(p % material))
! Search for the index of the perturbed nuclide.
@ -3386,7 +3385,7 @@ contains
case (SCORE_TOTAL)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % total > ZERO) then
cum_dsig = ZERO
associate(mat => materials(p % material))
@ -3405,7 +3404,7 @@ contains
end associate
score = score * (flux_deriv &
+ cum_dsig / material_xs % total)
else if (materials(p % material) % id == deriv % diff_material &
else if (materials(p % material) % id() == deriv % diff_material &
.and. material_xs % total > ZERO) then
dsig_t = ZERO
associate (nuc => nuclides(i_nuclide))
@ -3424,7 +3423,7 @@ contains
case (SCORE_SCATTER)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
(material_xs % total - material_xs % absorption) > ZERO) then
cum_dsig = ZERO
associate(mat => materials(p % material))
@ -3445,7 +3444,7 @@ contains
end associate
score = score * (flux_deriv + cum_dsig &
/ (material_xs % total - material_xs % absorption))
else if ( materials(p % material) % id == deriv % diff_material &
else if ( materials(p % material) % id() == deriv % diff_material &
.and. (material_xs % total - material_xs % absorption) > ZERO)&
then
dsig_t = ZERO
@ -3467,7 +3466,7 @@ contains
case (SCORE_ABSORPTION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % absorption > ZERO) then
cum_dsig = ZERO
associate(mat => materials(p % material))
@ -3486,7 +3485,7 @@ contains
end associate
score = score * (flux_deriv &
+ cum_dsig / material_xs % absorption)
else if (materials(p % material) % id == deriv % diff_material &
else if (materials(p % material) % id() == deriv % diff_material &
.and. material_xs % absorption > ZERO) then
dsig_a = ZERO
associate (nuc => nuclides(i_nuclide))
@ -3505,7 +3504,7 @@ contains
case (SCORE_FISSION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % fission > ZERO) then
cum_dsig = ZERO
associate(mat => materials(p % material))
@ -3524,7 +3523,7 @@ contains
end associate
score = score * (flux_deriv &
+ cum_dsig / material_xs % fission)
else if (materials(p % material) % id == deriv % diff_material &
else if (materials(p % material) % id() == deriv % diff_material &
.and. material_xs % fission > ZERO) then
dsig_f = ZERO
associate (nuc => nuclides(i_nuclide))
@ -3543,7 +3542,7 @@ contains
case (SCORE_NU_FISSION)
if (i_nuclide == -1 .and. &
materials(p % material) % id == deriv % diff_material .and. &
materials(p % material) % id() == deriv % diff_material .and. &
material_xs % nu_fission > ZERO) then
cum_dsig = ZERO
associate(mat => materials(p % material))
@ -3564,7 +3563,7 @@ contains
end associate
score = score * (flux_deriv &
+ cum_dsig / material_xs % nu_fission)
else if (materials(p % material) % id == deriv % diff_material &
else if (materials(p % material) % id() == deriv % diff_material &
.and. material_xs % nu_fission > ZERO) then
dsig_f = ZERO
associate (nuc => nuclides(i_nuclide))
@ -3615,7 +3614,7 @@ contains
case (DIFF_DENSITY)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material) then
if (mat % id() == deriv % diff_material) then
! phi is proportional to e^(-Sigma_tot * dist)
! (1 / phi) * (d_phi / d_rho) = - (d_Sigma_tot / d_rho) * dist
! (1 / phi) * (d_phi / d_rho) = - Sigma_tot / rho * dist
@ -3626,7 +3625,7 @@ contains
case (DIFF_NUCLIDE_DENSITY)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material) then
if (mat % id() == deriv % diff_material) then
! phi is proportional to e^(-Sigma_tot * dist)
! (1 / phi) * (d_phi / d_N) = - (d_Sigma_tot / d_N) * dist
! (1 / phi) * (d_phi / d_N) = - sigma_tot * dist
@ -3637,7 +3636,7 @@ contains
case (DIFF_TEMPERATURE)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material) then
if (mat % id() == deriv % diff_material) then
do l=1, mat % n_nuclides
associate (nuc => nuclides(mat % nuclide(l)))
if (nuc % mp_present .and. &
@ -3691,7 +3690,7 @@ contains
case (DIFF_DENSITY)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material) then
if (mat % id() == deriv % diff_material) then
! phi is proportional to Sigma_s
! (1 / phi) * (d_phi / d_rho) = (d_Sigma_s / d_rho) / Sigma_s
! (1 / phi) * (d_phi / d_rho) = 1 / rho
@ -3702,7 +3701,7 @@ contains
case (DIFF_NUCLIDE_DENSITY)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material &
if (mat % id() == deriv % diff_material &
.and. p % event_nuclide == deriv % diff_nuclide) then
! Find the index in this material for the diff_nuclide.
do j = 1, mat % n_nuclides
@ -3723,7 +3722,7 @@ contains
case (DIFF_TEMPERATURE)
associate (mat => materials(p % material))
if (mat % id == deriv % diff_material) then
if (mat % id() == deriv % diff_material) then
do l=1, mat % n_nuclides
associate (nuc => nuclides(mat % nuclide(l)))
if (mat % nuclide(l) == p % event_nuclide .and. &

View file

@ -91,6 +91,8 @@ contains
type_ = 'zernike'
type is (ParticleFilter)
type_ = 'particle'
type is (ZernikeRadialFilter)
type_ = 'zernikeradial'
end select
! Convert Fortran string to null-terminated C string. We assume the
@ -164,6 +166,8 @@ contains
allocate(UniverseFilter :: filters(index) % obj)
case ('zernike')
allocate(ZernikeFilter :: filters(index) % obj)
case ('zernikeradial')
allocate(ZernikeRadialFilter :: filters(index) % obj)
case default
err = E_UNASSIGNED
call set_errmsg("Unknown filter type: " // trim(type_))

View file

@ -76,7 +76,7 @@ contains
allocate(material_ids(size(this % materials)))
do i = 1, size(this % materials)
material_ids(i) = materials(this % materials(i)) % id
material_ids(i) = materials(this % materials(i)) % id()
end do
call write_dataset(filter_group, "bins", material_ids)
end subroutine to_statepoint_material
@ -110,7 +110,7 @@ contains
integer, intent(in) :: bin
character(MAX_LINE_LEN) :: label
label = "Material " // to_str(materials(this % materials(bin)) % id)
label = "Material " // to_str(materials(this % materials(bin)) % id())
end function text_label_material
!===============================================================================

View file

@ -5,7 +5,7 @@ module tally_filter_zernike
use constants
use error
use hdf5_interface
use math, only: calc_zn
use math, only: calc_zn, calc_zn_rad
use particle_header, only: Particle
use string, only: to_str
use tally_filter_header
@ -13,6 +13,10 @@ module tally_filter_zernike
implicit none
private
public :: openmc_zernike_filter_get_order
public :: openmc_zernike_filter_get_params
public :: openmc_zernike_filter_set_order
public :: openmc_zernike_filter_set_params
!===============================================================================
! ZERNIKEFILTER gives Zernike polynomial moments of a particle's position
@ -24,19 +28,43 @@ module tally_filter_zernike
real(8) :: y
real(8) :: r
contains
procedure :: from_xml
procedure :: get_all_bins
procedure :: to_statepoint
procedure :: text_label
procedure :: calc_n_bins => calc_n_bins_zn
procedure :: from_xml => from_xml_zn
procedure :: get_all_bins => get_all_bins_zn
procedure :: to_statepoint => to_statepoint_zn
procedure :: text_label => text_label_zn
end type ZernikeFilter
!===============================================================================
! ZERNIKERADIALFILTER gives even order radial Zernike polynomial moments of a
! particle's position
!===============================================================================
type, public, extends(ZernikeFilter) :: ZernikeRadialFilter
contains
procedure :: calc_n_bins => calc_n_bins_zn_rad
! Inherit from_xml from ZernikeFilter
procedure :: get_all_bins => get_all_bins_zn_rad
procedure :: to_statepoint => to_statepoint_zn_rad
procedure :: text_label => text_label_zn_rad
end type ZernikeRadialFilter
contains
!===============================================================================
! ZernikeFilter methods
!===============================================================================
subroutine from_xml(this, node)
function calc_n_bins_zn(this) result(n_bins)
class(ZernikeFilter), intent(in) :: this
integer :: n
integer :: n_bins
n = this % order
n_bins = ((n+1) * (n+2))/2
end function calc_n_bins_zn
subroutine from_xml_zn(this, node)
class(ZernikeFilter), intent(inout) :: this
type(XMLNode), intent(in) :: node
@ -50,10 +78,10 @@ contains
! Get specified order
call get_node_value(node, "order", n)
this % order = n
this % n_bins = ((n + 1)*(n + 2))/2
end subroutine from_xml
this % n_bins = this % calc_n_bins()
end subroutine from_xml_zn
subroutine get_all_bins(this, p, estimator, match)
subroutine get_all_bins_zn(this, p, estimator, match)
class(ZernikeFilter), intent(in) :: this
type(Particle), intent(in) :: p
integer, intent(in) :: estimator
@ -67,18 +95,20 @@ contains
x = p % coord(1) % xyz(1) - this % x
y = p % coord(1) % xyz(2) - this % y
r = sqrt(x*x + y*y)/this % r
theta = atan2(y, x)
if (r <= ONE) then
theta = atan2(y, x)
! Get moments for Zernike polynomial orders 0..n
call calc_zn(this % order, r, theta, zn)
! Get moments for Zernike polynomial orders 0..n
call calc_zn(this % order, r, theta, zn)
do i = 1, this % n_bins
call match % bins % push_back(i)
call match % weights % push_back(zn(i))
end do
endif
end subroutine get_all_bins_zn
do i = 1, this % n_bins
call match % bins % push_back(i)
call match % weights % push_back(zn(i))
end do
end subroutine get_all_bins
subroutine to_statepoint(this, filter_group)
subroutine to_statepoint_zn(this, filter_group)
class(ZernikeFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
@ -88,9 +118,9 @@ contains
call write_dataset(filter_group, "x", this % x)
call write_dataset(filter_group, "y", this % y)
call write_dataset(filter_group, "r", this % r)
end subroutine to_statepoint
end subroutine to_statepoint_zn
function text_label(this, bin) result(label)
function text_label_zn(this, bin) result(label)
class(ZernikeFilter), intent(in) :: this
integer, intent(in) :: bin
character(MAX_LINE_LEN) :: label
@ -108,7 +138,66 @@ contains
exit
end if
end do
end function text_label
end function text_label_zn
!===============================================================================
! ZernikeRadialFilter methods
!===============================================================================
function calc_n_bins_zn_rad(this) result(num_n_bins)
class(ZernikeRadialFilter), intent(in) :: this
integer :: n
integer :: num_n_bins
n = this % order
num_n_bins = n/2 + 1
end function calc_n_bins_zn_rad
subroutine get_all_bins_zn_rad(this, p, estimator, match)
class(ZernikeRadialFilter), intent(in) :: this
type(Particle), intent(in) :: p
integer, intent(in) :: estimator
type(TallyFilterMatch), intent(inout) :: match
integer :: i
real(8) :: x, y, r
real(C_DOUBLE) :: zn_rad(this % n_bins)
! Determine normalized (r,theta) positions
x = p % coord(1) % xyz(1) - this % x
y = p % coord(1) % xyz(2) - this % y
r = sqrt(x*x + y*y)/this % r
if (r <= ONE) then
! Get moments for even order Zernike polynomial orders 0..n
call calc_zn_rad(this % order, r, zn_rad)
do i = 1, this % n_bins
call match % bins % push_back(i)
call match % weights % push_back(zn_rad(i))
end do
endif
end subroutine get_all_bins_zn_rad
subroutine to_statepoint_zn_rad(this, filter_group)
class(ZernikeRadialFilter), intent(in) :: this
integer(HID_T), intent(in) :: filter_group
call write_dataset(filter_group, "type", "zernikeradial")
call write_dataset(filter_group, "n_bins", this % n_bins)
call write_dataset(filter_group, "order", this % order)
call write_dataset(filter_group, "x", this % x)
call write_dataset(filter_group, "y", this % y)
call write_dataset(filter_group, "r", this % r)
end subroutine to_statepoint_zn_rad
function text_label_zn_rad(this, bin) result(label)
class(ZernikeRadialFilter), intent(in) :: this
integer, intent(in) :: bin
character(MAX_LINE_LEN) :: label
label = "Zernike expansion, Z" // trim(to_str(2*(bin-1))) // ",0"
end function text_label_zn_rad
!===============================================================================
! C API FUNCTIONS
@ -125,6 +214,8 @@ contains
select type (f => filters(index) % obj)
type is (ZernikeFilter)
order = f % order
type is (ZernikeRadialFilter)
order = f % order
class default
err = E_INVALID_TYPE
call set_errmsg("Not a Zernike filter.")
@ -144,7 +235,7 @@ contains
err = verify_filter(index)
if (err == 0) then
select type (f => filters(index) % obj)
type is (ZernikeFilter)
class is (ZernikeFilter)
x = f % x
y = f % y
r = f % r
@ -168,6 +259,9 @@ contains
type is (ZernikeFilter)
f % order = order
f % n_bins = ((order + 1)*(order + 2))/2
type is (ZernikeRadialFilter)
f % order = order
f % n_bins = order/2 + 1
class default
err = E_INVALID_TYPE
call set_errmsg("Not a Zernike filter.")
@ -187,7 +281,7 @@ contains
err = verify_filter(index)
if (err == 0) then
select type (f => filters(index) % obj)
type is (ZernikeFilter)
class is (ZernikeFilter)
if (present(x)) f % x = x
if (present(y)) f % y = y
if (present(r)) f % r = r

View file

@ -340,6 +340,9 @@ contains
type is (ZernikeFilter)
j = FILTER_ZERNIKE
this % estimator = ESTIMATOR_COLLISION
type is (ZernikeRadialFilter)
j = FILTER_ZERNIKE_RADIAL
this % estimator = ESTIMATOR_COLLISION
type is (ParticleFilter)
j = FILTER_PARTICLE
end select

View file

@ -196,7 +196,7 @@ contains
i_material = p % material
if (i_material /= MATERIAL_VOID) then
do i_domain = 1, size(this % domain_id)
if (materials(i_material) % id == this % domain_id(i_domain)) then
if (materials(i_material) % id() == this % domain_id(i_domain)) then
call check_hit(i_domain, i_material, indices, hits, n_mat)
end if
end do

View file

@ -1,6 +1,6 @@
#include "xml_interface.h"
#include <algorithm> // for std::transform
#include <algorithm> // for transform
#include <sstream>
#include "error.h"
@ -9,10 +9,11 @@
namespace openmc {
std::string
get_node_value(pugi::xml_node node, const char *name)
get_node_value(pugi::xml_node node, const char* name, bool lowercase,
bool strip)
{
// Search for either an attribute or child tag and get the data as a char*.
const pugi::char_t *value_char;
const pugi::char_t* value_char;
if (node.attribute(name)) {
value_char = node.attribute(name).value();
} else if (node.child(name)) {
@ -23,14 +24,18 @@ get_node_value(pugi::xml_node node, const char *name)
<< node.name() << "\" XML node";
fatal_error(err_msg);
}
std::string value {value_char};
// Convert to lowercase string.
std::string value(value_char);
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
// Convert to lower-case if needed
if (lowercase) {
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
}
// Remove whitespace.
value.erase(0, value.find_first_not_of(" \t\r\n"));
value.erase(value.find_last_not_of(" \t\r\n") + 1);
// Strip leading/trailing whitespace if needed
if (strip) {
value.erase(0, value.find_first_not_of(" \t\r\n"));
value.erase(value.find_last_not_of(" \t\r\n") + 1);
}
return value;
}

View file

@ -1,6 +1,7 @@
#ifndef XML_INTERFACE_H
#define XML_INTERFACE_H
#include <sstream> // for stringstream
#include <string>
#include <vector>
@ -15,7 +16,25 @@ check_for_node(pugi::xml_node node, const char *name)
return node.attribute(name) || node.child(name);
}
std::string get_node_value(pugi::xml_node node, const char *name);
std::string get_node_value(pugi::xml_node node, const char *name,
bool lowercase=false, bool strip=false);
template <typename T>
std::vector<T> get_node_array(pugi::xml_node node, const char* name,
bool lowercase=false)
{
// Get value of node attribute/child
std::string s {get_node_value(node, name, lowercase)};
// Read values one by one into vector
std::stringstream iss {s};
T value;
std::vector<T> values;
while (iss >> value)
values.push_back(value);
return values;
}
} // namespace openmc
#endif // XML_INTERFACE_H

View file

@ -1,7 +1,15 @@
import numpy as np
import openmc
from pkg_resources import parse_version
import pytest
@pytest.fixture(scope='module', autouse=True)
def numpy_version_requirement():
assert parse_version(np.__version__) >= parse_version("1.14"), \
"Regression tests require NumPy 1.14 or greater"
@pytest.fixture(scope='module', autouse=True)
def setup_regression_test(request):
# Reset autogenerated IDs assigned to OpenMC objects

View file

@ -10,7 +10,7 @@ from openmc.data import JOULE_PER_EV
import openmc.deplete
from tests.regression_tests import config
from .example_geometry import generate_problem
from example_geometry import generate_problem
def test_full(run_in_tmpdir):
@ -34,15 +34,15 @@ def test_full(run_in_tmpdir):
# OpenMC-specific settings
settings = openmc.Settings()
settings.particles = 100
settings.batches = 100
settings.inactive = 40
settings.batches = 10
settings.inactive = 0
space = openmc.stats.Box(lower_left, upper_right)
settings.source = openmc.Source(space=space)
settings.seed = 1
settings.verbosity = 3
# Create operator
chain_file = Path(__file__).parents[1] / 'chain_simple.xml'
chain_file = Path(__file__).parents[2] / 'chain_simple.xml'
op = openmc.deplete.Operator(geometry, settings, chain_file)
op.round_number = True

Binary file not shown.

View file

@ -1,2 +1,2 @@
energyfunction nuclide score mean std. dev.
0 02180f5f310ee4 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03
0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03

View file

@ -60,7 +60,7 @@
<bins>0.0 0.625 20000000.0</bins>
</filter>
<filter id="4" type="mu">
<bins>-1.0 -0.818181818182 -0.636363636364 -0.454545454545 -0.272727272727 -0.0909090909091 0.0909090909091 0.272727272727 0.454545454545 0.636363636364 0.818181818182 1.0</bins>
<bins>-1.0 -0.8181818181818181 -0.6363636363636364 -0.4545454545454546 -0.2727272727272727 -0.09090909090909083 0.09090909090909083 0.2727272727272727 0.4545454545454546 0.6363636363636365 0.8181818181818183 1.0</bins>
</filter>
<filter id="17" type="material">
<bins>2</bins>

View file

@ -37,5 +37,5 @@ Cell
Fill = Material 2
Region = -1
Rotation = None
Temperature = [ 500. 700. 0. 800.]
Temperature = [500. 700. 0. 800.]
Translation = None

View file

@ -48,7 +48,7 @@
</space>
<angle type="isotropic" />
<energy interpolation="histogram" type="tabular">
<parameters>1.0 1.38949549437 1.93069772888 2.68269579528 3.72759372031 5.17947467923 7.19685673001 10.0 13.8949549437 19.3069772888 26.8269579528 37.2759372031 51.7947467923 71.9685673001 100.0 138.949549437 193.069772888 268.269579528 372.759372031 517.947467923 719.685673001 1000.0 1389.49549437 1930.69772888 2682.69579528 3727.59372031 5179.47467923 7196.85673001 10000.0 13894.9549437 19306.9772888 26826.9579528 37275.9372031 51794.7467923 71968.5673001 100000.0 138949.549437 193069.772888 268269.579528 372759.372031 517947.467923 719685.673001 1000000.0 1389495.49437 1930697.72888 2682695.79528 3727593.72031 5179474.67923 7196856.73001 10000000.0 0.0 2.90864392994e-08 5.80533561806e-08 8.67817193689e-08 1.15153477858e-07 1.43052046006e-07 1.70362782612e-07 1.96973462002e-07 2.22774735186e-07 2.47660579198e-07 2.71528732767e-07 2.9428111653e-07 3.15824236062e-07 3.36069566065e-07 3.54933914133e-07 3.72339762616e-07 3.88215587147e-07 4.02496150558e-07 4.15122770952e-07 4.26043562837e-07 4.35213650335e-07 4.42595351592e-07 4.48158333612e-07 4.5187973691e-07 4.53744269441e-07 4.53744269441e-07 4.5187973691e-07 4.48158333612e-07 4.42595351592e-07 4.35213650335e-07 4.26043562837e-07 4.15122770952e-07 4.02496150558e-07 3.88215587147e-07 3.72339762616e-07 3.54933914133e-07 3.36069566065e-07 3.15824236062e-07 2.9428111653e-07 2.71528732767e-07 2.47660579198e-07 2.22774735186e-07 1.96973462002e-07 1.70362782612e-07 1.43052046006e-07 1.15153477858e-07 8.67817193689e-08 5.80533561806e-08 2.90864392994e-08 5.55962111528e-23</parameters>
<parameters>1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23</parameters>
</energy>
</source>
</settings>

View file

@ -48,10 +48,10 @@
<bins>0.0 4000000.0 20000000.0</bins>
</filter>
<filter id="2" type="polar">
<bins>0.0 0.785398163397 3.14159265359</bins>
<bins>0.0 0.7853981633974483 3.141592653589793</bins>
</filter>
<filter id="3" type="azimuthal">
<bins>0.0 0.785398163397 3.14159265359</bins>
<bins>0.0 0.7853981633974483 3.141592653589793</bins>
</filter>
<filter id="4" type="surface">
<bins>1</bins>

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