Merge branch 'develop' into cmfd-capi

This commit is contained in:
Shikhar Kumar 2018-08-20 18:11:49 -04:00
commit 0a0168eaf0
218 changed files with 53530 additions and 4299 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,8 +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
src/cmfd_data.F90
@ -298,7 +304,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,17 +331,12 @@ 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
src/reaction_header.F90
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,25 +384,40 @@ 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_aux.cpp
src/hdf5_interface.cpp
src/lattice.cpp
src/material.cpp
src/math_functions.cpp
src/message_passing.cpp
src/mgxs.cpp
src/mgxs_interface.cpp
src/particle.cpp
src/plot.cpp
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
src/state_point.cpp
src/string_functions.cpp
src/surface.cpp
src/thermal.cpp
src/xml_interface.cpp
src/xsdata.cpp)
set_target_properties(libopenmc PROPERTIES
@ -449,7 +464,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

View file

@ -1,6 +1,7 @@
include CMakeLists.txt
include LICENSE
include schemas.xml
include pyproject.toml
include openmc/data/reconstruct.pyx
include docs/source/_templates/layout.html
include docs/sphinxext/LICENSE

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

@ -50,6 +50,12 @@ Each ``material`` element can have the following attributes or sub-elements:
*Default*: ""
:depletable:
Boolean value indicating whether the material is depletable.
:volume:
Volume of the material in cm^3.
:temperature:
An element with no attributes which is used to set the default temperature
of the material in Kelvin.

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

@ -57,9 +57,11 @@ extern "C" {
int openmc_material_add_nuclide(int32_t index, const char name[], double density);
int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n);
int openmc_material_get_id(int32_t index, int32_t* id);
int openmc_material_get_volume(int32_t index, double* volume);
int openmc_material_set_density(int32_t index, double density);
int openmc_material_set_densities(int32_t index, int n, const char** name, const double* density);
int openmc_material_set_id(int32_t index, int32_t id);
int openmc_material_set_volume(int32_t index, double volume);
int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n);
int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins);
int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh);
@ -96,6 +98,7 @@ extern "C" {
int openmc_sphharm_filter_set_order(int32_t index, int order);
int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]);
int openmc_statepoint_write(const char filename[]);
int openmc_tally_allocate(int32_t index, const char* type);
int openmc_tally_get_active(int32_t index, bool* active);
int openmc_tally_get_estimator(int32_t index, int32_t* estimator);
int openmc_tally_get_id(int32_t index, int32_t* id);

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
@ -61,7 +61,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
@ -314,7 +314,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
@ -322,11 +322,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

@ -5,7 +5,7 @@ from weakref import WeakValueDictionary
import numpy as np
from numpy.ctypeslib import as_array
from openmc.exceptions import AllocationError, InvalidIDError
from openmc.exceptions import AllocationError, InvalidIDError, OpenMCError
from . import _dll, Nuclide
from .core import _FortranObjectWithID
from .error import _error_handler
@ -32,6 +32,9 @@ _dll.openmc_material_get_densities.argtypes = [
POINTER(c_int)]
_dll.openmc_material_get_densities.restype = c_int
_dll.openmc_material_get_densities.errcheck = _error_handler
_dll.openmc_material_get_volume.argtypes = [c_int32, POINTER(c_double)]
_dll.openmc_material_get_volume.restype = c_int
_dll.openmc_material_get_volume.errcheck = _error_handler
_dll.openmc_material_set_density.argtypes = [c_int32, c_double]
_dll.openmc_material_set_density.restype = c_int
_dll.openmc_material_set_density.errcheck = _error_handler
@ -42,6 +45,9 @@ _dll.openmc_material_set_densities.errcheck = _error_handler
_dll.openmc_material_set_id.argtypes = [c_int32, c_int32]
_dll.openmc_material_set_id.restype = c_int
_dll.openmc_material_set_id.errcheck = _error_handler
_dll.openmc_material_set_volume.argtypes = [c_int32, c_double]
_dll.openmc_material_set_volume.restype = c_int
_dll.openmc_material_set_volume.errcheck = _error_handler
class Material(_FortranObjectWithID):
@ -113,6 +119,19 @@ class Material(_FortranObjectWithID):
def id(self, mat_id):
_dll.openmc_material_set_id(self._index, mat_id)
@property
def volume(self):
volume = c_double()
try:
_dll.openmc_material_get_volume(self._index, volume)
except OpenMCError:
return None
return volume.value
@volume.setter
def volume(self, volume):
_dll.openmc_material_set_volume(self._index, volume)
@property
def nuclides(self):
return self._get_densities()[0]

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

@ -26,6 +26,9 @@ _dll.openmc_get_tally_index.errcheck = _error_handler
_dll.openmc_global_tallies.argtypes = [POINTER(POINTER(c_double))]
_dll.openmc_global_tallies.restype = c_int
_dll.openmc_global_tallies.errcheck = _error_handler
_dll.openmc_tally_allocate.argtypes = [c_int32, c_char_p]
_dll.openmc_tally_allocate.restype = c_int
_dll.openmc_tally_allocate.errcheck = _error_handler
_dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)]
_dll.openmc_tally_get_active.restype = c_int
_dll.openmc_tally_get_active.errcheck = _error_handler
@ -101,7 +104,6 @@ _TALLY_TYPES = {
}
def global_tallies():
"""Mean and standard deviation of the mean for each global tally.
@ -196,7 +198,7 @@ class Tally(_FortranObjectWithID):
index = c_int32()
_dll.openmc_extend_tallies(1, index, None)
_dll.openmc_tally_set_type(index, b'generic')
_dll.openmc_tally_allocate(index, b'generic')
index = index.value
else:
index = mapping[uid]._index
@ -224,17 +226,13 @@ class Tally(_FortranObjectWithID):
@type.setter
def type(self, type):
_dll.openmc_tally_update_type(self._index, type.encode())
_dll.openmc_tally_set_type(self._index, type.encode())
@property
def estimator(self):
estimator = c_int32()
try:
_dll.openmc_tally_get_estimator(self._index, estimator)
except AllocationError:
return ""
else:
return _ESTIMATORS[estimator.value]
_dll.openmc_tally_get_estimator(self._index, estimator)
return _ESTIMATORS[estimator.value]
@estimator.setter
def estimator(self, estimator):
@ -353,6 +351,7 @@ class Tally(_FortranObjectWithID):
return std_dev
def reset(self):
"""Reset results and num_realizations of tally"""
_dll.openmc_tally_reset(self._index)
def ci_width(self, alpha=0.05):

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

@ -284,6 +284,8 @@ class Material(IDManagerMixin):
# Create the Material
material = cls(mat_id, name)
material.depletable = bool(group.attrs['depletable'])
if 'volume' in group.attrs:
material.volume = group.attrs['volume']
# Read the names of the S(a,b) tables for this Material and add them
if 'sab_names' in group:
@ -833,6 +835,9 @@ class Material(IDManagerMixin):
if self._depletable:
element.set("depletable", "true")
if self._volume:
element.set("volume", str(self._volume))
# Create temperature XML subelement
if self.temperature is not None:
subelement = ET.SubElement(element, "temperature")

2
pyproject.toml Normal file
View file

@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel", "numpy", "cython"]

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

@ -1,38 +0,0 @@
module angleenergy_header
use hdf5_interface, only: HID_T
!===============================================================================
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
! distribution that is a function of incoming energy. Each derived type must
! implement a sample() subroutine that returns an outgoing energy and scattering
! cosine given an incoming energy.
!===============================================================================
type, abstract :: AngleEnergy
contains
procedure(angleenergy_sample_), deferred :: sample
procedure(angleenergy_from_hdf5_), deferred :: from_hdf5
end type AngleEnergy
abstract interface
subroutine angleenergy_sample_(this, E_in, E_out, mu)
import AngleEnergy
class(AngleEnergy), intent(in) :: this
real(8), intent(in) :: E_in
real(8), intent(out) :: E_out
real(8), intent(out) :: mu
end subroutine angleenergy_sample_
subroutine angleenergy_from_hdf5_(this, group_id)
import AngleEnergy, HID_T
class(AngleEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
end subroutine angleenergy_from_hdf5_
end interface
type :: AngleEnergyContainer
class(AngleEnergy), allocatable :: obj
end type AngleEnergyContainer
end module angleenergy_header

View file

@ -25,7 +25,7 @@ module openmc_api
use tally_header
use tally_filter_header
use tally_filter
use tally, only: openmc_tally_set_type
use tally, only: openmc_tally_allocate
use simulation
use string, only: to_f_string
use timer_header
@ -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
@ -89,6 +87,7 @@ module openmc_api
public :: openmc_simulation_init
public :: openmc_source_bank
public :: openmc_source_set_strength
public :: openmc_tally_allocate
public :: openmc_tally_get_estimator
public :: openmc_tally_get_id
public :: openmc_tally_get_filters
@ -222,7 +221,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
@ -267,7 +266,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
@ -286,14 +284,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>
@ -9,6 +8,8 @@
#include "error.h"
#include "hdf5_interface.h"
#include "lattice.h"
#include "material.h"
#include "openmc.h"
#include "surface.h"
#include "xml_interface.h"
@ -19,14 +20,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
//==============================================================================
@ -199,52 +199,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")) {
@ -279,33 +291,31 @@ Cell::Cell(pugi::xml_node cell_node)
//==============================================================================
bool
Cell::contains(const double xyz[3], const double uvw[3],
int32_t on_surface) const
Cell::contains(Position r, Direction u, int32_t on_surface) const
{
if (simple) {
return contains_simple(xyz, uvw, on_surface);
return contains_simple(r, u, on_surface);
} else {
return contains_complex(xyz, uvw, on_surface);
return contains_complex(r, u, on_surface);
}
}
//==============================================================================
std::pair<double, int32_t>
Cell::distance(const double xyz[3], const double uvw[3],
int32_t on_surface) const
Cell::distance(Position r, Direction u, int32_t on_surface) const
{
double min_dist {INFTY};
int32_t i_surf {std::numeric_limits<int32_t>::max()};
for (int32_t token : rpn) {
// Ignore this token if it corresponds to an operator rather than a region.
if (token >= OP_UNION) {continue;}
if (token >= OP_UNION) continue;
// 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(xyz, uvw, 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 +356,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);
@ -356,8 +367,7 @@ Cell::to_hdf5(hid_t cell_group) const
//==============================================================================
bool
Cell::contains_simple(const double xyz[3], const double uvw[3],
int32_t on_surface) const
Cell::contains_simple(Position r, Direction u, int32_t on_surface) const
{
for (int32_t token : rpn) {
if (token < OP_UNION) {
@ -370,7 +380,7 @@ Cell::contains_simple(const double xyz[3], const double uvw[3],
return false;
} else {
// Note the off-by-one indexing
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
if (sense != (token > 0)) {return false;}
}
}
@ -381,8 +391,7 @@ Cell::contains_simple(const double xyz[3], const double uvw[3],
//==============================================================================
bool
Cell::contains_complex(const double xyz[3], const double uvw[3],
int32_t on_surface) const
Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
{
// Make a stack of booleans. We don't know how big it needs to be, but we do
// know that rpn.size() is an upper-bound.
@ -413,7 +422,7 @@ Cell::contains_complex(const double xyz[3], const double uvw[3],
stack[i_stack] = false;
} else {
// Note the off-by-one indexing
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);;
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
stack[i_stack] = (sense == (token > 0));
}
}
@ -435,7 +444,7 @@ Cell::contains_complex(const double xyz[3], const double uvw[3],
//==============================================================================
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++;}
@ -443,10 +452,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));
}
@ -464,6 +471,67 @@ read_cells(pugi::xml_node *node)
global_universes[it->second]->cells.push_back(i);
}
}
global_universes.shrink_to_fit();
}
//==============================================================================
// 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 {
set_errmsg("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 {
set_errmsg("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 {
set_errmsg("Index in cells array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
return 0;
}
//==============================================================================
@ -473,40 +541,50 @@ 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;}
int32_t cell_n_instances(Cell *c) {return c->n_instances;}
bool cell_simple(Cell *c) {return c->simple;}
bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface)
{return c->contains(xyz, uvw, on_surface);}
void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface,
double *min_dist, int32_t *i_surf)
//TODO: off-by-one
int32_t cell_material(Cell* c, int i)
{
std::pair<double, int32_t> out = c->distance(xyz, uvw, on_surface);
int32_t mat = c->material[i-1];
if (mat == MATERIAL_VOID) return MATERIAL_VOID;
return mat + 1;
}
bool cell_simple(Cell* c) {return c->simple;}
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)
{
Position r {xyz};
Direction u {uvw};
std::pair<double, int32_t> out = c->distance(r, u, on_surface);
*min_dist = out.first;
*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

@ -1,7 +1,8 @@
#ifndef CELL_H
#define CELL_H
#ifndef OPENMC_CELL_H
#define OPENMC_CELL_H
#include <cstdint>
#include <limits>
#include <string>
#include <unordered_map>
#include <vector>
@ -9,6 +10,8 @@
#include "hdf5.h"
#include "pugixml.hpp"
#include "position.h"
namespace openmc {
@ -16,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;
@ -90,29 +94,27 @@ public:
//! provides a performance benefit for the common case. In
//! contains_complex, we evaluate the RPN expression using a stack, similar to
//! how a RPN calculator would work.
//! @param xyz[3] The 3D Cartesian coordinate to check.
//! @param uvw[3] A direction used to "break ties" the coordinates are very
//! \param r The 3D Cartesian coordinate to check.
//! \param u A direction used to "break ties" the coordinates are very
//! close to a surface.
//! @param on_surface The signed index of a surface that the coordinate is
//! \param on_surface The signed index of a surface that the coordinate is
//! known to be on. This index takes precedence over surface sense
//! calculations.
bool
contains(const double xyz[3], const double uvw[3], int32_t on_surface) const;
contains(Position r, Direction u, int32_t on_surface) const;
//! Find the oncoming boundary of this cell.
std::pair<double, int32_t>
distance(const double xyz[3], const double uvw[3], int32_t on_surface) const;
distance(Position r, Direction u, int32_t on_surface) const;
//! \brief Write cell information to an HDF5 group.
//! @param group_id An HDF5 group id.
//! \param group_id An HDF5 group id.
void to_hdf5(hid_t group_id) const;
protected:
bool contains_simple(const double xyz[3], const double uvw[3],
int32_t on_surface) const;
bool contains_complex(const double xyz[3], const double uvw[3],
int32_t on_surface) const;
bool contains_simple(Position r, Direction u, int32_t on_surface) const;
bool contains_complex(Position r, Direction u, int32_t on_surface) const;
};
} // namespace openmc
#endif // CELL_H
#endif // OPENMC_CELL_H

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

@ -243,7 +243,7 @@ contains
use error, only: fatal_error, warning
use mesh_header, only: RegularMesh, openmc_extend_meshes
use string
use tally, only: openmc_tally_set_type
use tally, only: openmc_tally_allocate
use tally_header, only: openmc_extend_tallies
use tally_filter_header
use tally_filter
@ -434,7 +434,7 @@ contains
! Begin loop around tallies
do i = 1, size(cmfd_tallies)
! Allocate tally
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR)
err = openmc_tally_allocate(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR)
call openmc_get_tally_next_id(tally_id)
err = openmc_tally_set_id(i_start + i - 1, tally_id)
@ -465,7 +465,7 @@ contains
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
! Set tally type to volume
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
! Allocate and set filters
allocate(filter_indices(n_filter))
@ -493,7 +493,7 @@ contains
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
! Set tally type to volume
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
! Set the incoming energy mesh filter index in the tally find_filter
! array
@ -542,7 +542,7 @@ contains
! Set macro bins
t % score_bins(1) = SCORE_CURRENT
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR)
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR)
else if (i == 4) then
! Set name
@ -552,7 +552,7 @@ contains
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
! Set tally type to volume
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
! Allocate and set filters
n_filter = 2

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,274 @@ 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};
// 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};
constexpr std::array<int, 6> DEPLETION_RX {N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N};
// 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 +299,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 +317,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

154
src/distribution.h Normal file
View file

@ -0,0 +1,154 @@
//! \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;
// x property
std::vector<double>& x() { return x_; }
const std::vector<double>& x() const { return x_; }
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

View file

@ -380,7 +380,11 @@ contains
! Determine overall generation and number of active generations
i = overall_generation()
n = i - n_inactive*gen_per_batch
if (current_batch > n_inactive) then
n = gen_per_batch*n_realizations + current_gen
else
n = 0
end if
if (n <= 0) then
! For inactive generations, use current generation k as estimate for next

178
src/endf.cpp Normal file
View file

@ -0,0 +1,178 @@
#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));
}
}
//==============================================================================
// CoherentElasticXS implementation
//==============================================================================
CoherentElasticXS::CoherentElasticXS(hid_t dset)
{
// Read 2D array from dataset
xt::xarray<double> arr;
read_dataset(dset, arr);
// Get views for Bragg edges and structure factors
auto E = xt::view(arr, 0);
auto s = xt::view(arr, 1);
// Copy Bragg edges and partial sums of structure factors
std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_));
std::copy(s.begin(), s.end(), std::back_inserter(factors_));
}
double CoherentElasticXS::operator()(double E) const
{
if (E < bragg_edges_[0]) {
// If energy is below that of the lowest Bragg peak, the elastic cross
// section will be zero
return 0.0;
} else {
auto i_grid = lower_bound_index(bragg_edges_.begin(), bragg_edges_.end(), E);
return factors_[i_grid] / E;
}
}
} // namespace openmc

91
src/endf.h Normal file
View file

@ -0,0 +1,91 @@
//! \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
};
//==============================================================================
//! Coherent elastic scattering data from a crystalline material
//==============================================================================
class CoherentElasticXS : public Function1D {
explicit CoherentElasticXS(hid_t dset);
double operator()(double E) const;
private:
std::vector<double> bragg_edges_; //!< Bragg edges in [eV]
std::vector<double> factors_; //!< Partial sums of structure factors [eV-b]
};
} // 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

@ -5,6 +5,7 @@
#include <string>
#include <sstream>
#include "openmc.h"
namespace openmc {
@ -14,20 +15,38 @@ extern "C" void warning_from_c(const char* message, int message_len);
extern "C" void write_message_from_c(const char* message, int message_len,
int level);
inline
void fatal_error(const char *message)
inline void
set_errmsg(const char* message)
{
fatal_error_from_c(message, strlen(message));
std::strcpy(openmc_err_msg, message);
}
inline void
set_errmsg(const std::string& message)
{
std::strcpy(openmc_err_msg, message.c_str());
}
inline void
set_errmsg(const std::stringstream& message)
{
std::strcpy(openmc_err_msg, message.str().c_str());
}
inline
void fatal_error(const std::string &message)
void fatal_error(const char* message)
{
fatal_error_from_c(message, std::strlen(message));
}
inline
void fatal_error(const std::string& message)
{
fatal_error_from_c(message.c_str(), message.length());
}
inline
void fatal_error(const std::stringstream &message)
void fatal_error(const std::stringstream& message)
{
fatal_error(message.str());
}
@ -47,7 +66,7 @@ void warning(const std::stringstream& message)
inline
void write_message(const char* message, int level)
{
write_message_from_c(message, strlen(message), level);
write_message_from_c(message, std::strlen(message), level);
}
inline

View file

@ -172,7 +172,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.
@ -204,7 +204,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

@ -1,6 +1,10 @@
#ifndef GEOMETRY_H
#define GEOMETRY_H
#ifndef OPENMC_GEOMETRY_H
#define OPENMC_GEOMETRY_H
namespace openmc {
extern "C" int openmc_root_universe;
#endif // GEOMETRY_H
} // namespace openmc
#endif // OPENMC_GEOMETRY_H

View file

@ -8,6 +8,7 @@
#include "constants.h"
#include "error.h"
#include "lattice.h"
#include "material.h"
namespace openmc {
@ -15,11 +16,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 +37,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 +70,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 +82,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 +123,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 +140,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 +149,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 +169,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 +192,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 +203,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 +214,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 +224,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 +236,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 +244,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 +256,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 +267,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 +289,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 +299,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 +316,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,16 +338,16 @@ 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();
}

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,47 @@ get_groups(hid_t group_id, char* name[])
}
}
std::vector<std::string>
member_names(hid_t group_id, H5O_type_t type)
{
// 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 != type) 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;
}
std::vector<std::string>
group_names(hid_t group_id)
{
return member_names(group_id, H5O_TYPE_GROUP);
}
std::vector<std::string>
dataset_names(hid_t group_id)
{
return member_names(group_id, H5O_TYPE_DATASET);
}
bool
object_exists(hid_t object_id, const char* name)
@ -299,14 +381,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 +405,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 +512,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,16 +1,19 @@
#ifndef HDF5_INTERFACE_H
#define HDF5_INTERFACE_H
#include "hdf5.h"
#include "hdf5_hl.h"
#ifndef OPENMC_HDF5_INTERFACE_H
#define OPENMC_HDF5_INTERFACE_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 {
@ -19,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,
@ -70,6 +73,13 @@ 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);
std::vector<std::string> dataset_names(hid_t group_id);
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
//==============================================================================
@ -99,7 +109,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,
@ -140,7 +150,146 @@ 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};
}
// overload for std::vector<std::string>
inline void
read_attribute(hid_t obj_id, const char* name, std::vector<std::string>& vec)
{
auto dims = attribute_shape(obj_id, name);
auto m = dims[0];
// Allocate a C char array to get strings
auto n = attribute_typesize(obj_id, name);
char buffer[m][n+1];
// Read char data in attribute
read_attr_string(obj_id, name, n, buffer[0]);
for (int i = 0; i < m; ++i) {
vec.emplace_back(&buffer[i][0]);
}
}
//==============================================================================
// 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
@ -149,8 +298,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);
}
@ -162,14 +311,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);
}
@ -181,5 +334,12 @@ write_dataset(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
}
inline void
write_dataset(hid_t obj_id, const char* name, Position r)
{
std::array<double, 3> buffer {r.x, r.y, r.z};
write_dataset(obj_id, name, buffer);
}
} // namespace openmc
#endif //HDF5_INTERFACE_H
#endif // OPENMC_HDF5_INTERFACE_H

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.
@ -1095,7 +1100,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
@ -1109,42 +1114,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 &
@ -1246,7 +1215,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.")
@ -1309,7 +1278,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)
@ -1325,7 +1294,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)
@ -1540,10 +1509,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))
@ -1556,27 +1527,14 @@ 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)
if (to_lower(temp_str) == "true" .or. temp_str == "1") &
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))
call get_node_value(node_mat, "depletable", mat % depletable)
end if
! Copy material name
@ -1596,7 +1554,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
@ -1626,7 +1584,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
@ -1641,7 +1599,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
@ -1650,7 +1608,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.")
@ -1663,7 +1621,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
@ -1677,7 +1635,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)
@ -1685,7 +1643,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
@ -1715,7 +1673,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
@ -1854,7 +1812,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
@ -1935,7 +1893,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
@ -2159,7 +2117,7 @@ contains
READ_TALLIES: do i = 1, n
! Allocate tally
err = openmc_tally_set_type(i_start + i - 1, &
err = openmc_tally_allocate(i_start + i - 1, &
C_CHAR_'generic' // C_NULL_CHAR)
! Get pointer to tally
@ -2168,19 +2126,15 @@ contains
! Get pointer to tally xml node
node_tal = node_tal_list(i)
! Copy tally id
! Copy and set tally id
if (check_for_node(node_tal, "id")) then
call get_node_value(node_tal, "id", tally_id)
err = openmc_tally_set_id(i_start + i - 1, tally_id)
if (err /= 0) call fatal_error(to_f_string(openmc_err_msg))
else
call fatal_error("Must specify id for tally in tally XML file.")
end if
! Check to make sure 'id' hasn't been used
if (tally_dict % has(tally_id)) then
call fatal_error("Two or more tallies use the same unique ID: " &
// to_str(tally_id))
end if
! Copy tally name
if (check_for_node(node_tal, "name")) &
call get_node_value(node_tal, "name", t % name)
@ -2880,9 +2834,6 @@ contains
end select
end if
! Set tally id
err = openmc_tally_set_id(i_start + i - 1, tally_id)
end associate
end do READ_TALLIES
@ -3255,7 +3206,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
@ -3855,15 +3806,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
@ -3931,45 +3882,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
@ -3993,7 +3905,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
@ -4002,10 +3914,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]);
}
}
}
@ -223,26 +223,23 @@ RectLattice::are_valid_indices(const int i_xyz[3]) const
//==============================================================================
std::pair<double, std::array<int, 3>>
RectLattice::distance(const double xyz[3], const double uvw[3],
const int i_xyz[3]) const
RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const
{
// Get short aliases to the coordinates.
double x {xyz[0]};
double y {xyz[1]};
double z {xyz[2]};
double u {uvw[0]};
double v {uvw[1]};
double x = r.x;
double y = r.y;
double z = r.z;
// Determine the oncoming edge.
double x0 {copysign(0.5 * pitch[0], u)};
double y0 {copysign(0.5 * pitch[1], v)};
double x0 {copysign(0.5 * pitch[0], u.x)};
double y0 {copysign(0.5 * pitch[1], u.y)};
// Left and right sides
double d {INFTY};
std::array<int, 3> lattice_trans;
if ((std::abs(x - x0) > FP_PRECISION) && u != 0) {
d = (x0 - x) / u;
if (u > 0) {
if ((std::abs(x - x0) > FP_PRECISION) && u.x != 0) {
d = (x0 - x) / u.x;
if (u.x > 0) {
lattice_trans = {1, 0, 0};
} else {
lattice_trans = {-1, 0, 0};
@ -250,11 +247,11 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
}
// Front and back sides
if ((std::abs(y - y0) > FP_PRECISION) && v != 0) {
double this_d = (y0 - y) / v;
if ((std::abs(y - y0) > FP_PRECISION) && u.y != 0) {
double this_d = (y0 - y) / u.y;
if (this_d < d) {
d = this_d;
if (v > 0) {
if (u.y > 0) {
lattice_trans = {0, 1, 0};
} else {
lattice_trans = {0, -1, 0};
@ -264,13 +261,12 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
// Top and bottom sides
if (is_3d) {
double w {uvw[2]};
double z0 {copysign(0.5 * pitch[2], w)};
if ((std::abs(z - z0) > FP_PRECISION) && w != 0) {
double this_d = (z0 - z) / w;
double z0 {copysign(0.5 * pitch[2], u.z)};
if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
double this_d = (z0 - z) / u.z;
if (this_d < d) {
d = this_d;
if (w > 0) {
if (u.z > 0) {
lattice_trans = {0, 0, 1};
} else {
lattice_trans = {0, 0, -1};
@ -285,13 +281,13 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
//==============================================================================
std::array<int, 3>
RectLattice::get_indices(const double xyz[3]) const
RectLattice::get_indices(Position r) const
{
int ix {static_cast<int>(std::ceil((xyz[0] - lower_left[0]) / pitch[0]))-1};
int iy {static_cast<int>(std::ceil((xyz[1] - lower_left[1]) / pitch[1]))-1};
int ix {static_cast<int>(std::ceil((r.x - lower_left.x) / pitch.x))-1};
int iy {static_cast<int>(std::ceil((r.y - lower_left.y) / pitch.y))-1};
int iz;
if (is_3d) {
iz = static_cast<int>(std::ceil((xyz[2] - lower_left[2]) / pitch[2]))-1;
iz = static_cast<int>(std::ceil((r.z - lower_left.z) / pitch.z))-1;
} else {
iz = 0;
}
@ -300,18 +296,15 @@ RectLattice::get_indices(const double xyz[3]) const
//==============================================================================
std::array<double, 3>
RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const
Position
RectLattice::get_local_position(Position r, const int i_xyz[3]) const
{
std::array<double, 3> local_xyz;
local_xyz[0] = global_xyz[0] - (lower_left[0] + (i_xyz[0] + 0.5)*pitch[0]);
local_xyz[1] = global_xyz[1] - (lower_left[1] + (i_xyz[1] + 0.5)*pitch[1]);
r.x -= (lower_left.x + (i_xyz[0] + 0.5)*pitch.x);
r.y -= (lower_left.y + (i_xyz[1] + 0.5)*pitch.y);
if (is_3d) {
local_xyz[2] = global_xyz[2] - (lower_left[2] + (i_xyz[2] + 0.5)*pitch[2]);
} else {
local_xyz[2] = global_xyz[2];
r.z -= (lower_left.z + (i_xyz[2] + 0.5)*pitch.z);
}
return local_xyz;
return r;
}
//==============================================================================
@ -407,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;
@ -483,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;
@ -512,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;
@ -535,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;
@ -583,12 +576,11 @@ HexLattice::are_valid_indices(const int i_xyz[3]) const
//==============================================================================
std::pair<double, std::array<int, 3>>
HexLattice::distance(const double xyz[3], const double uvw[3],
const int i_xyz[3]) const
HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const
{
// Compute the direction on the hexagonal basis.
double beta_dir = uvw[0] * std::sqrt(3.0) / 2.0 + uvw[1] / 2.0;
double gamma_dir = uvw[0] * std::sqrt(3.0) / 2.0 - uvw[1] / 2.0;
double beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0;
double gamma_dir = u.x * std::sqrt(3.0) / 2.0 - u.y / 2.0;
// Note that hexagonal lattice distance calculations are performed
// using the particle's coordinates relative to the neighbor lattice
@ -600,15 +592,15 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
double d {INFTY};
std::array<int, 3> lattice_trans;
double edge = -copysign(0.5*pitch[0], beta_dir); // Oncoming edge
std::array<double, 3> xyz_t;
Position r_t;
if (beta_dir > 0) {
const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1], i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
} else {
const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1], i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
}
double beta = xyz_t[0] * std::sqrt(3.0) / 2.0 + xyz_t[1] / 2.0;
double beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0;
if ((std::abs(beta - edge) > FP_PRECISION) && beta_dir != 0) {
d = (edge - beta) / beta_dir;
if (beta_dir > 0) {
@ -622,12 +614,12 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
edge = -copysign(0.5*pitch[0], gamma_dir);
if (gamma_dir > 0) {
const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
} else {
const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
}
double gamma = xyz_t[0] * std::sqrt(3.0) / 2.0 - xyz_t[1] / 2.0;
double gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0;
if ((std::abs(gamma - edge) > FP_PRECISION) && gamma_dir != 0) {
double this_d = (edge - gamma) / gamma_dir;
if (this_d < d) {
@ -641,18 +633,18 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
}
// Upper and lower sides.
edge = -copysign(0.5*pitch[0], uvw[1]);
if (uvw[1] > 0) {
edge = -copysign(0.5*pitch[0], u.y);
if (u.y > 0) {
const int i_xyz_t[3] {i_xyz[0], i_xyz[1]+1, i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
} else {
const int i_xyz_t[3] {i_xyz[0], i_xyz[1]-1, i_xyz[2]};
xyz_t = get_local_xyz(xyz, i_xyz_t);
r_t = get_local_position(r, i_xyz_t);
}
if ((std::abs(xyz_t[1] - edge) > FP_PRECISION) && uvw[1] != 0) {
double this_d = (edge - xyz_t[1]) / uvw[1];
if ((std::abs(r_t.y - edge) > FP_PRECISION) && u.y != 0) {
double this_d = (edge - r_t.y) / u.y;
if (this_d < d) {
if (uvw[1] > 0) {
if (u.y > 0) {
lattice_trans = {0, 1, 0};
} else {
lattice_trans = {0, -1, 0};
@ -663,14 +655,13 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
// Top and bottom sides
if (is_3d) {
double z {xyz[2]};
double w {uvw[2]};
double z0 {copysign(0.5 * pitch[1], w)};
if ((std::abs(z - z0) > FP_PRECISION) && w != 0) {
double this_d = (z0 - z) / w;
double z = r.z;
double z0 {copysign(0.5 * pitch[1], u.z)};
if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
double this_d = (z0 - z) / u.z;
if (this_d < d) {
d = this_d;
if (w > 0) {
if (u.z > 0) {
lattice_trans = {0, 0, 1};
} else {
lattice_trans = {0, 0, -1};
@ -686,24 +677,24 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
//==============================================================================
std::array<int, 3>
HexLattice::get_indices(const double xyz[3]) const
HexLattice::get_indices(Position r) const
{
// Offset the xyz by the lattice center.
double xyz_o[3] {xyz[0] - center[0], xyz[1] - center[1], xyz[2]};
if (is_3d) {xyz_o[2] -= center[2];}
Position r_o {r.x - center.x, r.y - center.y, r.z};
if (is_3d) {r_o.z -= center.z;}
// Index the z direction.
std::array<int, 3> out;
if (is_3d) {
out[2] = static_cast<int>(std::ceil(xyz_o[2] / pitch[1] + 0.5 * n_axial))-1;
out[2] = static_cast<int>(std::ceil(r_o.z / pitch[1] + 0.5 * n_axial))-1;
} else {
out[2] = 0;
}
// Convert coordinates into skewed bases. The (x, alpha) basis is used to
// find the index of the global coordinates to within 4 cells.
double alpha = xyz_o[1] - xyz_o[0] / std::sqrt(3.0);
out[0] = static_cast<int>(std::floor(xyz_o[0]
double alpha = r_o.y - r_o.x / std::sqrt(3.0);
out[0] = static_cast<int>(std::floor(r_o.x
/ (0.5*std::sqrt(3.0) * pitch[0])));
out[1] = static_cast<int>(std::floor(alpha / pitch[0]));
@ -725,8 +716,8 @@ HexLattice::get_indices(const double xyz[3]) const
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
int i_xyz[3] {out[0] + j, out[1] + i, 0};
std::array<double, 3> xyz_t = get_local_xyz(xyz, i_xyz);
double d = xyz_t[0]*xyz_t[0] + xyz_t[1]*xyz_t[1];
Position r_t = get_local_position(r, i_xyz);
double d = r_t.x*r_t.x + r_t.y*r_t.y;
if (d < d_min) {
d_min = d;
k_min = k;
@ -751,26 +742,19 @@ HexLattice::get_indices(const double xyz[3]) const
//==============================================================================
std::array<double, 3>
HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const
Position
HexLattice::get_local_position(Position r, const int i_xyz[3]) const
{
std::array<double, 3> local_xyz;
// x_l = x_g - (center + pitch_x*cos(30)*index_x)
local_xyz[0] = global_xyz[0] - (center[0]
+ std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]);
r.x -= (center.x + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]);
// y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y)
local_xyz[1] = global_xyz[1] - (center[1]
+ (i_xyz[1] - n_rings + 1) * pitch[0]
+ (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0);
r.y -= (center.y + (i_xyz[1] - n_rings + 1) * pitch[0]
+ (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0);
if (is_3d) {
local_xyz[2] = global_xyz[2] - center[2]
+ (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1];
} else {
local_xyz[2] = global_xyz[2];
r.z -= center.z - (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1];
}
return local_xyz;
return r;
}
//==============================================================================
@ -908,7 +892,9 @@ extern "C" {
void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3],
const int i_xyz[3], double *d, int lattice_trans[3])
{
std::pair<double, std::array<int, 3>> ld {lat->distance(xyz, uvw, i_xyz)};
Position r {xyz};
Direction u {uvw};
std::pair<double, std::array<int, 3>> ld {lat->distance(r, u, i_xyz)};
*d = ld.first;
lattice_trans[0] = ld.second[0];
lattice_trans[1] = ld.second[1];
@ -917,7 +903,8 @@ extern "C" {
void lattice_get_indices(Lattice *lat, const double xyz[3], int i_xyz[3])
{
std::array<int, 3> inds = lat->get_indices(xyz);
Position r {xyz};
std::array<int, 3> inds = lat->get_indices(r);
i_xyz[0] = inds[0];
i_xyz[1] = inds[1];
i_xyz[2] = inds[2];
@ -926,10 +913,11 @@ extern "C" {
void lattice_get_local_xyz(Lattice *lat, const double global_xyz[3],
const int i_xyz[3], double local_xyz[3])
{
std::array<double, 3> xyz = lat->get_local_xyz(global_xyz, i_xyz);
local_xyz[0] = xyz[0];
local_xyz[1] = xyz[1];
local_xyz[2] = xyz[2];
Position global {global_xyz};
Position local = lat->get_local_position(global, i_xyz);
local_xyz[0] = local.x;
local_xyz[1] = local.y;
local_xyz[2] = local.z;
}
int32_t lattice_offset(Lattice *lat, int map, const int i_xyz[3])

View file

@ -1,5 +1,5 @@
#ifndef LATTICE_H
#define LATTICE_H
#ifndef OPENMC_LATTICE_H
#define OPENMC_LATTICE_H
#include <array>
#include <cstdint>
@ -7,10 +7,12 @@
#include <unordered_map>
#include <vector>
#include "constants.h"
#include "hdf5.h"
#include "pugixml.hpp"
#include "constants.h"
#include "position.h"
namespace openmc {
@ -69,54 +71,54 @@ public:
int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map);
//! \brief Check lattice indices.
//! @param i_xyz[3] The indices for a lattice tile.
//! @return true if the given indices fit within the lattice bounds. False
//! \param i_xyz[3] The indices for a lattice tile.
//! \return true if the given indices fit within the lattice bounds. False
//! otherwise.
virtual bool are_valid_indices(const int i_xyz[3]) const = 0;
//! \brief Find the next lattice surface crossing
//! @param xyz[3] A 3D Cartesian coordinate.
//! @param uvw[3] A 3D Cartesian direction.
//! @param i_xyz[3] The indices for a lattice tile.
//! @return The distance to the next crossing and an array indicating how the
//! \param r A 3D Cartesian coordinate.
//! \param u A 3D Cartesian direction.
//! \param i_xyz[3] The indices for a lattice tile.
//! \return The distance to the next crossing and an array indicating how the
//! lattice indices would change after crossing that boundary.
virtual std::pair<double, std::array<int, 3>>
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const
distance(Position r, Direction u, const int i_xyz[3]) const
= 0;
//! \brief Find the lattice tile indices for a given point.
//! @param xyz[3] A 3D Cartesian coordinate.
//! @return An array containing the indices of a lattice tile.
virtual std::array<int, 3> get_indices(const double xyz[3]) const = 0;
//! \param r A 3D Cartesian coordinate.
//! \return An array containing the indices of a lattice tile.
virtual std::array<int, 3> get_indices(Position r) const = 0;
//! \brief Get coordinates local to a lattice tile.
//! @param global_xyz[3] A 3D Cartesian coordinate.
//! @param i_xyz[3] The indices for a lattice tile.
//! @return Local 3D Cartesian coordinates.
virtual std::array<double, 3>
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const = 0;
//! \param r A 3D Cartesian coordinate.
//! \param i_xyz[3] The indices for a lattice tile.
//! \return Local 3D Cartesian coordinates.
virtual Position
get_local_position(Position r, const int i_xyz[3]) const = 0;
//! \brief Check flattened lattice index.
//! @param indx The index for a lattice tile.
//! @return true if the given index fit within the lattice bounds. False
//! \param indx The index for a lattice tile.
//! \return true if the given index fit within the lattice bounds. False
//! otherwise.
virtual bool is_valid_index(int indx) const
{return (indx >= 0) && (indx < universes.size());}
//! \brief Get the distribcell offset for a lattice tile.
//! @param The map index for the target cell.
//! @param i_xyz[3] The indices for a lattice tile.
//! @return Distribcell offset i.e. the largest instance number for the target
//! \param The map index for the target cell.
//! \param i_xyz[3] The indices for a lattice tile.
//! \return Distribcell offset i.e. the largest instance number for the target
//! cell found in the geometry tree under this lattice tile.
virtual int32_t& offset(int map, const int i_xyz[3]) = 0;
//! \brief Convert an array index to a useful human-readable string.
//! @param indx The index for a lattice tile.
//! @return A string representing the lattice tile.
//! \param indx The index for a lattice tile.
//! \return A string representing the lattice tile.
virtual std::string index_to_string(int indx) const = 0;
//! \brief Write lattice information to an HDF5 group.
//! @param group_id An HDF5 group id.
//! \param group_id An HDF5 group id.
void to_hdf5(hid_t group_id) const;
protected:
@ -193,12 +195,12 @@ public:
bool are_valid_indices(const int i_xyz[3]) const;
std::pair<double, std::array<int, 3>>
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const;
distance(Position r, Direction u, const int i_xyz[3]) const;
std::array<int, 3> get_indices(const double xyz[3]) const;
std::array<int, 3> get_indices(Position r) const;
std::array<double, 3>
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const;
Position
get_local_position(Position r, const int i_xyz[3]) const;
int32_t& offset(int map, const int i_xyz[3]);
@ -207,9 +209,9 @@ public:
void to_hdf5_inner(hid_t group_id) const;
private:
std::array<int, 3> n_cells; //!< Number of cells along each axis
std::array<double, 3> lower_left; //!< Global lower-left corner of the lattice
std::array<double, 3> pitch; //!< Lattice tile width along each axis
std::array<int, 3> n_cells; //!< Number of cells along each axis
Position lower_left; //!< Global lower-left corner of the lattice
Position pitch; //!< Lattice tile width along each axis
// Convenience aliases
int &nx {n_cells[0]};
@ -233,12 +235,12 @@ public:
bool are_valid_indices(const int i_xyz[3]) const;
std::pair<double, std::array<int, 3>>
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const;
distance(Position r, Direction u, const int i_xyz[3]) const;
std::array<int, 3> get_indices(const double xyz[3]) const;
std::array<int, 3> get_indices(Position r) const;
std::array<double, 3>
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const;
Position
get_local_position(Position r, const int i_xyz[3]) const;
bool is_valid_index(int indx) const;
@ -251,9 +253,9 @@ public:
private:
int n_rings; //!< Number of radial tile positions
int n_axial; //!< Number of axial tile positions
std::array<double, 3> center; //!< Global center of lattice
Position center; //!< Global center of lattice
std::array<double, 2> pitch; //!< Lattice tile width and height
};
} // namespace openmc
#endif // LATTICE_H
#endif // OPENMC_LATTICE_H

137
src/material.cpp Normal file
View file

@ -0,0 +1,137 @@
#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 node)
{
if (check_for_node(node, "id")) {
id = std::stoi(get_node_value(node, "id"));
} else {
fatal_error("Must specify id of material in materials XML file.");
}
if (check_for_node(node, "volume")) {
volume_ = std::stod(get_node_value(node, "volume"));
}
}
//==============================================================================
// 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);
}
}
}
//==============================================================================
// C API
//==============================================================================
extern "C" int
openmc_material_get_volume(int32_t index, double* volume)
{
if (index >= 1 && index <= global_materials.size()) {
Material* m = global_materials[index - 1];
if (m->volume_ >= 0.0) {
*volume = m->volume_;
return 0;
} else {
std::stringstream msg;
msg << "Volume for material with ID=" << m->id << " not set.";
set_errmsg(msg);
return OPENMC_E_UNASSIGNED;
}
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
}
extern "C" int
openmc_material_set_volume(int32_t index, double volume)
{
if (index >= 1 && index <= global_materials.size()) {
Material* m = global_materials[index - 1];
if (volume >= 0.0) {
m->volume_ = volume;
return 0;
} else {
set_errmsg("Volume must be non-negative");
return OPENMC_E_INVALID_ARGUMENT;
}
} else {
set_errmsg("Index in materials array is out of bounds.");
return OPENMC_E_OUT_OF_BOUNDS;
}
}
//==============================================================================
// 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

36
src/material.h Normal file
View file

@ -0,0 +1,36 @@
#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
double volume_ {-1.0}; //!< Volume in [cm^3]
Material() {};
explicit Material(pugi::xml_node material_node);
};
} // namespace openmc
#endif // OPENMC_MATERIAL_H

View file

@ -24,16 +24,52 @@ module material_header
public :: openmc_material_add_nuclide
public :: openmc_material_get_id
public :: openmc_material_get_densities
public :: openmc_material_get_volume
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
function openmc_material_get_volume(index, volume) result(err) bind(C)
import C_INT32_T, C_DOUBLE, C_INT
integer(C_INT32_T), value :: index
real(C_DOUBLE), intent(out) :: volume
integer(C_INT) :: err
end function openmc_material_get_volume
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 +103,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 +126,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
@ -172,7 +223,7 @@ contains
found = .false.
associate (sab => sab_tables(this % i_sab_tables(k)))
FIND_NUCLIDE: do j = 1, size(this % nuclide)
if (any(sab % nuclides == nuclides(this % nuclide(j)) % name)) then
if (sab % has_nuclide(nuclides(this % nuclide(j)) % name)) then
call i_sab_tables % push_back(this % i_sab_tables(k))
call i_sab_nuclides % push_back(j)
call sab_fracs % push_back(this % sab_fracs(k))
@ -185,7 +236,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 +246,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
@ -326,7 +377,7 @@ contains
! If particle energy is greater than the highest energy for the
! S(a,b) table, then don't use the S(a,b) table
if (p % E > sab_tables(i_sab) % data(1) % threshold_inelastic) then
if (p % E > sab_tables(i_sab) % threshold()) then
i_sab = 0
end if
@ -444,6 +495,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 +516,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 +538,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 +669,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 +685,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

67
src/nuclide.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef OPENMC_NUCLIDE_H
#define OPENMC_NUCLIDE_H
#include "constants.h"
namespace openmc {
//===============================================================================
//! Cached microscopic cross sections for a particular nuclide at the current
//! energy
//===============================================================================
struct NuclideMicroXS {
// Microscopic cross sections in barns
double total; //!< total cross section
double absorption; //!< absorption (disappearance)
double fission; //!< fission
double nu_fission; //!< neutron production from fission
double elastic; //!< If sab_frac is not 1 or 0, then this value is
//!< averaged over bound and non-bound nuclei
double thermal; //!< Bound thermal elastic & inelastic scattering
double thermal_elastic; //!< Bound thermal elastic scattering
double photon_prod; //!< microscopic photon production xs
// Cross sections for depletion reactions (note that these are not stored in
// macroscopic cache)
double reaction[DEPLETION_RX.size()];
// Indicies and factors needed to compute cross sections from the data tables
int index_grid; //!< Index on nuclide energy grid
int index_temp; //!< Temperature index for nuclide
double interp_factor; //!< Interpolation factor on nuc. energy grid
int index_sab {-1}; //!< Index in sab_tables
int index_temp_sab; //!< Temperature index for sab_tables
double sab_frac; //!< Fraction of atoms affected by S(a,b)
bool use_ptable; //!< In URR range with probability tables?
// Energy and temperature last used to evaluate these cross sections. If
// these values have changed, then the cross sections must be re-evaluated.
double last_E {0.0}; //!< Last evaluated energy
double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant
//!< * temperature (eV))
};
//===============================================================================
// MATERIALMACROXS contains cached macroscopic cross sections for the material a
// particle is traveling through
//===============================================================================
struct MaterialMacroXS {
double total; //!< macroscopic total xs
double absorption; //!< macroscopic absorption xs
double fission; //!< macroscopic fission xs
double nu_fission; //!< macroscopic production xs
double photon_prod; //!< macroscopic photon production xs
// Photon cross sections
double coherent; //!< macroscopic coherent xs
double incoherent; //!< macroscopic incoherent xs
double photoelectric; //!< macroscopic photoelectric xs
double pair_production; //!< macroscopic pair production xs
};
} // namespace openmc
#endif // OPENMC_NUCLIDE_H

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
@ -128,36 +126,36 @@ module nuclide_header
! (NuclideMicroXS % elastic)
real(8), parameter :: CACHE_INVALID = dble(Z"FFE0000000000000")
type NuclideMicroXS
type, bind(C) :: NuclideMicroXS
! Microscopic cross sections in barns
real(8) :: total
real(8) :: absorption ! absorption (disappearance)
real(8) :: fission ! fission
real(8) :: nu_fission ! neutron production from fission
real(C_DOUBLE) :: total
real(C_DOUBLE) :: absorption ! absorption (disappearance)
real(C_DOUBLE) :: fission ! fission
real(C_DOUBLE) :: nu_fission ! neutron production from fission
real(8) :: elastic ! If sab_frac is not 1 or 0, then this value is
real(C_DOUBLE) :: elastic ! If sab_frac is not 1 or 0, then this value is
! averaged over bound and non-bound nuclei
real(8) :: thermal ! Bound thermal elastic & inelastic scattering
real(8) :: thermal_elastic ! Bound thermal elastic scattering
real(8) :: photon_prod ! microscopic photon production xs
real(C_DOUBLE) :: thermal ! Bound thermal elastic & inelastic scattering
real(C_DOUBLE) :: thermal_elastic ! Bound thermal elastic scattering
real(C_DOUBLE) :: photon_prod ! microscopic photon production xs
! Cross sections for depletion reactions (note that these are not stored in
! macroscopic cache)
real(8) :: reaction(size(DEPLETION_RX))
real(C_DOUBLE) :: reaction(size(DEPLETION_RX))
! Indicies and factors needed to compute cross sections from the data tables
integer :: index_grid ! Index on nuclide energy grid
integer :: index_temp ! Temperature index for nuclide
real(8) :: interp_factor ! Interpolation factor on nuc. energy grid
integer :: index_sab = NONE ! Index in sab_tables
integer :: index_temp_sab ! Temperature index for sab_tables
real(8) :: sab_frac ! Fraction of atoms affected by S(a,b)
logical :: use_ptable ! In URR range with probability tables?
integer(C_INT) :: index_grid ! Index on nuclide energy grid
integer(C_INT) :: index_temp ! Temperature index for nuclide
real(C_DOUBLE) :: interp_factor ! Interpolation factor on nuc. energy grid
integer(C_INT) :: index_sab = NONE ! Index in sab_tables
integer(C_INT) :: index_temp_sab ! Temperature index for sab_tables
real(C_DOUBLE) :: sab_frac ! Fraction of atoms affected by S(a,b)
logical(C_BOOL) :: use_ptable ! In URR range with probability tables?
! Energy and temperature last used to evaluate these cross sections. If
! these values have changed, then the cross sections must be re-evaluated.
real(8) :: last_E = ZERO ! Last evaluated energy
real(8) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann
real(C_DOUBLE) :: last_E = ZERO ! Last evaluated energy
real(C_DOUBLE) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann
! constant * temperature (eV))
end type NuclideMicroXS
@ -166,7 +164,7 @@ module nuclide_header
! particle is traveling through
!===============================================================================
type MaterialMacroXS
type, bind(C) :: MaterialMacroXS
real(C_DOUBLE) :: total ! macroscopic total xs
real(C_DOUBLE) :: absorption ! macroscopic absorption xs
real(C_DOUBLE) :: fission ! macroscopic fission xs
@ -200,7 +198,7 @@ module nuclide_header
type(DictCharInt) :: nuclide_dict
! Cross section caches
type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide
type(NuclideMicroXS), allocatable, target :: micro_xs(:) ! Cache for each nuclide
type(MaterialMacroXS) :: material_xs ! Cache for current material
!$omp threadprivate(micro_xs, material_xs)
@ -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
@ -1114,9 +1096,9 @@ contains
real(8), intent(in) :: sab_frac ! fraction of atoms affected by S(a,b)
type(NuclideMicroXS), intent(inout) :: micro_xs ! Cross section cache
integer :: i_temp ! temperature index
real(8) :: inelastic ! S(a,b) inelastic cross section
real(8) :: elastic ! S(a,b) elastic cross section
integer(C_INT) :: i_temp ! temperature index
real(C_DOUBLE) :: inelastic ! S(a,b) inelastic cross section
real(C_DOUBLE) :: elastic ! S(a,b) elastic cross section
! Set flag that S(a,b) treatment should be used for scattering
micro_xs % index_sab = i_sab
@ -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

@ -324,7 +324,11 @@ contains
! Determine overall generation and number of active generations
i = overall_generation()
n = i - n_inactive*gen_per_batch
if (current_batch > n_inactive) then
n = gen_per_batch*n_realizations + current_gen
else
n = 0
end if
! write out information about batch and generation
write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') &
@ -357,7 +361,7 @@ contains
! Determine overall generation and number of active generations
i = current_batch*gen_per_batch
n = i - n_inactive*gen_per_batch
n = n_realizations*gen_per_batch
! write out information batch and option independent output
write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') &

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
@ -889,263 +884,16 @@ contains
real(8), intent(inout) :: uvw(3) ! directional cosines
real(8), intent(out) :: mu ! scattering cosine
integer :: i ! incoming energy bin
integer :: j ! outgoing energy bin
integer :: k ! outgoing cosine bin
integer :: i_temp ! temperature index
integer :: n_energy_out ! number of outgoing energy bins
real(8) :: f ! interpolation factor
real(8) :: r ! used for skewed sampling & continuous
real(8) :: E_ij ! outgoing energy j for E_in(i)
real(8) :: E_i1j ! outgoing energy j for E_in(i+1)
real(8) :: mu_ijk ! outgoing cosine k for E_in(i) and E_out(j)
real(8) :: mu_i1jk ! outgoing cosine k for E_in(i+1) and E_out(j)
real(8) :: prob ! probability for sampling Bragg edge
! Following are needed only for SAB_SECONDARY_CONT scattering
integer :: l ! sampled incoming E bin (is i or i + 1)
real(8) :: E_i_1, E_i_J ! endpoints on outgoing grid i
real(8) :: E_i1_1, E_i1_J ! endpoints on outgoing grid i+1
real(8) :: E_1, E_J ! endpoints interpolated between i and i+1
real(8) :: E_l_j, E_l_j1 ! adjacent E on outgoing grid l
real(8) :: p_l_j, p_l_j1 ! adjacent p on outgoing grid l
real(8) :: c_j, c_j1 ! cumulative probability
real(8) :: frac ! interpolation factor on outgoing energy
real(8) :: r1 ! RNG for outgoing energy
real(8) :: mu_left, mu_right ! adjacent mu values
real(C_DOUBLE) :: E_out
type(C_PTR) :: ptr
i_temp = micro_xs(i_nuclide) % index_temp_sab
! Sample from C++ side
ptr = C_LOC(micro_xs(i_nuclide))
call sab_tables(i_sab) % sample(ptr, E, E_out, mu)
! Get pointer to S(a,b) table
associate (sab => sab_tables(i_sab) % data(i_temp))
! Determine whether inelastic or elastic scattering will occur
if (prn() < micro_xs(i_nuclide) % thermal_elastic / &
micro_xs(i_nuclide) % thermal) then
! elastic scattering
! Get index and interpolation factor for elastic grid
if (E < sab % elastic_e_in(1)) then
i = 1
f = ZERO
else
i = binary_search(sab % elastic_e_in, sab % n_elastic_e_in, E)
f = (E - sab%elastic_e_in(i)) / &
(sab%elastic_e_in(i+1) - sab%elastic_e_in(i))
end if
! Select treatment based on elastic mode
if (sab % elastic_mode == SAB_ELASTIC_DISCRETE) then
! With this treatment, we interpolate between two discrete cosines
! corresponding to neighboring incoming energies. This is used for
! data derived in the incoherent approximation
! Sample outgoing cosine bin
k = 1 + int(prn() * sab % n_elastic_mu)
! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1)
mu_ijk = sab % elastic_mu(k,i)
mu_i1jk = sab % elastic_mu(k,i+1)
! Cosine of angle between incoming and outgoing neutron
mu = (1 - f)*mu_ijk + f*mu_i1jk
elseif (sab % elastic_mode == SAB_ELASTIC_EXACT) then
! This treatment is used for data derived in the coherent
! approximation, i.e. for crystalline structures that have Bragg
! edges.
! Sample a Bragg edge between 1 and i
prob = prn() * sab % elastic_P(i+1)
if (prob < sab % elastic_P(1)) then
k = 1
else
k = binary_search(sab % elastic_P(1:i+1), i+1, prob)
end if
! Characteristic scattering cosine for this Bragg edge
mu = ONE - TWO*sab % elastic_e_in(k) / E
end if
! Outgoing energy is same as incoming energy -- no need to do anything
else
! Perform inelastic calculations
! Get index and interpolation factor for inelastic grid
if (E < sab % inelastic_e_in(1)) then
i = 1
f = ZERO
else
i = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
f = (E - sab%inelastic_e_in(i)) / &
(sab%inelastic_e_in(i+1) - sab%inelastic_e_in(i))
end if
! Now that we have an incoming energy bin, we need to determine the
! outgoing energy bin. This will depend on the "secondary energy
! mode". If the mode is 0, then the outgoing energy bin is chosen from a
! set of equally-likely bins. If the mode is 1, then the first
! two and last two bins are skewed to have lower probabilities than the
! other bins (0.1 for the first and last bins and 0.4 for the second and
! second to last bins, relative to a normal bin probability of 1).
! Finally, if the mode is 2, then a continuous distribution (with
! accompanying PDF and CDF is utilized)
if ((sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) .or. &
(sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED)) then
if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) then
! All bins equally likely
j = 1 + int(prn() * sab % n_inelastic_e_out)
elseif (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED) then
! Distribution skewed away from edge points
! Determine number of outgoing energy and angle bins
n_energy_out = sab % n_inelastic_e_out
r = prn() * (n_energy_out - 3)
if (r > ONE) then
! equally likely N-4 middle bins
j = int(r) + 2
elseif (r > 0.6_8) then
! second to last bin has relative probability of 0.4
j = n_energy_out - 1
elseif (r > HALF) then
! last bin has relative probability of 0.1
j = n_energy_out
elseif (r > 0.1_8) then
! second bin has relative probability of 0.4
j = 2
else
! first bin has relative probability of 0.1
j = 1
end if
end if
! Determine outgoing energy corresponding to E_in(i) and E_in(i+1)
E_ij = sab % inelastic_e_out(j,i)
E_i1j = sab % inelastic_e_out(j,i+1)
! Outgoing energy
E = (1 - f)*E_ij + f*E_i1j
! Sample outgoing cosine bin
k = 1 + int(prn() * sab % n_inelastic_mu)
! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1)
mu_ijk = sab % inelastic_mu(k,j,i)
mu_i1jk = sab % inelastic_mu(k,j,i+1)
! Cosine of angle between incoming and outgoing neutron
mu = (1 - f)*mu_ijk + f*mu_i1jk
else if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_CONT) then
! Continuous secondary energy - this is to be similar to
! Law 61 interpolation on outgoing energy
! Sample between ith and (i+1)th bin
r = prn()
if (f > r) then
l = i + 1
else
l = i
end if
! Determine endpoints on grid i
n_energy_out = sab % inelastic_data(i) % n_e_out
E_i_1 = sab % inelastic_data(i) % e_out(1)
E_i_J = sab % inelastic_data(i) % e_out(n_energy_out)
! Determine endpoints on grid i + 1
n_energy_out = sab % inelastic_data(i + 1) % n_e_out
E_i1_1 = sab % inelastic_data(i + 1) % e_out(1)
E_i1_J = sab % inelastic_data(i + 1) % e_out(n_energy_out)
E_1 = E_i_1 + f * (E_i1_1 - E_i_1)
E_J = E_i_J + f * (E_i1_J - E_i_J)
! Determine outgoing energy bin
! (First reset n_energy_out to the right value)
n_energy_out = sab % inelastic_data(l) % n_e_out
r1 = prn()
c_j = sab % inelastic_data(l) % e_out_cdf(1)
do j = 1, n_energy_out - 1
c_j1 = sab % inelastic_data(l) % e_out_cdf(j + 1)
if (r1 < c_j1) exit
c_j = c_j1
end do
! check to make sure k is <= n_energy_out - 1
j = min(j, n_energy_out - 1)
! Get the data to interpolate between
E_l_j = sab % inelastic_data(l) % e_out(j)
p_l_j = sab % inelastic_data(l) % e_out_pdf(j)
! Next part assumes linear-linear interpolation in standard
E_l_j1 = sab % inelastic_data(l) % e_out(j + 1)
p_l_j1 = sab % inelastic_data(l) % e_out_pdf(j + 1)
! Find secondary energy (variable E)
frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j)
if (frac == ZERO) then
E = E_l_j + (r1 - c_j) / p_l_j
else
E = E_l_j + (sqrt(max(ZERO, p_l_j * p_l_j + &
TWO * frac * (r1 - c_j))) - p_l_j) / frac
end if
! Now interpolate between incident energy bins i and i + 1
if (l == i) then
E = E_1 + (E - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1)
else
E = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1)
end if
! Sample outgoing cosine bin
k = 1 + int(prn() * sab % n_inelastic_mu)
! Rather than use the sampled discrete mu directly, it is smeared over
! a bin of width min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
! discrete mu value itself.
associate (mu_l => sab % inelastic_data(l) % mu)
f = (r1 - c_j)/(c_j1 - c_j)
! Determine (k-1)th mu value
if (k == 1) then
mu_left = -ONE
else
mu_left = mu_l(k-1, j) + f*(mu_l(k-1, j+1) - mu_l(k-1,j))
end if
! Determine kth mu value
mu = mu_l(k, j) + f*(mu_l(k, j+1) - mu_l(k, j))
! Determine (k+1)th mu value
if (k == sab % n_inelastic_mu) then
mu_right = ONE - mu
else
mu_right = mu_l(k+1, j) + f*(mu_l(k+1, j+1) - mu_l(k+1,j)) - mu
end if
end associate
! Smear angle
mu = mu + min(mu - mu_left, mu_right - mu)*(prn() - HALF)
end if ! (inelastic secondary energy treatment)
end if ! (elastic or inelastic)
end associate
! Because of floating-point roundoff, it may be possible for mu to be
! outside of the range [-1,1). In these cases, we just set mu to exactly
! -1 or 1
if (abs(mu) > ONE) mu = sign(ONE,mu)
! change direction of particle
! Set energy to outgoing, change direction of particle
E = E_out
uvw = rotate_angle(uvw, mu)
end subroutine sab_scatter
!===============================================================================
@ -1581,7 +1329,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 +1348,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 +1372,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 +1411,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 +1440,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 +1488,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

63
src/position.cpp Normal file
View file

@ -0,0 +1,63 @@
#include "position.h"
namespace openmc {
//==============================================================================
// Position implementation
//==============================================================================
Position&
Position::operator+=(Position other)
{
x += other.x;
y += other.y;
z += other.z;
return *this;
}
Position&
Position::operator+=(double v)
{
x += v;
y += v;
z += v;
return *this;
}
Position&
Position::operator-=(Position other)
{
x -= other.x;
y -= other.y;
z -= other.z;
return *this;
}
Position&
Position::operator-=(double v)
{
x -= v;
y -= v;
z -= v;
return *this;
}
Position&
Position::operator*=(Position other)
{
x *= other.x;
y *= other.y;
z *= other.z;
return *this;
}
Position&
Position::operator*=(double v)
{
x *= v;
y *= v;
z *= v;
return *this;
}
} // namespace openmc

74
src/position.h Normal file
View file

@ -0,0 +1,74 @@
#ifndef OPENMC_POSITION_H
#define OPENMC_POSITION_H
namespace openmc {
//==============================================================================
//! Type representing a position in Cartesian coordinates
//==============================================================================
struct Position {
// Constructors
Position() = default;
Position(double x_, double y_, double z_) : x{x_}, y{y_}, z{z_} { };
Position(const double xyz[]) : x{xyz[0]}, y{xyz[1]}, z{xyz[2]} { };
// Unary operators
Position& operator+=(Position);
Position& operator+=(double);
Position& operator-=(Position);
Position& operator-=(double);
Position& operator*=(Position);
Position& operator*=(double);
const double& operator[](int i) const {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
}
}
double& operator[](int i) {
switch (i) {
case 0: return x;
case 1: return y;
case 2: return z;
}
}
// Other member functions
//! Dot product of two vectors
//! \param[in] other Vector to take dot product with
//! \result Resulting dot product
inline double dot(Position other) {
return x*other.x + y*other.y + z*other.z;
}
// Data members
double x = 0.;
double y = 0.;
double z = 0.;
};
// Binary operators
inline Position operator+(Position a, Position b) { return a += b; }
inline Position operator+(Position a, double b) { return a += b; }
inline Position operator+(double a, Position b) { return b += a; }
inline Position operator-(Position a, Position b) { return a -= b; }
inline Position operator-(Position a, double b) { return a -= b; }
inline Position operator-(double a, Position b) { return b -= a; }
inline Position operator*(Position a, Position b) { return a *= b; }
inline Position operator*(Position a, double b) { return a *= b; }
inline Position operator*(double a, Position b) { return b *= a; }
//==============================================================================
//! Type representing a vector direction in Cartesian coordinates
//==============================================================================
using Direction = Position;
} // namespace openmc
#endif // OPENMC_POSITION_H

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

@ -1,10 +1,14 @@
element materials {
element material {
(element id { xsd:int } | attribute id { xsd:int }) &
(element name { xsd:string { maxLength="52" } } |
attribute name { xsd:string { maxLength="52" } })? &
element temperature { xsd:double }? &
(element name { xsd:string } | attribute name { xsd:string })? &
(element depletable { xsd:boolean } | attribute depletable { xsd:boolean })? &
(element volume { xsd:double } | attribute volume { xsd:double })? &
(element temperature { xsd:double } | attribute temperature { xsd:double })? &
element density {
(element value { xsd:double } | attribute value { xsd:double })? &

View file

@ -15,21 +15,42 @@
<optional>
<choice>
<element name="name">
<data type="string">
<param name="maxLength">52</param>
</data>
<data type="string"/>
</element>
<attribute name="name">
<data type="string">
<param name="maxLength">52</param>
</data>
<data type="string"/>
</attribute>
</choice>
</optional>
<optional>
<element name="temperature">
<data type="double"/>
</element>
<choice>
<element name="depletable">
<data type="boolean"/>
</element>
<attribute name="depletable">
<data type="boolean"/>
</attribute>
</choice>
</optional>
<optional>
<choice>
<element name="volume">
<data type="double"/>
</element>
<attribute name="volume">
<data type="double"/>
</attribute>
</choice>
</optional>
<optional>
<choice>
<element name="temperature">
<data type="double"/>
</element>
<attribute name="temperature">
<data type="double"/>
</attribute>
</choice>
</optional>
<element name="density">
<interleave>

View file

@ -1,468 +1,172 @@
module sab_header
use, intrinsic :: ISO_C_BINDING
use, intrinsic :: ISO_FORTRAN_ENV
use algorithm, only: find, sort, binary_search
use constants
use dict_header, only: DictIntInt, DictCharInt
use distribution_univariate, only: Tabular
use error, only: warning, fatal_error
use dict_header, only: DictCharInt
use hdf5_interface
use random_lcg, only: prn
use secondary_correlated, only: CorrelatedAngleEnergy
use settings
use stl_vector, only: VectorInt, VectorReal
use string, only: to_str, str_to_int
use stl_vector, only: VectorReal
use string, only: to_c_string
implicit none
private
!===============================================================================
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
! thermal scattering collisions which utilize a continuous secondary energy
! representation.
!===============================================================================
type DistEnergySab
integer :: n_e_out
real(8), allocatable :: e_out(:)
real(8), allocatable :: e_out_pdf(:)
real(8), allocatable :: e_out_cdf(:)
real(8), allocatable :: mu(:,:)
end type DistEnergySab
public :: free_memory_sab
!===============================================================================
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
! of light isotopes such as water, graphite, Be, etc
!===============================================================================
type SabData
! threshold for S(a,b) treatment (usually ~4 eV)
real(8) :: threshold_inelastic
real(8) :: threshold_elastic = ZERO
! Inelastic scattering data
integer :: n_inelastic_e_in ! # of incoming E for inelastic
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
real(8), allocatable :: inelastic_e_in(:)
real(8), allocatable :: inelastic_sigma(:)
! The following are used only if secondary_mode is 0 or 1
real(8), allocatable :: inelastic_e_out(:,:)
real(8), allocatable :: inelastic_mu(:,:,:)
! The following is used only if secondary_mode is 3
! The different implementation is necessary because the continuous
! representation has a variable number of outgoing energy points for each
! incoming energy
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
! Elastic scattering data
integer :: elastic_mode ! elastic mode (discrete/exact)
integer :: n_elastic_e_in ! # of incoming E for elastic
integer :: n_elastic_mu ! # of outgoing angles for elastic
real(8), allocatable :: elastic_e_in(:)
real(8), allocatable :: elastic_P(:)
real(8), allocatable :: elastic_mu(:,:)
end type SabData
type SAlphaBeta
character(150) :: name ! name of table, e.g. lwtr.10t
real(8) :: awr ! weight of nucleus in neutron masses
real(8), allocatable :: kTs(:) ! temperatures in eV (k*T)
character(10), allocatable :: nuclides(:) ! List of valid nuclides
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
! cross sections and distributions at each temperature
type(SabData), allocatable :: data(:)
type, public :: SAlphaBeta
type(C_PTR) :: ptr
contains
procedure :: from_hdf5 => salphabeta_from_hdf5
procedure :: calculate_xs => sab_calculate_xs
procedure :: from_hdf5
procedure :: calculate_xs
procedure :: free
procedure :: has_nuclide
procedure :: sample
procedure :: threshold
end type SAlphaBeta
! S(a,b) tables
type(SAlphaBeta), allocatable, target :: sab_tables(:)
integer(C_INT), bind(C) :: n_sab_tables
type(DictCharInt) :: sab_dict
type(SAlphaBeta), public, allocatable, target :: sab_tables(:)
integer(C_INT), public, bind(C) :: n_sab_tables
type(DictCharInt), public :: sab_dict
interface
function sab_from_hdf5(group_id, temperature, n, method, &
tolerance, minmax) result(ptr) bind(C)
import HID_T, C_DOUBLE, C_INT, C_PTR
integer(HID_T), value :: group_id
real(C_DOUBLE), intent(in) :: temperature
integer(C_INT), value :: n
integer(C_INT), value :: method
real(C_DOUBLE), value :: tolerance
real(C_DOUBLE), intent(in) :: minmax(2)
type(C_PTR) :: ptr
end function
subroutine sab_calculate_xs(ptr, E, sqrtkT, i_temp, elastic, &
inelastic) bind(C)
import C_PTR, C_DOUBLE, C_INT
type(C_PTR), value :: ptr
real(C_DOUBLE), value :: E
real(C_DOUBLE), value :: sqrtkT
integer(C_INT), intent(out) :: i_temp
real(C_DOUBLE), intent(out) :: elastic
real(C_DOUBLE), intent(out) :: inelastic
end subroutine
subroutine sab_free(ptr) bind(C)
import C_PTR
type(C_PTR), value :: ptr
end subroutine
function sab_has_nuclide(ptr, name) result(val) bind(C)
import C_PTR, C_CHAR, C_BOOL
type(C_PTR), value :: ptr
character(kind=C_CHAR), intent(in) :: name(*)
logical(C_BOOL) :: val
end function
subroutine sab_sample(ptr, micro_xs, E_in, E_out, mu) bind(C)
import C_PTR, C_INT, C_DOUBLE
type(C_PTR), value :: ptr
type(C_PTR), value :: micro_xs
real(C_DOUBLE), value :: E_in
real(C_DOUBLE), intent(out) :: E_out
real(C_DOUBLE), intent(out) :: mu
end subroutine
function sab_threshold(ptr) result(threshold) bind(C)
import C_PTR, C_double
type(C_PTR), value :: ptr
real(C_DOUBLE) :: threshold
end function
end interface
contains
subroutine salphabeta_from_hdf5(this, group_id, temperature, method, &
subroutine from_hdf5(this, group_id, temperature, method, &
tolerance, minmax)
class(SAlphaBeta), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
type(VectorReal), intent(in) :: temperature ! list of temperatures
integer, intent(in) :: method
real(8), intent(in) :: tolerance
real(8), intent(in) :: minmax(2)
integer(C_INT), intent(in) :: method
real(C_DOUBLE), intent(in) :: tolerance
real(C_DOUBLE), intent(in) :: minmax(2)
integer :: i, j
integer :: t
integer :: n_energy, n_energy_out, n_mu
integer :: i_closest
integer :: n_temperature
integer(HID_T) :: T_group
integer(HID_T) :: elastic_group
integer(HID_T) :: inelastic_group
integer(HID_T) :: dset_id
integer(HID_T) :: kT_group
integer(HSIZE_T) :: dims2(2)
integer(HSIZE_T) :: dims3(3)
real(8), allocatable :: temp(:,:)
character(20) :: type
type(CorrelatedAngleEnergy) :: correlated_dist
real(C_DOUBLE) :: dummy
integer(C_INT) :: n
character(MAX_WORD_LEN) :: temp_str
character(MAX_WORD_LEN), allocatable :: dset_names(:)
real(8), allocatable :: temps_available(:) ! temperatures available
real(8) :: temp_desired
real(8) :: temp_actual
type(VectorInt) :: temps_to_read
! Get name of table from group
this % name = get_name(group_id)
! Get rid of leading '/'
this % name = trim(this % name(2:))
call read_attribute(this % awr, group_id, 'atomic_weight_ratio')
call read_attribute(this % nuclides, group_id, 'nuclides')
call read_attribute(type, group_id, 'secondary_mode')
select case (type)
case ('equal')
this % secondary_mode = SAB_SECONDARY_EQUAL
case ('skewed')
this % secondary_mode = SAB_SECONDARY_SKEWED
case ('continuous')
this % secondary_mode = SAB_SECONDARY_CONT
end select
! Read temperatures
kT_group = open_group(group_id, 'kTs')
! Determine temperatures available
call get_datasets(kT_group, dset_names)
allocate(temps_available(size(dset_names)))
do i = 1, size(dset_names)
! Read temperature value
call read_dataset(temps_available(i), kT_group, trim(dset_names(i)))
temps_available(i) = temps_available(i) / K_BOLTZMANN
end do
call sort(temps_available)
! Determine actual temperatures to read -- start by checking whether a
! temperature range was given, in which case all temperatures in the range
! are loaded irrespective of what temperatures actually appear in the model
if (minmax(2) > ZERO) then
do i = 1, size(temps_available)
temp_actual = temps_available(i)
if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then
call temps_to_read % push_back(nint(temp_actual))
end if
end do
n = temperature % size()
if (n > 0) then
this % ptr = sab_from_hdf5(group_id, temperature % data(1), n, method, tolerance, minmax)
else
! In this case, temperatures % data(1) doesn't exist, so we just pass a
! dummy value
this % ptr = sab_from_hdf5(group_id, dummy, n, method, tolerance, minmax)
end if
select case (method)
case (TEMPERATURE_NEAREST)
! Determine actual temperatures to read
do i = 1, temperature % size()
temp_desired = temperature % data(i)
i_closest = minloc(abs(temps_available - temp_desired), dim=1)
temp_actual = temps_available(i_closest)
if (abs(temp_actual - temp_desired) < tolerance) then
if (find(temps_to_read, nint(temp_actual)) == -1) then
call temps_to_read % push_back(nint(temp_actual))
end if
else
call fatal_error("Nuclear data library does not contain cross sections &
&for " // trim(this % name) // " at or near " // &
trim(to_str(nint(temp_desired))) // " K.")
end if
end do
case (TEMPERATURE_INTERPOLATION)
! If temperature interpolation or multipole is selected, get a list of
! bounding temperatures for each actual temperature present in the model
TEMP_LOOP: do i = 1, temperature % size()
temp_desired = temperature % data(i)
do j = 1, size(temps_available) - 1
if (temps_available(j) <= temp_desired .and. &
temp_desired < temps_available(j + 1)) then
if (find(temps_to_read, nint(temps_available(j))) == -1) then
call temps_to_read % push_back(nint(temps_available(j)))
end if
if (find(temps_to_read, nint(temps_available(j + 1))) == -1) then
call temps_to_read % push_back(nint(temps_available(j + 1)))
end if
cycle TEMP_LOOP
end if
end do
call fatal_error("Nuclear data library does not contain cross sections &
&for " // trim(this % name) // " at temperatures that bound " // &
trim(to_str(nint(temp_desired))) // " K.")
end do TEMP_LOOP
end select
! Sort temperatures to read
call sort(temps_to_read)
n_temperature = temps_to_read % size()
allocate(this % kTs(n_temperature))
allocate(this % data(n_temperature))
do t = 1, n_temperature
! Get temperature as a string
temp_str = trim(to_str(temps_to_read % data(t))) // "K"
! Read exact temperature value
call read_dataset(this % kTs(t), kT_group, temp_str)
! Open group for temperature i
T_group = open_group(group_id, temp_str)
! Coherent elastic data
if (object_exists(T_group, 'elastic')) then
! Read cross section data
elastic_group = open_group(T_group, 'elastic')
dset_id = open_dataset(elastic_group, 'xs')
call read_attribute(type, dset_id, 'type')
call get_shape(dset_id, dims2)
allocate(temp(dims2(1), dims2(2)))
call read_dataset(temp, dset_id)
call close_dataset(dset_id)
! Set cross section data and type
this % data(t) % n_elastic_e_in = int(dims2(1), 4)
allocate(this % data(t) % elastic_e_in(this % data(t) % n_elastic_e_in))
allocate(this % data(t) % elastic_P(this % data(t) % n_elastic_e_in))
this % data(t) % elastic_e_in(:) = temp(:, 1)
this % data(t) % elastic_P(:) = temp(:, 2)
select case (type)
case ('tab1')
this % data(t) % elastic_mode = SAB_ELASTIC_DISCRETE
case ('bragg')
this % data(t) % elastic_mode = SAB_ELASTIC_EXACT
end select
deallocate(temp)
! Set elastic threshold
this % data(t) % threshold_elastic = this % data(t) % elastic_e_in(&
this % data(t) % n_elastic_e_in)
! Read angle distribution
if (this % data(t) % elastic_mode /= SAB_ELASTIC_EXACT) then
dset_id = open_dataset(elastic_group, 'mu_out')
call get_shape(dset_id, dims2)
this % data(t) % n_elastic_mu = int(dims2(1), 4)
allocate(this % data(t) % elastic_mu(dims2(1), dims2(2)))
call read_dataset(this % data(t) % elastic_mu, dset_id)
call close_dataset(dset_id)
end if
call close_group(elastic_group)
end if
! Inelastic data
if (object_exists(T_group, 'inelastic')) then
! Read type of inelastic data
inelastic_group = open_group(T_group, 'inelastic')
! Read cross section data
dset_id = open_dataset(inelastic_group, 'xs')
call get_shape(dset_id, dims2)
allocate(temp(dims2(1), dims2(2)))
call read_dataset(temp, dset_id)
call close_dataset(dset_id)
! Set cross section data
this % data(t) % n_inelastic_e_in = int(dims2(1), 4)
allocate(this % data(t) % inelastic_e_in(this % data(t) % n_inelastic_e_in))
allocate(this % data(t) % inelastic_sigma(this % data(t) % n_inelastic_e_in))
this % data(t) % inelastic_e_in(:) = temp(:, 1)
this % data(t) % inelastic_sigma(:) = temp(:, 2)
deallocate(temp)
! Set inelastic threshold
this % data(t) % threshold_inelastic = this % data(t) % inelastic_e_in(&
this % data(t) % n_inelastic_e_in)
if (this % secondary_mode /= SAB_SECONDARY_CONT) then
! Read energy distribution
dset_id = open_dataset(inelastic_group, 'energy_out')
call get_shape(dset_id, dims2)
this % data(t) % n_inelastic_e_out = int(dims2(1), 4)
allocate(this % data(t) % inelastic_e_out(dims2(1), dims2(2)))
call read_dataset(this % data(t) % inelastic_e_out, dset_id)
call close_dataset(dset_id)
! Read angle distribution
dset_id = open_dataset(inelastic_group, 'mu_out')
call get_shape(dset_id, dims3)
this % data(t) % n_inelastic_mu = int(dims3(1), 4)
allocate(this % data(t) % inelastic_mu(dims3(1), dims3(2), dims3(3)))
call read_dataset(this % data(t) % inelastic_mu, dset_id)
call close_dataset(dset_id)
else
! Read correlated angle-energy distribution
call correlated_dist % from_hdf5(inelastic_group)
! Convert to S(a,b) native format
n_energy = size(correlated_dist % energy)
allocate(this % data(t) % inelastic_data(n_energy))
do i = 1, n_energy
associate (edist => correlated_dist % distribution(i))
! Get number of outgoing energies for incoming energy i
n_energy_out = size(edist % e_out)
this % data(t) % inelastic_data(i) % n_e_out = n_energy_out
allocate(this % data(t) % inelastic_data(i) % e_out(n_energy_out))
allocate(this % data(t) % inelastic_data(i) % e_out_pdf(n_energy_out))
allocate(this % data(t) % inelastic_data(i) % e_out_cdf(n_energy_out))
! Copy outgoing energy distribution
this % data(t) % inelastic_data(i) % e_out(:) = edist % e_out
this % data(t) % inelastic_data(i) % e_out_pdf(:) = edist % p
this % data(t) % inelastic_data(i) % e_out_cdf(:) = edist % c
do j = 1, n_energy_out
select type (adist => edist % angle(j) % obj)
type is (Tabular)
! On first pass, allocate space for angles
if (j == 1) then
n_mu = size(adist % x)
this % data(t) % n_inelastic_mu = n_mu
allocate(this % data(t) % inelastic_data(i) % mu(&
n_mu, n_energy_out))
end if
! Copy outgoing angles
this % data(t) % inelastic_data(i) % mu(:, j) = adist % x
end select
end do
end associate
end do
! Clear data on correlated angle-energy object
deallocate(correlated_dist % breakpoints)
deallocate(correlated_dist % interpolation)
deallocate(correlated_dist % energy)
deallocate(correlated_dist % distribution)
end if
call close_group(inelastic_group)
end if
call close_group(T_group)
end do
call close_group(kT_group)
end subroutine salphabeta_from_hdf5
end subroutine from_hdf5
!===============================================================================
! SAB_CALCULATE_XS determines the elastic and inelastic scattering
! cross-sections in the thermal energy range.
!===============================================================================
subroutine sab_calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic)
subroutine calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic)
class(SAlphaBeta), intent(in) :: this ! S(a,b) object
real(8), intent(in) :: E ! energy
real(8), intent(in) :: sqrtkT ! temperature
real(C_DOUBLE), intent(in) :: E ! energy
real(C_DOUBLE), intent(in) :: sqrtkT ! temperature
integer, intent(out) :: i_temp ! index in the S(a,b)'s temperature
real(8), intent(out) :: elastic ! thermal elastic cross section
real(8), intent(out) :: inelastic ! thermal inelastic cross section
real(C_DOUBLE), intent(out) :: elastic ! thermal elastic cross section
real(C_DOUBLE), intent(out) :: inelastic ! thermal inelastic cross section
integer :: i_grid ! index on S(a,b) energy grid
real(8) :: f ! interp factor on S(a,b) energy grid
real(8) :: kT
call sab_calculate_xs(this % ptr, E, sqrtkT, i_temp, elastic, inelastic)
end subroutine
! Determine temperature for S(a,b) table
kT = sqrtkT**2
if (temperature_method == TEMPERATURE_NEAREST) then
! If using nearest temperature, do linear search on temperature
do i_temp = 1, size(this % kTs)
if (abs(this % kTs(i_temp) - kT) < &
K_BOLTZMANN*temperature_tolerance) exit
end do
else
! Find temperatures that bound the actual temperature
do i_temp = 1, size(this % kTs) - 1
if (this % kTs(i_temp) <= kT .and. &
kT < this % kTs(i_temp + 1)) exit
end do
subroutine free(this)
class(SAlphaBeta), intent(inout) :: this
call sab_free(this % ptr)
end subroutine
! Randomly sample between temperature i and i+1
f = (kT - this % kTs(i_temp)) / &
(this % kTs(i_temp + 1) - this % kTs(i_temp))
if (f > prn()) i_temp = i_temp + 1
end if
function has_nuclide(this, name) result(val)
class(SAlphaBeta), intent(in) :: this
character(len=*), intent(in) :: name
logical(C_BOOL) :: val
val = sab_has_nuclide(this % ptr, to_c_string(name))
end function
! Get pointer to S(a,b) table
associate (sab => this % data(i_temp))
subroutine sample(this, micro_xs, E_in, E_out, mu)
class(SAlphaBeta), intent(in) :: this
type(C_PTR), value :: micro_xs
real(C_DOUBLE), value :: E_in
real(C_DOUBLE), intent(out) :: E_out
real(C_DOUBLE), intent(out) :: mu
! Get index and interpolation factor for inelastic grid
if (E < sab % inelastic_e_in(1)) then
i_grid = 1
f = ZERO
else
i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
f = (E - sab%inelastic_e_in(i_grid)) / &
(sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid))
end if
! Calculate S(a,b) inelastic scattering cross section
inelastic = (ONE - f) * sab % inelastic_sigma(i_grid) + &
f * sab % inelastic_sigma(i_grid + 1)
! Check for elastic data
if (E < sab % threshold_elastic) then
! Determine whether elastic scattering is given in the coherent or
! incoherent approximation. For coherent, the cross section is
! represented as P/E whereas for incoherent, it is simply P
if (sab % elastic_mode == SAB_ELASTIC_EXACT) then
if (E < sab % elastic_e_in(1)) then
! If energy is below that of the lowest Bragg peak, the elastic
! cross section will be zero
elastic = ZERO
else
i_grid = binary_search(sab % elastic_e_in, &
sab % n_elastic_e_in, E)
elastic = sab % elastic_P(i_grid) / E
end if
else
! Determine index on elastic energy grid
if (E < sab % elastic_e_in(1)) then
i_grid = 1
else
i_grid = binary_search(sab % elastic_e_in, &
sab % n_elastic_e_in, E)
end if
! Get interpolation factor for elastic grid
f = (E - sab%elastic_e_in(i_grid))/(sab%elastic_e_in(i_grid+1) - &
sab%elastic_e_in(i_grid))
! Calculate S(a,b) elastic scattering cross section
elastic = (ONE - f) * sab % elastic_P(i_grid) + &
f * sab % elastic_P(i_grid + 1)
end if
else
! No elastic data
elastic = ZERO
end if
end associate
end subroutine sab_calculate_xs
call sab_sample(this % ptr, micro_xs, E_in, E_out, mu)
end subroutine
function threshold(this)
class(SAlphaBeta), intent(in) :: this
real(C_DOUBLE) :: threshold
threshold = sab_threshold(this % ptr)
end function
!===============================================================================
! FREE_MEMORY_SAB deallocates global arrays defined in this module
!===============================================================================
subroutine free_memory_sab()
integer :: i
n_sab_tables = 0
if (allocated(sab_tables)) deallocate(sab_tables)
if (allocated(sab_tables)) then
do i = 1, size(sab_tables)
call sab_tables(i) % free()
end do
deallocate(sab_tables)
end if
call sab_dict % clear()
end subroutine free_memory_sab

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

@ -1,302 +0,0 @@
module secondary_correlated
use algorithm, only: binary_search
use angleenergy_header, only: AngleEnergy
use constants, only: ZERO, ONE, HALF, TWO, HISTOGRAM, LINEAR_LINEAR
use distribution_univariate, only: DistributionContainer, Tabular
use hdf5_interface
use random_lcg, only: prn
!===============================================================================
! CORRELATEDANGLEENERGY represents a correlated angle-energy distribution. This
! corresponds to ACE law 61 and ENDF File 6, LAW=1, LANG/=2.
!===============================================================================
type AngleEnergyTable
integer :: interpolation
integer :: n_discrete
real(8), allocatable :: e_out(:)
real(8), allocatable :: p(:)
real(8), allocatable :: c(:)
type(DistributionContainer), allocatable :: angle(:)
end type AngleEnergyTable
type, extends(AngleEnergy) :: CorrelatedAngleEnergy
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(AngleEnergyTable), allocatable :: distribution(:) ! outgoing E/mu distributions
contains
procedure :: sample => correlated_sample
procedure :: from_hdf5 => correlated_from_hdf5
end type CorrelatedAngleEnergy
contains
subroutine correlated_sample(this, E_in, E_out, mu)
class(CorrelatedAngleEnergy), 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 ! sapmled 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
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 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
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 (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
! Find correlated angular distribution for closest outgoing energy bin
if (r1 - c_k < c_k1 - r1) then
mu = this%distribution(l)%angle(k)%obj%sample()
else
mu = this%distribution(l)%angle(k + 1)%obj%sample()
end if
end subroutine correlated_sample
subroutine correlated_from_hdf5(this, group_id)
class(CorrelatedAngleEnergy), intent(inout) :: this
integer(HID_T), intent(in) :: group_id
integer :: i, j, k
integer :: n_energy
integer :: m, n
integer :: offset_mu
integer :: interp_mu
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(:,:)
real(8), allocatable :: mu(:,:)
! 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)
deallocate(temp)
! 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, 'energy_out')
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)
! Get outgoing angle data
dset_id = open_dataset(group_id, 'mu')
call get_shape(dset_id, dims2)
allocate(mu(dims2(1), dims2(2)))
call read_dataset(mu, 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 % angle(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)
! 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
do j = 1, n
allocate(Tabular :: this%distribution(i)%angle(j)%obj)
select type(mudist => this%distribution(i)%angle(j)%obj)
type is (Tabular)
! Get interpolation scheme
interp_mu = nint(eout(offsets(i)+j, 4))
! Determine offset and size of distribution
offset_mu = nint(eout(offsets(i)+j, 5))
if (offsets(i) + j < size(eout, 1)) then
m = nint(eout(offsets(i)+j+1, 5)) - offset_mu
else
m = size(mu, 1) - offset_mu
end if
! 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 % interpolation = interp_mu
allocate(mudist % x(m))
allocate(mudist % p(m))
allocate(mudist % c(m))
mudist % x(:) = mu(offset_mu+1:offset_mu+m, 1)
mudist % p(:) = mu(offset_mu+1:offset_mu+m, 2)
mudist % c(:) = mu(offset_mu+1:offset_mu+m, 3)
else
! Initialize tabular distribution
call mudist % initialize(mu(offset_mu+1:offset_mu+m, 1), &
mu(offset_mu+1:offset_mu+m, 2), interp_mu)
end if
end select
end do
end do
end subroutine correlated_from_hdf5
end module secondary_correlated

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,60 @@
//! \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:
//! 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
};
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;
// energy property
std::vector<double>& energy() { return energy_; }
const std::vector<double>& energy() const { return energy_; }
// distribution property
std::vector<CorrTable>& distribution() { return distribution_; }
const std::vector<CorrTable>& distribution() const { return distribution_; }
private:
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

@ -1,5 +1,6 @@
#include "settings.h"
#include "constants.h"
#include "error.h"
#include "openmc.h"
#include "string_utils.h"
@ -20,6 +21,12 @@ std::string path_multipole;
std::string path_output;
std::string path_source;
int temperature_method {TEMPERATURE_NEAREST};
bool temperature_multipole {false};
double temperature_tolerance {10.0};
double temperature_default {293.6};
std::array<double, 2> temperature_range {0.0, 0.0};
//==============================================================================
// Functions
//==============================================================================
@ -65,6 +72,32 @@ void read_settings(pugi::xml_node* root)
}
}
}
// Get temperature settings
if (check_for_node(*root, "temperature_default")) {
temperature_default = std::stod(get_node_value(*root, "temperature_default"));
}
if (check_for_node(*root, "temperature_method")) {
auto temp_str = get_node_value(*root, "temperature_method", true, true);
if (temp_str == "nearest") {
temperature_method = TEMPERATURE_NEAREST;
} else if (temp_str == "interpolation") {
temperature_method = TEMPERATURE_INTERPOLATION;
} else {
fatal_error("Unknown temperature method: " + temp_str);
}
}
if (check_for_node(*root, "temperature_tolerance")) {
temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance"));
}
if (check_for_node(*root, "temperature_multipole")) {
temperature_multipole = get_node_value_bool(*root, "temperature_multipole");
}
if (check_for_node(*root, "temperature_range")) {
auto range = get_node_array<double>(*root, "temperature_range");
temperature_range[0] = range[0];
temperature_range[1] = range[1];
}
}
} // namespace openmc
} // namespace openmc

View file

@ -4,6 +4,7 @@
//! \file settings.h
//! \brief Settings for OpenMC
#include <array>
#include <string>
#include "pugixml.hpp"
@ -31,6 +32,12 @@ extern std::string path_multipole;
extern std::string path_output;
extern std::string path_source;
extern int temperature_method;
extern bool temperature_multipole;
extern double temperature_tolerance;
extern double temperature_default;
extern std::array<double, 2> temperature_range;
//==============================================================================
//! Read settings from XML file
//! \param[in] root XML node for <settings>
@ -40,4 +47,4 @@ extern "C" void read_settings(pugi::xml_node* root);
} // namespace openmc
#endif // OPENMC_SETTINGS_H
#endif // OPENMC_SETTINGS_H

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