mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-29 06:35:48 -04:00
Merge remote-tracking branch 'upstream/develop' into cpp_geometry
This commit is contained in:
commit
8b287dc72d
192 changed files with 51525 additions and 2744 deletions
16
.travis.yml
16
.travis.yml
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -84,8 +84,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL GNU)
|
|||
# Make sure version is sufficient
|
||||
execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION)
|
||||
if(GCC_VERSION VERSION_LESS 4.8)
|
||||
message(FATAL_ERROR "gfortran version must be 4.8 or higher")
|
||||
if(GCC_VERSION VERSION_LESS 4.9)
|
||||
message(FATAL_ERROR "gcc version must be 4.9 or higher")
|
||||
endif()
|
||||
|
||||
# GCC compiler options
|
||||
|
|
@ -210,7 +210,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
|
|||
|
||||
endif()
|
||||
|
||||
list(APPEND cxxflags -std=c++11 -O2)
|
||||
list(APPEND cxxflags -std=c++14 -O2)
|
||||
if(debug)
|
||||
list(REMOVE_ITEM cxxflags -O2)
|
||||
list(APPEND cxxflags -g -O0)
|
||||
|
|
@ -236,6 +236,14 @@ message(STATUS "Linker flags: ${ldflags}")
|
|||
add_library(pugixml vendor/pugixml/pugixml.cpp)
|
||||
target_include_directories(pugixml PUBLIC vendor/pugixml/)
|
||||
|
||||
#===============================================================================
|
||||
# xtensor header-only library
|
||||
#===============================================================================
|
||||
|
||||
add_subdirectory(vendor/xtl)
|
||||
add_subdirectory(vendor/xtensor)
|
||||
target_link_libraries(xtensor INTERFACE xtl)
|
||||
|
||||
#===============================================================================
|
||||
# RPATH information
|
||||
#===============================================================================
|
||||
|
|
@ -280,7 +288,6 @@ set_target_properties(faddeeva PROPERTIES
|
|||
|
||||
add_library(libopenmc SHARED
|
||||
src/algorithm.F90
|
||||
src/angle_distribution.F90
|
||||
src/angleenergy_header.F90
|
||||
src/bank_header.F90
|
||||
src/api.F90
|
||||
|
|
@ -298,7 +305,6 @@ add_library(libopenmc SHARED
|
|||
src/eigenvalue.F90
|
||||
src/endf.F90
|
||||
src/endf_header.F90
|
||||
src/energy_distribution.F90
|
||||
src/error.F90
|
||||
src/geometry.F90
|
||||
src/geometry_header.F90
|
||||
|
|
@ -326,7 +332,6 @@ add_library(libopenmc SHARED
|
|||
src/physics_mg.F90
|
||||
src/plot.F90
|
||||
src/plot_header.F90
|
||||
src/product_header.F90
|
||||
src/progress_header.F90
|
||||
src/pugixml/pugixml_f.F90
|
||||
src/random_lcg.F90
|
||||
|
|
@ -334,9 +339,6 @@ add_library(libopenmc SHARED
|
|||
src/relaxng
|
||||
src/sab_header.F90
|
||||
src/secondary_correlated.F90
|
||||
src/secondary_kalbach.F90
|
||||
src/secondary_nbody.F90
|
||||
src/secondary_uncorrelated.F90
|
||||
src/set_header.F90
|
||||
src/settings.F90
|
||||
src/simulation_header.F90
|
||||
|
|
@ -384,12 +386,19 @@ add_library(libopenmc SHARED
|
|||
src/tallies/trigger.F90
|
||||
src/tallies/trigger_header.F90
|
||||
src/cell.cpp
|
||||
src/distribution.cpp
|
||||
src/distribution_angle.cpp
|
||||
src/distribution_energy.cpp
|
||||
src/distribution_multi.cpp
|
||||
src/distribution_spatial.cpp
|
||||
src/endf.cpp
|
||||
src/initialize.cpp
|
||||
src/finalize.cpp
|
||||
src/geometry.cpp
|
||||
src/geometry_aux.cpp
|
||||
src/hdf5_interface.cpp
|
||||
src/lattice.cpp
|
||||
src/material.cpp
|
||||
src/math_functions.cpp
|
||||
src/message_passing.cpp
|
||||
src/mgxs.cpp
|
||||
|
|
@ -400,6 +409,12 @@ add_library(libopenmc SHARED
|
|||
src/position.cpp
|
||||
src/pugixml/pugixml_c.cpp
|
||||
src/random_lcg.cpp
|
||||
src/reaction.cpp
|
||||
src/reaction_product.cpp
|
||||
src/secondary_correlated.cpp
|
||||
src/secondary_kalbach.cpp
|
||||
src/secondary_nbody.cpp
|
||||
src/secondary_uncorrelated.cpp
|
||||
src/scattdata.cpp
|
||||
src/settings.cpp
|
||||
src/simulation.cpp
|
||||
|
|
@ -452,7 +467,7 @@ endif()
|
|||
# target_link_libraries treats any arguments starting with - but not -l as
|
||||
# linker flags. Thus, we can pass both linker flags and libraries together.
|
||||
target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml
|
||||
faddeeva)
|
||||
faddeeva xtensor)
|
||||
|
||||
#===============================================================================
|
||||
# openmc executable
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ development team will be happy to discuss it.
|
|||
## How to Submit Changes
|
||||
|
||||
All changes to OpenMC happen through pull requests. For a full overview of the
|
||||
process, see the developer's guide section on [Development
|
||||
Workflow](http://openmc.readthedocs.io/en/latest/devguide/workflow.html).
|
||||
process, see the developer's guide section on [Contributing to
|
||||
OpenMC](http://openmc.readthedocs.io/en/latest/devguide/contributing.html).
|
||||
|
||||
## Code Style
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
129
docs/source/devguide/contributing.rst
Normal file
129
docs/source/devguide/contributing.rst
Normal 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ Constructing Tallies
|
|||
openmc.SpatialLegendreFilter
|
||||
openmc.SphericalHarmonicsFilter
|
||||
openmc.ZernikeFilter
|
||||
openmc.ZernikeRadialFilter
|
||||
openmc.ParticleFilter
|
||||
openmc.Mesh
|
||||
openmc.Trigger
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from contextlib import contextmanager
|
||||
from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, c_char,
|
||||
POINTER, Structure, c_void_p, create_string_buffer)
|
||||
from ctypes import (CDLL, c_bool, c_int, c_int32, c_int64, c_double, c_char_p,
|
||||
c_char, POINTER, Structure, c_void_p, create_string_buffer)
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
|
|
@ -52,7 +52,7 @@ _dll.openmc_simulation_init.restype = c_int
|
|||
_dll.openmc_simulation_init.errcheck = _error_handler
|
||||
_dll.openmc_simulation_finalize.restype = c_int
|
||||
_dll.openmc_simulation_finalize.errcheck = _error_handler
|
||||
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)]
|
||||
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p), POINTER(c_bool)]
|
||||
_dll.openmc_statepoint_write.restype = c_int
|
||||
_dll.openmc_statepoint_write.errcheck = _error_handler
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ def source_bank():
|
|||
return as_array(ptr, (n.value,)).view(bank_dtype)
|
||||
|
||||
|
||||
def statepoint_write(filename=None):
|
||||
def statepoint_write(filename=None, write_source=True):
|
||||
"""Write a statepoint file.
|
||||
|
||||
Parameters
|
||||
|
|
@ -277,11 +277,13 @@ def statepoint_write(filename=None):
|
|||
filename : str or None
|
||||
Path to the statepoint to write. If None is passed, a default name that
|
||||
contains the current batch will be written.
|
||||
write_source : bool
|
||||
Whether or not to include the source bank in the statepoint.
|
||||
|
||||
"""
|
||||
if filename is not None:
|
||||
filename = c_char_p(filename.encode())
|
||||
_dll.openmc_statepoint_write(filename)
|
||||
_dll.openmc_statepoint_write(filename, c_bool(write_source))
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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 = (
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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
21
src/angle_energy.h
Normal 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
|
||||
13
src/api.F90
13
src/api.F90
|
|
@ -37,8 +37,6 @@ module openmc_api
|
|||
public :: openmc_calculate_volumes
|
||||
public :: openmc_cell_filter_get_bins
|
||||
public :: openmc_cell_get_id
|
||||
public :: openmc_cell_get_fill
|
||||
public :: openmc_cell_set_fill
|
||||
public :: openmc_cell_set_id
|
||||
public :: openmc_cell_set_temperature
|
||||
public :: openmc_energy_filter_get_bins
|
||||
|
|
@ -214,7 +212,7 @@ contains
|
|||
if (p % material == MATERIAL_VOID) then
|
||||
id = 0
|
||||
else
|
||||
id = materials(p % material) % id
|
||||
id = materials(p % material) % id()
|
||||
end if
|
||||
end if
|
||||
instance = p % cell_instance - 1
|
||||
|
|
@ -259,7 +257,6 @@ contains
|
|||
if (allocated(tallies)) then
|
||||
do i = 1, size(tallies)
|
||||
associate (t => tallies(i) % obj)
|
||||
t % active = .false.
|
||||
t % n_realizations = 0
|
||||
if (allocated(t % results)) then
|
||||
t % results(:, :, :) = ZERO
|
||||
|
|
@ -278,14 +275,6 @@ contains
|
|||
k_abs_tra = ZERO
|
||||
k_sum(:) = ZERO
|
||||
|
||||
! Clear active tally lists
|
||||
call active_analog_tallies % clear()
|
||||
call active_tracklength_tallies % clear()
|
||||
call active_meshsurf_tallies % clear()
|
||||
call active_collision_tallies % clear()
|
||||
call active_surface_tallies % clear()
|
||||
call active_tallies % clear()
|
||||
|
||||
! Reset timers
|
||||
call time_total % reset()
|
||||
call time_total % reset()
|
||||
|
|
|
|||
176
src/cell.cpp
176
src/cell.cpp
|
|
@ -1,7 +1,6 @@
|
|||
#include "cell.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -10,6 +9,8 @@
|
|||
#include "geometry.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "lattice.h"
|
||||
#include "material.h"
|
||||
#include "openmc.h"
|
||||
#include "settings.h"
|
||||
#include "surface.h"
|
||||
#include "xml_interface.h"
|
||||
|
|
@ -21,14 +22,13 @@ namespace openmc {
|
|||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
// TODO: Convert to enum
|
||||
constexpr int32_t OP_LEFT_PAREN {std::numeric_limits<int32_t>::max()};
|
||||
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
|
||||
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
|
||||
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
|
||||
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
|
||||
|
||||
extern "C" double FP_PRECISION;
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
|
@ -201,52 +201,64 @@ generate_rpn(int32_t cell_id, std::vector<int32_t> infix)
|
|||
Cell::Cell(pugi::xml_node cell_node)
|
||||
{
|
||||
if (check_for_node(cell_node, "id")) {
|
||||
id = stoi(get_node_value(cell_node, "id"));
|
||||
id = std::stoi(get_node_value(cell_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of cell in geometry XML file.");
|
||||
}
|
||||
|
||||
//TODO: don't automatically lowercase cell and surface names
|
||||
if (check_for_node(cell_node, "name")) {
|
||||
name = get_node_value(cell_node, "name");
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "universe")) {
|
||||
universe = stoi(get_node_value(cell_node, "universe"));
|
||||
universe = std::stoi(get_node_value(cell_node, "universe"));
|
||||
} else {
|
||||
universe = 0;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "fill")) {
|
||||
fill = stoi(get_node_value(cell_node, "fill"));
|
||||
} else {
|
||||
fill = C_NONE;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "material")) {
|
||||
//TODO: read material ids.
|
||||
material.push_back(C_NONE+1);
|
||||
material.shrink_to_fit();
|
||||
} else {
|
||||
material.push_back(C_NONE);
|
||||
material.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Make sure that either material or fill was specified.
|
||||
if ((material[0] == C_NONE) && (fill == C_NONE)) {
|
||||
// Make sure that either material or fill was specified, but not both.
|
||||
bool fill_present = check_for_node(cell_node, "fill");
|
||||
bool material_present = check_for_node(cell_node, "material");
|
||||
if (!(fill_present || material_present)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Neither material nor fill was specified for cell " << id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
// Make sure that material and fill haven't been specified simultaneously.
|
||||
if ((material[0] != C_NONE) && (fill != C_NONE)) {
|
||||
if (fill_present && material_present) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Cell " << id << " has both a material and a fill specified; "
|
||||
<< "only one can be specified per cell";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
if (fill_present) {
|
||||
fill = std::stoi(get_node_value(cell_node, "fill"));
|
||||
} else {
|
||||
fill = C_NONE;
|
||||
}
|
||||
|
||||
// Read the material element. There can be zero materials (filled with a
|
||||
// universe), more than one material (distribmats), and some materials may
|
||||
// be "void".
|
||||
if (material_present) {
|
||||
std::vector<std::string> mats
|
||||
{get_node_array<std::string>(cell_node, "material", true)};
|
||||
if (mats.size() > 0) {
|
||||
material.reserve(mats.size());
|
||||
for (std::string mat : mats) {
|
||||
if (mat.compare("void") == 0) {
|
||||
material.push_back(MATERIAL_VOID);
|
||||
} else {
|
||||
material.push_back(std::stoi(mat));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "An empty material element was specified for cell " << id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the region specification.
|
||||
std::string region_spec;
|
||||
if (check_for_node(cell_node, "region")) {
|
||||
|
|
@ -305,7 +317,7 @@ Cell::distance(Position r, Direction u, int32_t on_surface) const
|
|||
// Calculate the distance to this surface.
|
||||
// Note the off-by-one indexing
|
||||
bool coincident {token == on_surface};
|
||||
double d {surfaces_c[abs(token)-1]->distance(r, u, coincident)};
|
||||
double d {global_surfaces[abs(token)-1]->distance(r, u, coincident)};
|
||||
|
||||
// Check if this distance is the new minimum.
|
||||
if (d < min_dist) {
|
||||
|
|
@ -346,7 +358,8 @@ Cell::to_hdf5(hid_t cell_group) const
|
|||
region_spec << " |";
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token);
|
||||
region_spec << " "
|
||||
<< copysign(global_surfaces[abs(token)-1]->id, token);
|
||||
}
|
||||
}
|
||||
write_string(cell_group, "region", region_spec.str(), false);
|
||||
|
|
@ -369,7 +382,7 @@ Cell::contains_simple(Position r, Direction u, int32_t on_surface) const
|
|||
return false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(r, u);
|
||||
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
|
||||
if (sense != (token > 0)) {return false;}
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +424,7 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
|
|||
stack[i_stack] = false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(r, u);;
|
||||
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
|
||||
stack[i_stack] = (sense == (token > 0));
|
||||
}
|
||||
}
|
||||
|
|
@ -433,7 +446,7 @@ Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
|
|||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_cells(pugi::xml_node *node)
|
||||
read_cells(pugi::xml_node* node)
|
||||
{
|
||||
// Count the number of cells.
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {n_cells++;}
|
||||
|
|
@ -441,10 +454,8 @@ read_cells(pugi::xml_node *node)
|
|||
fatal_error("No cells found in geometry.xml!");
|
||||
}
|
||||
|
||||
// Allocate the vector of Cells.
|
||||
global_cells.reserve(n_cells);
|
||||
|
||||
// Loop over XML cell elements and populate the array.
|
||||
global_cells.reserve(n_cells);
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {
|
||||
global_cells.push_back(new Cell(cell_node));
|
||||
}
|
||||
|
|
@ -462,11 +473,72 @@ read_cells(pugi::xml_node *node)
|
|||
global_universes[it->second]->cells.push_back(i);
|
||||
}
|
||||
}
|
||||
global_universes.shrink_to_fit();
|
||||
|
||||
// Allocate the cell overlap count if necessary.
|
||||
if (openmc_check_overlaps) overlap_check_count.resize(n_cells, 0);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// C-API functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n)
|
||||
{
|
||||
if (index >= 1 && index <= global_cells.size()) {
|
||||
//TODO: off-by-one
|
||||
Cell& c {*global_cells[index - 1]};
|
||||
*type = c.type;
|
||||
if (c.type == FILL_MATERIAL) {
|
||||
*indices = c.material.data();
|
||||
*n = c.material.size();
|
||||
} else {
|
||||
*indices = &c.fill;
|
||||
*n = 1;
|
||||
}
|
||||
} else {
|
||||
strcpy(openmc_err_msg, "Index in cells array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_cell_set_fill(int32_t index, int type, int32_t n,
|
||||
const int32_t* indices)
|
||||
{
|
||||
if (index >= 1 && index <= global_cells.size()) {
|
||||
//TODO: off-by-one
|
||||
Cell& c {*global_cells[index - 1]};
|
||||
if (type == FILL_MATERIAL) {
|
||||
c.type = FILL_MATERIAL;
|
||||
c.material.clear();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int i_mat = indices[i];
|
||||
if (i_mat == MATERIAL_VOID) {
|
||||
c.material.push_back(MATERIAL_VOID);
|
||||
} else if (i_mat >= 1 && i_mat <= global_materials.size()) {
|
||||
//TODO: off-by-one
|
||||
c.material.push_back(i_mat - 1);
|
||||
} else {
|
||||
strcpy(openmc_err_msg, "Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
c.material.shrink_to_fit();
|
||||
} else if (type == FILL_UNIVERSE) {
|
||||
c.type = FILL_UNIVERSE;
|
||||
} else {
|
||||
c.type = FILL_LATTICE;
|
||||
}
|
||||
} else {
|
||||
strcpy(openmc_err_msg, "Index in cells array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
|
@ -474,35 +546,39 @@ read_cells(pugi::xml_node *node)
|
|||
extern "C" {
|
||||
Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];}
|
||||
|
||||
int32_t cell_id(Cell *c) {return c->id;}
|
||||
int32_t cell_id(Cell* c) {return c->id;}
|
||||
|
||||
void cell_set_id(Cell *c, int32_t id) {c->id = id;}
|
||||
void cell_set_id(Cell* c, int32_t id) {c->id = id;}
|
||||
|
||||
int cell_type(Cell *c) {return c->type;}
|
||||
int cell_type(Cell* c) {return c->type;}
|
||||
|
||||
void cell_set_type(Cell *c, int type) {c->type = type;}
|
||||
int32_t cell_universe(Cell* c) {return c->universe;}
|
||||
|
||||
int32_t cell_universe(Cell *c) {return c->universe;}
|
||||
int32_t cell_fill(Cell* c) {return c->fill;}
|
||||
|
||||
void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;}
|
||||
int32_t cell_n_instances(Cell* c) {return c->n_instances;}
|
||||
|
||||
int32_t cell_fill(Cell *c) {return c->fill;}
|
||||
int cell_material_size(Cell* c) {return c->material.size();}
|
||||
|
||||
int32_t* cell_fill_ptr(Cell *c) {return &c->fill;}
|
||||
//TODO: off-by-one
|
||||
int32_t cell_material(Cell* c, int i)
|
||||
{
|
||||
int32_t mat = c->material[i-1];
|
||||
if (mat == MATERIAL_VOID) return MATERIAL_VOID;
|
||||
return mat + 1;
|
||||
}
|
||||
|
||||
int32_t cell_n_instances(Cell *c) {return c->n_instances;}
|
||||
bool cell_simple(Cell* c) {return c->simple;}
|
||||
|
||||
bool cell_simple(Cell *c) {return c->simple;}
|
||||
|
||||
bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface)
|
||||
bool cell_contains(Cell* c, double xyz[3], double uvw[3], int32_t on_surface)
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
return c->contains(r, u, on_surface);
|
||||
}
|
||||
|
||||
void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface,
|
||||
double *min_dist, int32_t *i_surf)
|
||||
void cell_distance(Cell* c, double xyz[3], double uvw[3], int32_t on_surface,
|
||||
double* min_dist, int32_t* i_surf)
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
|
|
@ -511,9 +587,9 @@ extern "C" {
|
|||
*i_surf = out.second;
|
||||
}
|
||||
|
||||
int32_t cell_offset(Cell *c, int map) {return c->offset[map];}
|
||||
int32_t cell_offset(Cell* c, int map) {return c->offset[map];}
|
||||
|
||||
void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);}
|
||||
void cell_to_hdf5(Cell* c, hid_t group) {c->to_hdf5(group);}
|
||||
|
||||
void extend_cells_c(int32_t n)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define OPENMC_CELL_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
@ -18,6 +19,7 @@ namespace openmc {
|
|||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
// TODO: Convert to enum
|
||||
extern "C" int FILL_MATERIAL;
|
||||
extern "C" int FILL_UNIVERSE;
|
||||
extern "C" int FILL_LATTICE;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -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 :: &
|
||||
|
|
|
|||
402
src/constants.h
402
src/constants.h
|
|
@ -1,8 +1,8 @@
|
|||
//! \file constants.h
|
||||
//! A collection of constants
|
||||
|
||||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
#ifndef OPENMC_CONSTANTS_H
|
||||
#define OPENMC_CONSTANTS_H
|
||||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace openmc {
|
||||
|
||||
// TODO: Replace with xtensor/other library?
|
||||
typedef std::vector<double> double_1dvec;
|
||||
typedef std::vector<std::vector<double> > double_2dvec;
|
||||
typedef std::vector<std::vector<std::vector<double> > > double_3dvec;
|
||||
|
|
@ -21,29 +22,283 @@ typedef std::vector<int> int_1dvec;
|
|||
typedef std::vector<std::vector<int> > int_2dvec;
|
||||
typedef std::vector<std::vector<std::vector<int> > > int_3dvec;
|
||||
|
||||
constexpr int MAX_SAMPLE {10000};
|
||||
// ============================================================================
|
||||
// VERSIONING NUMBERS
|
||||
|
||||
constexpr std::array<int, 3> VERSION {0, 10, 0};
|
||||
// OpenMC major, minor, and release numbers
|
||||
constexpr int VERSION_MAJOR {0};
|
||||
constexpr int VERSION_MINOR {10};
|
||||
constexpr int VERSION_RELEASE {0};
|
||||
constexpr std::array<int, 3> VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE};
|
||||
|
||||
// HDF5 data format
|
||||
constexpr int HDF5_VERSION[] {1, 0};
|
||||
|
||||
// Version numbers for binary files
|
||||
constexpr std::array<int, 2> VERSION_PARTICLE_RESTART {2, 0};
|
||||
constexpr std::array<int, 2> VERSION_TRACK {2, 0};
|
||||
constexpr std::array<int, 2> VERSION_SUMMARY {6, 0};
|
||||
constexpr std::array<int, 2> VERSION_VOLUME {1, 0};
|
||||
constexpr std::array<int, 2> VERSION_VOXEL {1, 0};
|
||||
constexpr std::array<int, 2> VERSION_MGXS_LIBRARY {1, 0};
|
||||
constexpr char VERSION_MULTIPOLE[] {"v0.2"};
|
||||
|
||||
// ============================================================================
|
||||
// ADJUSTABLE PARAMETERS
|
||||
|
||||
// NOTE: This is the only section of the constants module that should ever be
|
||||
// adjusted. Modifying constants in other sections may cause the code to fail.
|
||||
|
||||
// Monoatomic ideal-gas scattering treatment threshold
|
||||
constexpr double FREE_GAS_THRESHOLD {400.0};
|
||||
|
||||
// Significance level for confidence intervals
|
||||
constexpr double CONFIDENCE_LEVEL {0.95};
|
||||
|
||||
// Used for surface current tallies
|
||||
constexpr double TINY_BIT {1e-8};
|
||||
|
||||
// User for precision in geometry
|
||||
constexpr double FP_PRECISION {1e-14};
|
||||
constexpr double FP_REL_PRECISION {1e-5};
|
||||
constexpr double FP_COINCIDENT {1e-12};
|
||||
|
||||
// Maximum number of collisions/crossings
|
||||
constexpr int MAX_EVENTS {1000000};
|
||||
constexpr int MAX_SAMPLE {100000};
|
||||
|
||||
// Maximum number of words in a single line, length of line, and length of
|
||||
// single word
|
||||
constexpr int MAX_WORDS {500};
|
||||
constexpr int MAX_LINE_LEN {250};
|
||||
constexpr int MAX_WORD_LEN {150};
|
||||
constexpr int MAX_FILE_LEN {255};
|
||||
|
||||
// Physical Constants
|
||||
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
|
||||
// Maximum number of external source spatial resamples to encounter before an
|
||||
// error is thrown.
|
||||
constexpr int EXTSRC_REJECT_THRESHOLD {10000};
|
||||
constexpr double EXTSRC_REJECT_FRACTION {0.05};
|
||||
|
||||
// ============================================================================
|
||||
// MATH AND PHYSICAL CONSTANTS
|
||||
|
||||
// Values here are from the Committee on Data for Science and Technology
|
||||
// (CODATA) 2014 recommendation (doi:10.1103/RevModPhys.88.035009).
|
||||
|
||||
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
|
||||
// use so for now we will reuse the Fortran constant until we are OK with
|
||||
// modifying test results
|
||||
constexpr double PI {3.1415926535898};
|
||||
const double SQRT_PI {std::sqrt(PI)};
|
||||
constexpr double INFTY {std::numeric_limits<double>::max()};
|
||||
|
||||
// Physical constants
|
||||
constexpr double MASS_NEUTRON {1.00866491588}; // mass of a neutron in amu
|
||||
constexpr double MASS_NEUTRON_EV {939.5654133e6}; // mass of a neutron in eV/c^2
|
||||
constexpr double MASS_PROTON {1.007276466879}; // mass of a proton in amu
|
||||
constexpr double MASS_ELECTRON_EV {0.5109989461e6}; // electron mass energy equivalent in eV/c^2
|
||||
constexpr double FINE_STRUCTURE {137.035999139}; // inverse fine structure constant
|
||||
constexpr double PLANCK_C {1.2398419739062977e4}; // Planck's constant times c in eV-Angstroms
|
||||
constexpr double AMU {1.660539040e-27}; // 1 amu in kg
|
||||
constexpr double C_LIGHT {2.99792458e8}; // speed of light in m/s
|
||||
constexpr double N_AVOGADRO {0.6022140857}; // Avogadro's number in 10^24/mol
|
||||
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
|
||||
|
||||
// Electron subshell labels
|
||||
constexpr char SUBSHELLS[][4] {
|
||||
"K ", "L1 ", "L2 ", "L3 ", "M1 ", "M2 ", "M3 ", "M4 ", "M5 ",
|
||||
"N1 ", "N2 ", "N3 ", "N4 ", "N5 ", "N6 ", "N7 ", "O1 ", "O2 ",
|
||||
"O3 ", "O4 ", "O5 ", "O6 ", "O7 ", "O8 ", "O9 ", "P1 ", "P2 ",
|
||||
"P3 ", "P4 ", "P5 ", "P6 ", "P7 ", "P8 ", "P9 ", "P10", "P11",
|
||||
"Q1 ", "Q2 ", "Q3 "
|
||||
};
|
||||
|
||||
// Void material
|
||||
// TODO: refactor and remove
|
||||
constexpr int MATERIAL_VOID {-1};
|
||||
|
||||
// ============================================================================
|
||||
// CROSS SECTION RELATED CONSTANTS
|
||||
|
||||
// Angular distribution type
|
||||
// TODO: Convert to enum
|
||||
constexpr int ANGLE_ISOTROPIC {1};
|
||||
constexpr int ANGLE_32_EQUI {2};
|
||||
constexpr int ANGLE_TABULAR {3};
|
||||
constexpr int ANGLE_LEGENDRE {4};
|
||||
constexpr int ANGLE_HISTOGRAM {5};
|
||||
|
||||
// Temperature treatment method
|
||||
// TODO: Convert to enum?
|
||||
constexpr int TEMPERATURE_NEAREST {1};
|
||||
constexpr int TEMPERATURE_INTERPOLATION {2};
|
||||
|
||||
// Secondary energy mode for S(a,b) inelastic scattering
|
||||
// TODO: Convert to enum
|
||||
constexpr int SAB_SECONDARY_EQUAL {0}; // Equally-likely outgoing energy bins
|
||||
constexpr int SAB_SECONDARY_SKEWED {1}; // Skewed outgoing energy bins
|
||||
constexpr int SAB_SECONDARY_CONT {2}; // Continuous, linear-linear interpolation
|
||||
|
||||
// Elastic mode for S(a,b) elastic scattering
|
||||
// TODO: Convert to enum
|
||||
constexpr int SAB_ELASTIC_DISCRETE {3}; // Sample from discrete cosines
|
||||
constexpr int SAB_ELASTIC_EXACT {4}; // Exact treatment for coherent elastic
|
||||
|
||||
// Reaction types
|
||||
// TODO: Convert to enum
|
||||
constexpr int TOTAL_XS {1};
|
||||
constexpr int ELASTIC {2};
|
||||
constexpr int N_NONELASTIC {3};
|
||||
constexpr int N_LEVEL {4};
|
||||
constexpr int MISC {5};
|
||||
constexpr int N_2ND {11};
|
||||
constexpr int N_2N {16};
|
||||
constexpr int N_3N {17};
|
||||
constexpr int N_FISSION {18};
|
||||
constexpr int N_F {19};
|
||||
constexpr int N_NF {20};
|
||||
constexpr int N_2NF {21};
|
||||
constexpr int N_NA {22};
|
||||
constexpr int N_N3A {23};
|
||||
constexpr int N_2NA {24};
|
||||
constexpr int N_3NA {25};
|
||||
constexpr int N_NP {28};
|
||||
constexpr int N_N2A {29};
|
||||
constexpr int N_2N2A {30};
|
||||
constexpr int N_ND {32};
|
||||
constexpr int N_NT {33};
|
||||
constexpr int N_N3HE {34};
|
||||
constexpr int N_ND2A {35};
|
||||
constexpr int N_NT2A {36};
|
||||
constexpr int N_4N {37};
|
||||
constexpr int N_3NF {38};
|
||||
constexpr int N_2NP {41};
|
||||
constexpr int N_3NP {42};
|
||||
constexpr int N_N2P {44};
|
||||
constexpr int N_NPA {45};
|
||||
constexpr int N_N1 {51};
|
||||
constexpr int N_N40 {90};
|
||||
constexpr int N_NC {91};
|
||||
constexpr int N_DISAPPEAR {101};
|
||||
constexpr int N_GAMMA {102};
|
||||
constexpr int N_P {103};
|
||||
constexpr int N_D {104};
|
||||
constexpr int N_T {105};
|
||||
constexpr int N_3HE {106};
|
||||
constexpr int N_A {107};
|
||||
constexpr int N_2A {108};
|
||||
constexpr int N_3A {109};
|
||||
constexpr int N_2P {111};
|
||||
constexpr int N_PA {112};
|
||||
constexpr int N_T2A {113};
|
||||
constexpr int N_D2A {114};
|
||||
constexpr int N_PD {115};
|
||||
constexpr int N_PT {116};
|
||||
constexpr int N_DA {117};
|
||||
constexpr int N_5N {152};
|
||||
constexpr int N_6N {153};
|
||||
constexpr int N_2NT {154};
|
||||
constexpr int N_TA {155};
|
||||
constexpr int N_4NP {156};
|
||||
constexpr int N_3ND {157};
|
||||
constexpr int N_NDA {158};
|
||||
constexpr int N_2NPA {159};
|
||||
constexpr int N_7N {160};
|
||||
constexpr int N_8N {161};
|
||||
constexpr int N_5NP {162};
|
||||
constexpr int N_6NP {163};
|
||||
constexpr int N_7NP {164};
|
||||
constexpr int N_4NA {165};
|
||||
constexpr int N_5NA {166};
|
||||
constexpr int N_6NA {167};
|
||||
constexpr int N_7NA {168};
|
||||
constexpr int N_4ND {169};
|
||||
constexpr int N_5ND {170};
|
||||
constexpr int N_6ND {171};
|
||||
constexpr int N_3NT {172};
|
||||
constexpr int N_4NT {173};
|
||||
constexpr int N_5NT {174};
|
||||
constexpr int N_6NT {175};
|
||||
constexpr int N_2N3HE {176};
|
||||
constexpr int N_3N3HE {177};
|
||||
constexpr int N_4N3HE {178};
|
||||
constexpr int N_3N2P {179};
|
||||
constexpr int N_3N3A {180};
|
||||
constexpr int N_3NPA {181};
|
||||
constexpr int N_DT {182};
|
||||
constexpr int N_NPD {183};
|
||||
constexpr int N_NPT {184};
|
||||
constexpr int N_NDT {185};
|
||||
constexpr int N_NP3HE {186};
|
||||
constexpr int N_ND3HE {187};
|
||||
constexpr int N_NT3HE {188};
|
||||
constexpr int N_NTA {189};
|
||||
constexpr int N_2N2P {190};
|
||||
constexpr int N_P3HE {191};
|
||||
constexpr int N_D3HE {192};
|
||||
constexpr int N_3HEA {193};
|
||||
constexpr int N_4N2P {194};
|
||||
constexpr int N_4N2A {195};
|
||||
constexpr int N_4NPA {196};
|
||||
constexpr int N_3P {197};
|
||||
constexpr int N_N3P {198};
|
||||
constexpr int N_3N2PA {199};
|
||||
constexpr int N_5N2P {200};
|
||||
constexpr int COHERENT {502};
|
||||
constexpr int INCOHERENT {504};
|
||||
constexpr int PAIR_PROD_ELEC {515};
|
||||
constexpr int PAIR_PROD {516};
|
||||
constexpr int PAIR_PROD_NUC {517};
|
||||
constexpr int PHOTOELECTRIC {522};
|
||||
constexpr int N_P0 {600};
|
||||
constexpr int N_PC {649};
|
||||
constexpr int N_D0 {650};
|
||||
constexpr int N_DC {699};
|
||||
constexpr int N_T0 {700};
|
||||
constexpr int N_TC {749};
|
||||
constexpr int N_3HE0 {750};
|
||||
constexpr int N_3HEC {799};
|
||||
constexpr int N_A0 {800};
|
||||
constexpr int N_AC {849};
|
||||
constexpr int N_2N0 {875};
|
||||
constexpr int N_2NC {891};
|
||||
|
||||
// Fission neutron emission (nu) type
|
||||
constexpr int NU_NONE {0}; // No nu values (non-fissionable)
|
||||
constexpr int NU_POLYNOMIAL {1}; // Nu values given by polynomial
|
||||
constexpr int NU_TABULAR {2}; // Nu values given by tabular distribution
|
||||
|
||||
// Library types
|
||||
constexpr int LIBRARY_NEUTRON {1};
|
||||
constexpr int LIBRARY_THERMAL {2};
|
||||
constexpr int LIBRARY_PHOTON {3};
|
||||
constexpr int LIBRARY_MULTIGROUP {4};
|
||||
|
||||
// Probability table parameters
|
||||
constexpr int URR_CUM_PROB {1};
|
||||
constexpr int URR_TOTAL {2};
|
||||
constexpr int URR_ELASTIC {3};
|
||||
constexpr int URR_FISSION {4};
|
||||
constexpr int URR_N_GAMMA {5};
|
||||
constexpr int URR_HEATING {6};
|
||||
|
||||
// Maximum number of partial fission reactions
|
||||
constexpr int PARTIAL_FISSION_MAX {4};
|
||||
|
||||
// Resonance elastic scattering methods
|
||||
// TODO: Convert to enum
|
||||
constexpr int RES_SCAT_ARES {1};
|
||||
constexpr int RES_SCAT_DBRC {2};
|
||||
constexpr int RES_SCAT_WCM {3};
|
||||
constexpr int RES_SCAT_CXS {4};
|
||||
|
||||
// Electron treatments
|
||||
// TODO: Convert to enum
|
||||
constexpr int ELECTRON_LED {1}; // Local Energy Deposition
|
||||
constexpr int ELECTRON_TTB {2}; // Thick Target Bremsstrahlung
|
||||
|
||||
// ============================================================================
|
||||
// MULTIGROUP RELATED
|
||||
|
||||
// MGXS Table Types
|
||||
// TODO: Convert to enum
|
||||
constexpr int MGXS_ISOTROPIC {1}; // Isotroically weighted data
|
||||
constexpr int MGXS_ANGLE {2}; // Data by angular bins
|
||||
|
||||
|
|
@ -53,18 +308,8 @@ constexpr double MACROSCOPIC_AWR {-2.};
|
|||
// Number of mu bins to use when converting Legendres to tabular type
|
||||
constexpr int DEFAULT_NMU {33};
|
||||
|
||||
// Temperature treatment method
|
||||
constexpr int TEMPERATURE_NEAREST {1};
|
||||
constexpr int TEMPERATURE_INTERPOLATION {2};
|
||||
|
||||
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
|
||||
// use so for now we will reuse the Fortran constant until we are OK with
|
||||
// modifying test results
|
||||
constexpr double PI {3.1415926535898};
|
||||
|
||||
const double SQRT_PI {std::sqrt(PI)};
|
||||
|
||||
// Mgxs::get_xs enumerated types
|
||||
// TODO: Convert to enum
|
||||
constexpr int MG_GET_XS_TOTAL {0};
|
||||
constexpr int MG_GET_XS_ABSORPTION {1};
|
||||
constexpr int MG_GET_XS_INVERSE_VELOCITY {2};
|
||||
|
|
@ -81,11 +326,120 @@ constexpr int MG_GET_XS_NU_FISSION {12};
|
|||
constexpr int MG_GET_XS_CHI_PROMPT {13};
|
||||
constexpr int MG_GET_XS_CHI_DELAYED {14};
|
||||
|
||||
extern "C" double FP_COINCIDENT;
|
||||
extern "C" double FP_PRECISION;
|
||||
constexpr double INFTY {std::numeric_limits<double>::max()};
|
||||
// ============================================================================
|
||||
// TALLY-RELATED CONSTANTS
|
||||
|
||||
// Tally result entries
|
||||
constexpr int RESULT_VALUE {1};
|
||||
constexpr int RESULT_SUM {2};
|
||||
constexpr int RESULT_SUM_SQ {3};
|
||||
|
||||
// Tally type
|
||||
// TODO: Convert to enum
|
||||
constexpr int TALLY_VOLUME {1};
|
||||
constexpr int TALLY_MESH_SURFACE {2};
|
||||
constexpr int TALLY_SURFACE {3};
|
||||
|
||||
// Tally estimator types
|
||||
// TODO: Convert to enum
|
||||
constexpr int ESTIMATOR_ANALOG {1};
|
||||
constexpr int ESTIMATOR_TRACKLENGTH {2};
|
||||
constexpr int ESTIMATOR_COLLISION {3};
|
||||
|
||||
// Event types for tallies
|
||||
// TODO: Convert to enum
|
||||
constexpr int EVENT_SURFACE {-2};
|
||||
constexpr int EVENT_LATTICE {-1};
|
||||
constexpr int EVENT_SCATTER {1};
|
||||
constexpr int EVENT_ABSORB {2};
|
||||
|
||||
// Tally score type -- if you change these, make sure you also update the
|
||||
// _SCORES dictionary in openmc/capi/tally.py
|
||||
// TODO: Convert to enum
|
||||
constexpr int SCORE_FLUX {-1}; // flux
|
||||
constexpr int SCORE_TOTAL {-2}; // total reaction rate
|
||||
constexpr int SCORE_SCATTER {-3}; // scattering rate
|
||||
constexpr int SCORE_NU_SCATTER {-4}; // scattering production rate
|
||||
constexpr int SCORE_ABSORPTION {-5}; // absorption rate
|
||||
constexpr int SCORE_FISSION {-6}; // fission rate
|
||||
constexpr int SCORE_NU_FISSION {-7}; // neutron production rate
|
||||
constexpr int SCORE_KAPPA_FISSION {-8}; // fission energy production rate
|
||||
constexpr int SCORE_CURRENT {-9}; // current
|
||||
constexpr int SCORE_EVENTS {-10}; // number of events
|
||||
constexpr int SCORE_DELAYED_NU_FISSION {-11}; // delayed neutron production rate
|
||||
constexpr int SCORE_PROMPT_NU_FISSION {-12}; // prompt neutron production rate
|
||||
constexpr int SCORE_INVERSE_VELOCITY {-13}; // flux-weighted inverse velocity
|
||||
constexpr int SCORE_FISS_Q_PROMPT {-14}; // prompt fission Q-value
|
||||
constexpr int SCORE_FISS_Q_RECOV {-15}; // recoverable fission Q-value
|
||||
constexpr int SCORE_DECAY_RATE {-16}; // delayed neutron precursor decay rate
|
||||
|
||||
// Tally map bin finding
|
||||
constexpr int NO_BIN_FOUND {-1};
|
||||
|
||||
// Tally filter and map types
|
||||
// TODO: Refactor to remove or convert to enum
|
||||
constexpr int FILTER_UNIVERSE {1};
|
||||
constexpr int FILTER_MATERIAL {2};
|
||||
constexpr int FILTER_CELL {3};
|
||||
constexpr int FILTER_CELLBORN {4};
|
||||
constexpr int FILTER_SURFACE {5};
|
||||
constexpr int FILTER_MESH {6};
|
||||
constexpr int FILTER_ENERGYIN {7};
|
||||
constexpr int FILTER_ENERGYOUT {8};
|
||||
constexpr int FILTER_DISTRIBCELL {9};
|
||||
constexpr int FILTER_MU {10};
|
||||
constexpr int FILTER_POLAR {11};
|
||||
constexpr int FILTER_AZIMUTHAL {12};
|
||||
constexpr int FILTER_DELAYEDGROUP {13};
|
||||
constexpr int FILTER_ENERGYFUNCTION {14};
|
||||
constexpr int FILTER_CELLFROM {15};
|
||||
constexpr int FILTER_MESHSURFACE {16};
|
||||
constexpr int FILTER_LEGENDRE {17};
|
||||
constexpr int FILTER_SPH_HARMONICS {18};
|
||||
constexpr int FILTER_SPTL_LEGENDRE {19};
|
||||
constexpr int FILTER_ZERNIKE {20};
|
||||
constexpr int FILTER_PARTICLE {21};
|
||||
|
||||
// Mesh types
|
||||
constexpr int MESH_REGULAR {1};
|
||||
|
||||
// Tally surface current directions
|
||||
constexpr int OUT_LEFT {1}; // x min
|
||||
constexpr int IN_LEFT {2}; // x min
|
||||
constexpr int OUT_RIGHT {3}; // x max
|
||||
constexpr int IN_RIGHT {4}; // x max
|
||||
constexpr int OUT_BACK {5}; // y min
|
||||
constexpr int IN_BACK {6}; // y min
|
||||
constexpr int OUT_FRONT {7}; // y max
|
||||
constexpr int IN_FRONT {8}; // y max
|
||||
constexpr int OUT_BOTTOM {9}; // z min
|
||||
constexpr int IN_BOTTOM {10}; // z min
|
||||
constexpr int OUT_TOP {11}; // z max
|
||||
constexpr int IN_TOP {12}; // z max
|
||||
|
||||
// Tally trigger types and threshold
|
||||
constexpr int VARIANCE {1};
|
||||
constexpr int RELATIVE_ERROR {2};
|
||||
constexpr int STANDARD_DEVIATION {3};
|
||||
|
||||
// Global tally parameters
|
||||
constexpr int K_COLLISION {1};
|
||||
constexpr int K_ABSORPTION {2};
|
||||
constexpr int K_TRACKLENGTH {3};
|
||||
constexpr int LEAKAGE {4};
|
||||
|
||||
// Differential tally independent variables
|
||||
constexpr int DIFF_DENSITY {1};
|
||||
constexpr int DIFF_NUCLIDE_DENSITY {2};
|
||||
constexpr int DIFF_TEMPERATURE {3};
|
||||
|
||||
constexpr int C_NONE {-1};
|
||||
|
||||
// Interpolation rules
|
||||
enum class Interpolation {
|
||||
histogram, lin_lin, lin_log, log_lin, log_log
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // CONSTANTS_H
|
||||
#endif // OPENMC_CONSTANTS_H
|
||||
|
|
|
|||
266
src/distribution.cpp
Normal file
266
src/distribution.cpp
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
#include "distribution.h"
|
||||
|
||||
#include <algorithm> // for copy
|
||||
#include <cmath> // for sqrt, floor, max
|
||||
#include <iterator> // for back_inserter
|
||||
#include <numeric> // for accumulate
|
||||
#include <string> // for string, stod
|
||||
|
||||
#include "error.h"
|
||||
#include "math_functions.h"
|
||||
#include "random_lcg.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Discrete implementation
|
||||
//==============================================================================
|
||||
|
||||
Discrete::Discrete(pugi::xml_node node)
|
||||
{
|
||||
auto params = get_node_array<double>(node, "parameters");
|
||||
|
||||
std::size_t n = params.size();
|
||||
std::copy(params.begin(), params.begin() + n/2, std::back_inserter(x_));
|
||||
std::copy(params.begin() + n/2, params.end(), std::back_inserter(p_));
|
||||
|
||||
normalize();
|
||||
}
|
||||
|
||||
Discrete::Discrete(const double* x, const double* p, int n)
|
||||
: x_{x, x+n}, p_{p, p+n}
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
double Discrete::sample() const
|
||||
{
|
||||
int n = x_.size();
|
||||
if (n > 1) {
|
||||
double xi = prn();
|
||||
double c = 0.0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
c += p_[i];
|
||||
if (xi < c) return x_[i];
|
||||
}
|
||||
// throw exception?
|
||||
} else {
|
||||
return x_[0];
|
||||
}
|
||||
}
|
||||
|
||||
void Discrete::normalize()
|
||||
{
|
||||
// Renormalize density function so that it sums to unity
|
||||
double norm = std::accumulate(p_.begin(), p_.end(), 0.0);
|
||||
for (auto& p_i : p_)
|
||||
p_i /= norm;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Uniform implementation
|
||||
//==============================================================================
|
||||
|
||||
Uniform::Uniform(pugi::xml_node node)
|
||||
{
|
||||
auto params = get_node_array<double>(node, "parameters");
|
||||
if (params.size() != 2)
|
||||
openmc::fatal_error("Uniform distribution must have two "
|
||||
"parameters specified.");
|
||||
|
||||
a_ = params.at(0);
|
||||
b_ = params.at(1);
|
||||
}
|
||||
|
||||
double Uniform::sample() const
|
||||
{
|
||||
return a_ + prn()*(b_ - a_);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Maxwell implementation
|
||||
//==============================================================================
|
||||
|
||||
Maxwell::Maxwell(pugi::xml_node node)
|
||||
{
|
||||
theta_ = std::stod(get_node_value(node, "parameters"));
|
||||
}
|
||||
|
||||
double Maxwell::sample() const
|
||||
{
|
||||
return maxwell_spectrum_c(theta_);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Watt implementation
|
||||
//==============================================================================
|
||||
|
||||
Watt::Watt(pugi::xml_node node)
|
||||
{
|
||||
auto params = get_node_array<double>(node, "parameters");
|
||||
if (params.size() != 2)
|
||||
openmc::fatal_error("Watt energy distribution must have two "
|
||||
"parameters specified.");
|
||||
|
||||
a_ = params.at(0);
|
||||
b_ = params.at(1);
|
||||
}
|
||||
|
||||
double Watt::sample() const
|
||||
{
|
||||
return watt_spectrum_c(a_, b_);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Tabular implementation
|
||||
//==============================================================================
|
||||
|
||||
Tabular::Tabular(pugi::xml_node node)
|
||||
{
|
||||
if (check_for_node(node, "interpolation")) {
|
||||
std::string temp = get_node_value(node, "interpolation");
|
||||
if (temp == "histogram") {
|
||||
interp_ = Interpolation::histogram;
|
||||
} else if (temp == "linear-linear") {
|
||||
interp_ = Interpolation::lin_lin;
|
||||
} else {
|
||||
openmc::fatal_error("Unknown interpolation type for distribution: " + temp);
|
||||
}
|
||||
} else {
|
||||
interp_ = Interpolation::histogram;
|
||||
}
|
||||
|
||||
// Read and initialize tabular distribution
|
||||
auto params = get_node_array<double>(node, "parameters");
|
||||
std::size_t n = params.size() / 2;
|
||||
const double* x = params.data();
|
||||
const double* p = x + n;
|
||||
init(x, p, n);
|
||||
}
|
||||
|
||||
Tabular::Tabular(const double* x, const double* p, int n, Interpolation interp, const double* c)
|
||||
: interp_{interp}
|
||||
{
|
||||
init(x, p, n, c);
|
||||
}
|
||||
|
||||
void Tabular::init(const double* x, const double* p, std::size_t n, const double* c)
|
||||
{
|
||||
// Copy x/p arrays into vectors
|
||||
std::copy(x, x + n, std::back_inserter(x_));
|
||||
std::copy(p, p + n, std::back_inserter(p_));
|
||||
|
||||
// Check interpolation parameter
|
||||
if (interp_ != Interpolation::histogram &&
|
||||
interp_ != Interpolation::lin_lin) {
|
||||
openmc::fatal_error("Only histogram and linear-linear interpolation "
|
||||
"for tabular distribution is supported.");
|
||||
}
|
||||
|
||||
// Calculate cumulative distribution function
|
||||
if (c) {
|
||||
std::copy(c, c + n, std::back_inserter(c_));
|
||||
} else {
|
||||
c_.resize(n);
|
||||
c_[0] = 0.0;
|
||||
for (int i = 1; i < n; ++i) {
|
||||
if (interp_ == Interpolation::histogram) {
|
||||
c_[i] = c_[i-1] + p_[i-1]*(x_[i] - x_[i-1]);
|
||||
} else if (interp_ == Interpolation::lin_lin) {
|
||||
c_[i] = c_[i-1] + 0.5*(p_[i-1] + p_[i]) * (x_[i] - x_[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize density and distribution functions
|
||||
for (int i = 0; i < n; ++i) {
|
||||
p_[i] = p_[i]/c_[n-1];
|
||||
c_[i] = c_[i]/c_[n-1];
|
||||
}
|
||||
}
|
||||
|
||||
double Tabular::sample() const
|
||||
{
|
||||
// Sample value of CDF
|
||||
double c = prn();
|
||||
|
||||
// Find first CDF bin which is above the sampled value
|
||||
double c_i = c_[0];
|
||||
int i;
|
||||
std::size_t n = c_.size();
|
||||
for (i = 0; i < n - 1; ++i) {
|
||||
if (c <= c_[i+1]) break;
|
||||
c_i = c_[i+1];
|
||||
}
|
||||
|
||||
// Determine bounding PDF values
|
||||
double x_i = x_[i];
|
||||
double p_i = p_[i];
|
||||
|
||||
if (interp_ == Interpolation::histogram) {
|
||||
// Histogram interpolation
|
||||
if (p_i > 0.0) {
|
||||
return x_i + (c - c_i)/p_i;
|
||||
} else {
|
||||
return x_i;
|
||||
}
|
||||
} else {
|
||||
// Linear-linear interpolation
|
||||
double x_i1 = x_[i + 1];
|
||||
double p_i1 = p_[i + 1];
|
||||
|
||||
double m = (p_i1 - p_i)/(x_i1 - x_i);
|
||||
if (m == 0.0) {
|
||||
return x_i + (c - c_i)/p_i;
|
||||
} else {
|
||||
return x_i + (std::sqrt(std::max(0.0, p_i*p_i + 2*m*(c - c_i))) - p_i)/m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Equiprobable implementation
|
||||
//==============================================================================
|
||||
|
||||
double Equiprobable::sample() const
|
||||
{
|
||||
std::size_t n = x_.size();
|
||||
|
||||
double r = prn();
|
||||
int i = std::floor((n - 1)*r);
|
||||
|
||||
double xl = x_[i];
|
||||
double xr = x_[i+i];
|
||||
return xl + ((n - 1)*r - i) * (xr - xl);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Helper function
|
||||
//==============================================================================
|
||||
|
||||
UPtrDist distribution_from_xml(pugi::xml_node node)
|
||||
{
|
||||
if (!check_for_node(node, "type"))
|
||||
openmc::fatal_error("Distribution type must be specified.");
|
||||
|
||||
// Determine type of distribution
|
||||
std::string type = get_node_value(node, "type", true, true);
|
||||
|
||||
// Allocate extension of Distribution
|
||||
if (type == "uniform") {
|
||||
return UPtrDist{new Uniform(node)};
|
||||
} else if (type == "maxwell") {
|
||||
return UPtrDist{new Maxwell(node)};
|
||||
} else if (type == "watt") {
|
||||
return UPtrDist{new Watt(node)};
|
||||
} else if (type == "discrete") {
|
||||
return UPtrDist{new Discrete(node)};
|
||||
} else if (type == "tabular") {
|
||||
return UPtrDist{new Tabular(node)};
|
||||
} else {
|
||||
openmc::fatal_error("Invalid distribution type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
150
src/distribution.h
Normal file
150
src/distribution.h
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
//! \file distribution.h
|
||||
//! Univariate probability distributions
|
||||
|
||||
#ifndef OPENMC_DISTRIBUTION_H
|
||||
#define OPENMC_DISTRIBUTION_H
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Abstract class representing a univariate probability distribution
|
||||
//==============================================================================
|
||||
|
||||
class Distribution {
|
||||
public:
|
||||
virtual ~Distribution() = default;
|
||||
virtual double sample() const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! A discrete distribution (probability mass function)
|
||||
//==============================================================================
|
||||
|
||||
class Discrete : public Distribution {
|
||||
public:
|
||||
explicit Discrete(pugi::xml_node node);
|
||||
Discrete(const double* x, const double* p, int n);
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
std::vector<double> x_; //!< Possible outcomes
|
||||
std::vector<double> p_; //!< Probability of each outcome
|
||||
|
||||
//! Normalize distribution so that probabilities sum to unity
|
||||
void normalize();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Uniform distribution over the interval [a,b]
|
||||
//==============================================================================
|
||||
|
||||
class Uniform : public Distribution {
|
||||
public:
|
||||
explicit Uniform(pugi::xml_node node);
|
||||
Uniform(double a, double b) : a_{a}, b_{b} {};
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double a_; //!< Lower bound of distribution
|
||||
double b_; //!< Upper bound of distribution
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Maxwellian distribution of form c*E*exp(-E/theta)
|
||||
//==============================================================================
|
||||
|
||||
class Maxwell : public Distribution {
|
||||
public:
|
||||
explicit Maxwell(pugi::xml_node node);
|
||||
Maxwell(double theta) : theta_{theta} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double theta_; //!< Factor in exponential [eV]
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Watt fission spectrum with form c*exp(-E/a)*sinh(sqrt(b*E))
|
||||
//==============================================================================
|
||||
|
||||
class Watt : public Distribution {
|
||||
public:
|
||||
explicit Watt(pugi::xml_node node);
|
||||
Watt(double a, double b) : a_{a}, b_{b} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double a_; //!< Factor in exponential [eV]
|
||||
double b_; //!< Factor in square root [1/eV]
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Histogram or linear-linear interpolated tabular distribution
|
||||
//==============================================================================
|
||||
|
||||
class Tabular : public Distribution {
|
||||
public:
|
||||
explicit Tabular(pugi::xml_node node);
|
||||
Tabular(const double* x, const double* p, int n, Interpolation interp,
|
||||
const double* c=nullptr);
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
std::vector<double> x_; //!< tabulated independent variable
|
||||
std::vector<double> p_; //!< tabulated probability density
|
||||
std::vector<double> c_; //!< cumulative distribution at tabulated values
|
||||
Interpolation interp_; //!< interpolation rule
|
||||
|
||||
//! Initialize tabulated probability density function
|
||||
//! \param x Array of values for independent variable
|
||||
//! \param p Array of tabulated probabilities
|
||||
//! \param n Number of tabulated values
|
||||
void init(const double* x, const double* p, std::size_t n,
|
||||
const double* c=nullptr);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Equiprobable distribution
|
||||
//==============================================================================
|
||||
|
||||
class Equiprobable : public Distribution {
|
||||
public:
|
||||
explicit Equiprobable(pugi::xml_node node);
|
||||
Equiprobable(const double* x, int n) : x_{x, x+n} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
std::vector<double> x_; //! Possible outcomes
|
||||
};
|
||||
|
||||
|
||||
using UPtrDist = std::unique_ptr<Distribution>;
|
||||
|
||||
//! Return univariate probability distribution specified in XML file
|
||||
//! \param[in] node XML node representing distribution
|
||||
//! \return Unique pointer to distribution
|
||||
UPtrDist distribution_from_xml(pugi::xml_node node);
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_DISTRIBUTION_H
|
||||
95
src/distribution_angle.cpp
Normal file
95
src/distribution_angle.cpp
Normal 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
39
src/distribution_angle.h
Normal 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
337
src/distribution_energy.cpp
Normal 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
152
src/distribution_energy.h
Normal 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
|
||||
51
src/distribution_multi.cpp
Normal file
51
src/distribution_multi.cpp
Normal 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
73
src/distribution_multi.h
Normal 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
|
||||
97
src/distribution_spatial.cpp
Normal file
97
src/distribution_spatial.cpp
Normal 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
|
||||
74
src/distribution_spatial.h
Normal file
74
src/distribution_spatial.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef OPENMC_DISTRIBTUION_SPATIAL_H
|
||||
#define OPENMC_DISTRIBUTION_SPATIAL_H
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include "distribution.h"
|
||||
#include "position.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Probability density function for points in Euclidean space
|
||||
//==============================================================================
|
||||
|
||||
class SpatialDistribution {
|
||||
public:
|
||||
virtual ~SpatialDistribution() = default;
|
||||
|
||||
//! Sample a position from the distribution
|
||||
virtual Position sample() const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Distribution of points specified by independent distributions in x,y,z
|
||||
//==============================================================================
|
||||
|
||||
class CartesianIndependent : public SpatialDistribution {
|
||||
public:
|
||||
explicit CartesianIndependent(pugi::xml_node node);
|
||||
|
||||
//! Sample a position from the distribution
|
||||
//! \return Sampled position
|
||||
Position sample() const;
|
||||
private:
|
||||
UPtrDist x_; //!< Distribution of x coordinates
|
||||
UPtrDist y_; //!< Distribution of y coordinates
|
||||
UPtrDist z_; //!< Distribution of z coordinates
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Uniform distribution of points over a box
|
||||
//==============================================================================
|
||||
|
||||
class SpatialBox : public SpatialDistribution {
|
||||
public:
|
||||
explicit SpatialBox(pugi::xml_node node);
|
||||
|
||||
//! Sample a position from the distribution
|
||||
//! \return Sampled position
|
||||
Position sample() const;
|
||||
private:
|
||||
Position lower_left_; //!< Lower-left coordinates of box
|
||||
Position upper_right_; //!< Upper-right coordinates of box
|
||||
bool only_fissionable {false}; //!< Only accept sites in fissionable region?
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Distribution at a single point
|
||||
//==============================================================================
|
||||
|
||||
class SpatialPoint : public SpatialDistribution {
|
||||
public:
|
||||
explicit SpatialPoint(pugi::xml_node node);
|
||||
|
||||
//! Sample a position from the distribution
|
||||
//! \return Sampled position
|
||||
Position sample() const;
|
||||
private:
|
||||
Position r_; //!< Single position at which sites are generated
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_DISTRIBUTION_SPATIAL_H
|
||||
147
src/endf.cpp
Normal file
147
src/endf.cpp
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#include "endf.h"
|
||||
|
||||
#include <algorithm> // for copy
|
||||
#include <cmath> // for log, exp
|
||||
#include <iterator> // for back_inserter
|
||||
|
||||
#include "constants.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "search.h"
|
||||
#include "xtensor/xarray.hpp"
|
||||
#include "xtensor/xview.hpp"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Functions
|
||||
//==============================================================================
|
||||
|
||||
Interpolation int2interp(int i)
|
||||
{
|
||||
switch (i) {
|
||||
case 1:
|
||||
return Interpolation::histogram;
|
||||
case 2:
|
||||
return Interpolation::lin_lin;
|
||||
case 3:
|
||||
return Interpolation::lin_log;
|
||||
case 4:
|
||||
return Interpolation::log_lin;
|
||||
case 5:
|
||||
return Interpolation::log_log;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_fission(int mt)
|
||||
{
|
||||
return mt == 18 || mt == 19 || mt == 20 || mt == 21 || mt == 38;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Polynomial implementation
|
||||
//==============================================================================
|
||||
|
||||
Polynomial::Polynomial(hid_t dset)
|
||||
{
|
||||
// Read coefficients into a vector
|
||||
read_dataset(dset, coef_);
|
||||
}
|
||||
|
||||
double Polynomial::operator()(double x) const
|
||||
{
|
||||
// Use Horner's rule to evaluate polynomial. Note that coefficients are
|
||||
// ordered in increasing powers of x.
|
||||
double y = 0.0;
|
||||
for (auto c = coef_.crbegin(); c != coef_.crend(); ++c) {
|
||||
y = y*x + *c;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Tabulated1D implementation
|
||||
//==============================================================================
|
||||
|
||||
Tabulated1D::Tabulated1D(hid_t dset)
|
||||
{
|
||||
read_attribute(dset, "breakpoints", nbt_);
|
||||
n_regions_ = nbt_.size();
|
||||
|
||||
// Change 1-indexing to 0-indexing
|
||||
for (auto& b : nbt_) --b;
|
||||
|
||||
std::vector<int> int_temp;
|
||||
read_attribute(dset, "interpolation", int_temp);
|
||||
|
||||
// Convert vector of ints into Interpolation
|
||||
for (const auto i : int_temp)
|
||||
int_.push_back(int2interp(i));
|
||||
|
||||
xt::xarray<double> arr;
|
||||
read_dataset(dset, arr);
|
||||
|
||||
auto xs = xt::view(arr, 0);
|
||||
auto ys = xt::view(arr, 1);
|
||||
|
||||
std::copy(xs.begin(), xs.end(), std::back_inserter(x_));
|
||||
std::copy(ys.begin(), ys.end(), std::back_inserter(y_));
|
||||
n_pairs_ = x_.size();
|
||||
}
|
||||
|
||||
double Tabulated1D::operator()(double x) const
|
||||
{
|
||||
// find which bin the abscissa is in -- if the abscissa is outside the
|
||||
// tabulated range, the first or last point is chosen, i.e. no interpolation
|
||||
// is done outside the energy range
|
||||
int i;
|
||||
if (x < x_[0]) {
|
||||
return y_[0];
|
||||
} else if (x > x_[n_pairs_ - 1]) {
|
||||
return y_[n_pairs_ - 1];
|
||||
} else {
|
||||
i = lower_bound_index(x_.begin(), x_.end(), x);
|
||||
}
|
||||
|
||||
// determine interpolation scheme
|
||||
Interpolation interp;
|
||||
if (n_regions_ == 0) {
|
||||
interp = Interpolation::lin_lin;
|
||||
} else if (n_regions_ == 1) {
|
||||
interp = int_[0];
|
||||
} else if (n_regions_ > 1) {
|
||||
for (int j = 0; j < n_regions_; ++j) {
|
||||
if (i < nbt_[j]) {
|
||||
interp = int_[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle special case of histogram interpolation
|
||||
if (interp == Interpolation::histogram) return y_[i];
|
||||
|
||||
// determine bounding values
|
||||
double x0 = x_[i];
|
||||
double x1 = x_[i + 1];
|
||||
double y0 = y_[i];
|
||||
double y1 = y_[i + 1];
|
||||
|
||||
// determine interpolation factor and interpolated value
|
||||
double r;
|
||||
switch (interp) {
|
||||
case Interpolation::lin_lin:
|
||||
r = (x - x0)/(x1 - x0);
|
||||
return y0 + r*(y1 - y0);
|
||||
case Interpolation::lin_log:
|
||||
r = log(x/x0)/log(x1/x0);
|
||||
return y0 + r*(y1 - y0);
|
||||
case Interpolation::log_lin:
|
||||
r = (x - x0)/(x1 - x0);
|
||||
return y0*exp(r*log(y1/y0));
|
||||
case Interpolation::log_log:
|
||||
r = log(x/x0)/log(x1/x0);
|
||||
return y0*exp(r*log(y1/y0));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
78
src/endf.h
Normal file
78
src/endf.h
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
//! \file endf.h
|
||||
//! Classes and functions related to the ENDF-6 format
|
||||
|
||||
#ifndef OPENMC_ENDF_H
|
||||
#define OPENMC_ENDF_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "constants.h"
|
||||
#include "hdf5.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//! Convert integer representing interpolation law to enum
|
||||
//! \param[in] i Intereger (e.g. 1=histogram, 2=lin-lin)
|
||||
//! \return Corresponding enum value
|
||||
Interpolation int2interp(int i);
|
||||
|
||||
//! Determine whether MT number corresponds to a fission reaction
|
||||
//! \param[in] MT ENDF MT value
|
||||
//! \return Whether corresponding reaction is a fission reaction
|
||||
bool is_fission(int MT);
|
||||
|
||||
//==============================================================================
|
||||
//! Abstract one-dimensional function
|
||||
//==============================================================================
|
||||
|
||||
class Function1D {
|
||||
public:
|
||||
virtual double operator()(double x) const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! One-dimensional function expressed as a polynomial
|
||||
//==============================================================================
|
||||
|
||||
class Polynomial : public Function1D {
|
||||
public:
|
||||
//! Construct polynomial from HDF5 data
|
||||
//! \param[in] dset Dataset containing coefficients
|
||||
explicit Polynomial(hid_t dset);
|
||||
|
||||
//! Evaluate the polynomials
|
||||
//! \param[in] x independent variable
|
||||
//! \return Polynomial evaluated at x
|
||||
double operator()(double x) const;
|
||||
private:
|
||||
std::vector<double> coef_; //!< Polynomial coefficients
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! One-dimensional interpolable function
|
||||
//==============================================================================
|
||||
|
||||
class Tabulated1D : public Function1D {
|
||||
public:
|
||||
Tabulated1D() = default;
|
||||
|
||||
//! Construct function from HDF5 data
|
||||
//! \param[in] dset Dataset containing tabulated data
|
||||
explicit Tabulated1D(hid_t dset);
|
||||
|
||||
//! Evaluate the tabulated function
|
||||
//! \param[in] x independent variable
|
||||
//! \return Function evaluated at x
|
||||
double operator()(double x) const;
|
||||
private:
|
||||
std::size_t n_regions_ {0}; //!< number of interpolation regions
|
||||
std::vector<int> nbt_; //!< values separating interpolation regions
|
||||
std::vector<Interpolation> int_; //!< interpolation schemes
|
||||
std::size_t n_pairs_; //!< number of (x,y) pairs
|
||||
std::vector<double> x_; //!< values of abscissa
|
||||
std::vector<double> y_; //!< values of ordinate
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_ENDF_H
|
||||
|
|
@ -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
|
||||
|
|
@ -94,7 +94,7 @@ contains
|
|||
p % last_sqrtkT = p % sqrtkT
|
||||
|
||||
! Get distributed offset
|
||||
if (size(c % material) > 1 .or. size(c % sqrtkT) > 1) then
|
||||
if (c % material_size() > 1 .or. size(c % sqrtkT) > 1) then
|
||||
! Distributed instances of this cell have different
|
||||
! materials/temperatures. Determine which instance this is for
|
||||
! assigning the matching material/temperature.
|
||||
|
|
@ -126,7 +126,7 @@ contains
|
|||
end if
|
||||
|
||||
! Save the material
|
||||
if (size(c % material) > 1) then
|
||||
if (c % material_size() > 1) then
|
||||
p % material = c % material(offset + 1)
|
||||
else
|
||||
p % material = c % material(1)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@
|
|||
#include "cell.h"
|
||||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "geometry.h"
|
||||
#include "lattice.h"
|
||||
#include "material.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
|
@ -15,11 +17,11 @@ namespace openmc {
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
adjust_indices_c()
|
||||
adjust_indices()
|
||||
{
|
||||
// Adjust material/fill idices.
|
||||
for (Cell *c : global_cells) {
|
||||
if (c->material[0] == C_NONE) {
|
||||
for (Cell* c : global_cells) {
|
||||
if (c->fill != C_NONE) {
|
||||
int32_t id = c->fill;
|
||||
auto search_univ = universe_map.find(id);
|
||||
auto search_lat = lattice_map.find(id);
|
||||
|
|
@ -36,13 +38,26 @@ adjust_indices_c()
|
|||
fatal_error(err_msg);
|
||||
}
|
||||
} else {
|
||||
//TODO: materials
|
||||
c->type = FILL_MATERIAL;
|
||||
for (auto it = c->material.begin(); it != c->material.end(); it++) {
|
||||
int32_t mid = *it;
|
||||
if (mid != MATERIAL_VOID) {
|
||||
auto search = material_map.find(mid);
|
||||
if (search != material_map.end()) {
|
||||
*it = search->second;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Could not find material " << mid
|
||||
<< " specified on cell " << c->id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change cell.universe values from IDs to indices.
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
auto search = universe_map.find(c->universe);
|
||||
if (search != universe_map.end()) {
|
||||
//TODO: Remove this off-by-one indexing.
|
||||
|
|
@ -56,7 +71,7 @@ adjust_indices_c()
|
|||
}
|
||||
|
||||
// Change all lattice universe values from IDs to indices.
|
||||
for (Lattice *l : lattices_c) {
|
||||
for (Lattice* l : lattices_c) {
|
||||
l->adjust_indices();
|
||||
}
|
||||
}
|
||||
|
|
@ -68,12 +83,12 @@ find_root_universe()
|
|||
{
|
||||
// Find all the universes listed as a cell fill.
|
||||
std::unordered_set<int32_t> fill_univ_ids;
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
fill_univ_ids.insert(c->fill);
|
||||
}
|
||||
|
||||
// Find all the universes contained in a lattice.
|
||||
for (Lattice *lat : lattices_c) {
|
||||
for (Lattice* lat : lattices_c) {
|
||||
for (auto it = lat->begin(); it != lat->end(); ++it) {
|
||||
fill_univ_ids.insert(*it);
|
||||
}
|
||||
|
|
@ -109,13 +124,13 @@ find_root_universe()
|
|||
void
|
||||
allocate_offset_tables(int n_maps)
|
||||
{
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
if (c->type != FILL_MATERIAL) {
|
||||
c->offset.resize(n_maps, C_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
for (Lattice *lat : lattices_c) {
|
||||
for (Lattice* lat : lattices_c) {
|
||||
lat->allocate_offset_table(n_maps);
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +141,7 @@ void
|
|||
count_cell_instances(int32_t univ_indx)
|
||||
{
|
||||
for (int32_t cell_indx : global_universes[univ_indx]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
++c.n_instances;
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
|
|
@ -135,7 +150,7 @@ count_cell_instances(int32_t univ_indx)
|
|||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
// This cell contains a lattice. Recurse into the lattice universes.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
count_cell_instances(*it);
|
||||
}
|
||||
|
|
@ -155,14 +170,14 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
|
|||
|
||||
int count {0};
|
||||
for (int32_t cell_indx : global_universes[search_univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
|
|
@ -178,10 +193,10 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
|
|||
void
|
||||
fill_offset_tables(int32_t target_univ_id, int map)
|
||||
{
|
||||
for (Universe *univ : global_universes) {
|
||||
for (Universe* univ : global_universes) {
|
||||
int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation.
|
||||
for (int32_t cell_indx : univ->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
c.offset[map] = offset;
|
||||
|
|
@ -189,7 +204,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
|
|||
offset += count_universe_instances(search_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
offset = lat.fill_offset_table(offset, target_univ_id, map);
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +215,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
|
|||
|
||||
std::string
|
||||
distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
const Universe &search_univ, int32_t offset)
|
||||
const Universe& search_univ, int32_t offset)
|
||||
{
|
||||
std::stringstream path;
|
||||
|
||||
|
|
@ -210,7 +225,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
// write to the path and return.
|
||||
for (int32_t cell_indx : search_univ.cells) {
|
||||
if ((cell_indx == target_cell) && (offset == target_offset)) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
path << "c" << c.id;
|
||||
return path.str();
|
||||
}
|
||||
|
|
@ -222,7 +237,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
std::vector<std::int32_t>::const_reverse_iterator cell_it
|
||||
{search_univ.cells.crbegin()};
|
||||
for (; cell_it != search_univ.cells.crend(); ++cell_it) {
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
Cell& c = *global_cells[*cell_it];
|
||||
|
||||
// Material cells don't contain other cells so ignore them.
|
||||
if (c.type != FILL_MATERIAL) {
|
||||
|
|
@ -230,7 +245,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
if (c.type == FILL_UNIVERSE) {
|
||||
temp_offset = offset + c.offset[map];
|
||||
} else {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
int32_t indx = lat.universes.size()*map + lat.begin().indx;
|
||||
temp_offset = offset + lat.offsets[indx];
|
||||
}
|
||||
|
|
@ -242,7 +257,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
}
|
||||
|
||||
// Add the cell to the path string.
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
Cell& c = *global_cells[*cell_it];
|
||||
path << "c" << c.id << "->";
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
|
|
@ -253,7 +268,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
return path.str();
|
||||
} else {
|
||||
// Recurse into the lattice cell.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
path << "l" << lat.id;
|
||||
for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
|
||||
int32_t indx = lat.universes.size()*map + it.indx;
|
||||
|
|
@ -275,7 +290,7 @@ int
|
|||
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
Universe& root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
return path_.size() + 1;
|
||||
|
|
@ -285,9 +300,9 @@ distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
|
||||
void
|
||||
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ, char *path)
|
||||
int32_t root_univ, char* path)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
Universe& root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
path_.copy(path, path_.size());
|
||||
|
|
@ -302,12 +317,12 @@ maximum_levels(int32_t univ)
|
|||
int levels_below {0};
|
||||
|
||||
for (int32_t cell_indx : global_universes[univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
|
|
@ -324,18 +339,20 @@ maximum_levels(int32_t univ)
|
|||
void
|
||||
free_memory_geometry_c()
|
||||
{
|
||||
for (Cell *c : global_cells) {delete c;}
|
||||
for (Cell* c : global_cells) {delete c;}
|
||||
global_cells.clear();
|
||||
cell_map.clear();
|
||||
n_cells = 0;
|
||||
|
||||
for (Universe *u : global_universes) {delete u;}
|
||||
for (Universe* u : global_universes) {delete u;}
|
||||
global_universes.clear();
|
||||
universe_map.clear();
|
||||
|
||||
for (Lattice *lat : lattices_c) {delete lat;}
|
||||
for (Lattice* lat : lattices_c) {delete lat;}
|
||||
lattices_c.clear();
|
||||
lattice_map.clear();
|
||||
|
||||
overlap_check_count.clear();
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -51,6 +51,35 @@ get_shape(hid_t obj_id, hsize_t* dims)
|
|||
}
|
||||
|
||||
|
||||
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name)
|
||||
{
|
||||
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);
|
||||
std::vector<hsize_t> shape = object_shape(attr);
|
||||
H5Aclose(attr);
|
||||
return shape;
|
||||
}
|
||||
|
||||
std::vector<hsize_t> object_shape(hid_t obj_id)
|
||||
{
|
||||
// Get number of dimensions
|
||||
auto type = H5Iget_type(obj_id);
|
||||
hid_t dspace;
|
||||
if (type == H5I_DATASET) {
|
||||
dspace = H5Dget_space(obj_id);
|
||||
} else if (type == H5I_ATTR) {
|
||||
dspace = H5Aget_space(obj_id);
|
||||
}
|
||||
int n = H5Sget_simple_extent_ndims(dspace);
|
||||
|
||||
// Get shape of array
|
||||
std::vector<hsize_t> shape(n);
|
||||
H5Sget_simple_extent_dims(dspace, shape.data(), nullptr);
|
||||
|
||||
// Free resources and return
|
||||
H5Sclose(dspace);
|
||||
return shape;
|
||||
}
|
||||
|
||||
void
|
||||
get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims)
|
||||
{
|
||||
|
|
@ -116,6 +145,18 @@ dataset_typesize(hid_t dset)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
ensure_exists(hid_t group_id, const char* name)
|
||||
{
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Object \"" << name << "\" does not exist in group "
|
||||
<< object_name(group_id);
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
file_open(const char* filename, char mode, bool parallel)
|
||||
{
|
||||
|
|
@ -285,6 +326,36 @@ get_groups(hid_t group_id, char* name[])
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
group_names(hid_t group_id)
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get names
|
||||
H5O_info_t oinfo;
|
||||
size_t size;
|
||||
std::vector<std::string> names;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type != H5O_TYPE_GROUP) continue;
|
||||
|
||||
// Get size of name
|
||||
size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC,
|
||||
i, nullptr, 0, H5P_DEFAULT);
|
||||
|
||||
// Read name
|
||||
char buffer[size];
|
||||
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
|
||||
buffer, size, H5P_DEFAULT);
|
||||
names.emplace_back(&buffer[0], size);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
object_exists(hid_t object_id, const char* name)
|
||||
|
|
@ -299,14 +370,23 @@ object_exists(hid_t object_id, const char* name)
|
|||
}
|
||||
|
||||
|
||||
std::string
|
||||
object_name(hid_t obj_id)
|
||||
{
|
||||
// Determine size and create buffer
|
||||
size_t size = 1 + H5Iget_name(obj_id, nullptr, 0);
|
||||
char buffer[size];
|
||||
|
||||
// Read and return name
|
||||
H5Iget_name(obj_id, buffer, size);
|
||||
return {buffer, size};
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
open_dataset(hid_t group_id, const char* name)
|
||||
{
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
ensure_exists(group_id, name);
|
||||
return H5Dopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
|
|
@ -314,11 +394,7 @@ open_dataset(hid_t group_id, const char* name)
|
|||
hid_t
|
||||
open_group(hid_t group_id, const char* name)
|
||||
{
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
ensure_exists(group_id, name);
|
||||
return H5Gopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
|
|
@ -425,7 +501,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde
|
|||
|
||||
|
||||
void
|
||||
read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep)
|
||||
read_complex(hid_t obj_id, const char* name, std::complex<double>* buffer, bool indep)
|
||||
{
|
||||
// Create compound datatype for complex numbers
|
||||
struct complex_t {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
#ifndef OPENMC_HDF5_INTERFACE_H
|
||||
#define OPENMC_HDF5_INTERFACE_H
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <complex.h>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
#include "xtensor/xadapt.hpp"
|
||||
#include "xtensor/xarray.hpp"
|
||||
|
||||
#include "position.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -21,7 +22,7 @@ namespace openmc {
|
|||
//==============================================================================
|
||||
|
||||
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
const void* buffer);
|
||||
void* buffer);
|
||||
void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer);
|
||||
void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
|
|
@ -72,6 +73,12 @@ read_nd_vector(hid_t obj_id, const char* name,
|
|||
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
|
||||
bool must_have = false);
|
||||
|
||||
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name);
|
||||
void ensure_exists(hid_t group_id, const char* name);
|
||||
std::vector<std::string> group_names(hid_t group_id);
|
||||
std::vector<hsize_t> object_shape(hid_t obj_id);
|
||||
std::string object_name(hid_t obj_id);
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
|
@ -101,7 +108,7 @@ extern "C" {
|
|||
void read_attr_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer);
|
||||
void read_complex(hid_t obj_id, const char* name,
|
||||
double _Complex* buffer, bool indep);
|
||||
std::complex<double>* buffer, bool indep);
|
||||
void read_double(hid_t obj_id, const char* name, double* buffer,
|
||||
bool indep);
|
||||
void read_int(hid_t obj_id, const char* name, int* buffer,
|
||||
|
|
@ -142,7 +149,127 @@ template<typename T>
|
|||
struct H5TypeMap { static const hid_t type_id; };
|
||||
|
||||
//==============================================================================
|
||||
// Template functions used to provide simple interface to lower-level functions
|
||||
// Templates/overloads for read_attribute
|
||||
//==============================================================================
|
||||
|
||||
// Scalar version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, T& buffer)
|
||||
{
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
// vector version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, std::vector<T>& vec)
|
||||
{
|
||||
// Get shape of attribute array
|
||||
auto shape = attribute_shape(obj_id, name);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
vec.resize(size);
|
||||
|
||||
// Read data from attribute
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, vec.data());
|
||||
}
|
||||
|
||||
// Generic array version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, xt::xarray<T>& arr)
|
||||
{
|
||||
// Get shape of attribute array
|
||||
auto shape = attribute_shape(obj_id, name);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
T* buffer = new T[size];
|
||||
|
||||
// Read data from attribute
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, buffer);
|
||||
|
||||
// Adapt array into xarray
|
||||
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
|
||||
}
|
||||
|
||||
// overload for std::string
|
||||
inline void
|
||||
read_attribute(hid_t obj_id, const char* name, std::string& str)
|
||||
{
|
||||
// Create buffer to read data into
|
||||
auto n = attribute_typesize(obj_id, name);
|
||||
char buffer[n];
|
||||
|
||||
// Read attribute and set string
|
||||
read_attr_string(obj_id, name, n, buffer);
|
||||
str = std::string{buffer, n};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for read_dataset
|
||||
//==============================================================================
|
||||
|
||||
template<typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, T buffer, bool indep=false)
|
||||
{
|
||||
read_dataset(obj_id, name, H5TypeMap<T>::type_id, &buffer, indep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t dset, std::vector<T>& vec, bool indep=false)
|
||||
{
|
||||
// Get shape of dataset
|
||||
std::vector<hsize_t> shape = object_shape(dset);
|
||||
|
||||
// Resize vector to appropriate size
|
||||
vec.resize(shape[0]);
|
||||
|
||||
// Read data into vector
|
||||
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, vec.data(), indep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, std::vector<T>& vec, bool indep=false)
|
||||
{
|
||||
hid_t dset = open_dataset(obj_id, name);
|
||||
read_dataset(dset, vec, indep);
|
||||
close_dataset(dset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t dset, xt::xarray<T>& arr, bool indep=false)
|
||||
{
|
||||
// Get shape of dataset
|
||||
std::vector<hsize_t> shape = object_shape(dset);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
T* buffer = new T[size];
|
||||
|
||||
// Read data from attribute
|
||||
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, buffer, indep);
|
||||
|
||||
// Adapt into xarray
|
||||
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, xt::xarray<T>& arr, bool indep=false)
|
||||
{
|
||||
// Open dataset and read array
|
||||
hid_t dset = open_dataset(obj_id, name);
|
||||
read_dataset(dset, arr, indep);
|
||||
close_dataset(dset);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for write_attribute
|
||||
//==============================================================================
|
||||
|
||||
template<typename T> inline void
|
||||
|
|
@ -151,8 +278,8 @@ write_attribute(hid_t obj_id, const char* name, T buffer)
|
|||
write_attr(obj_id, name, 0, nullptr, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
write_attribute<const char*>(hid_t obj_id, const char* name, const char* buffer)
|
||||
inline void
|
||||
write_attribute(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
write_attr_string(obj_id, name, buffer);
|
||||
}
|
||||
|
|
@ -164,14 +291,18 @@ write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
|||
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for write_dataset
|
||||
//==============================================================================
|
||||
|
||||
template<typename T> inline void
|
||||
write_dataset(hid_t obj_id, const char* name, T buffer)
|
||||
{
|
||||
write_dataset(obj_id, 0, nullptr, name, H5TypeMap<T>::type_id, &buffer, false);
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
write_dataset<const char*>(hid_t obj_id, const char* name, const char* buffer)
|
||||
inline void
|
||||
write_dataset(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
write_string(obj_id, name, buffer, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ module input_xml
|
|||
save
|
||||
|
||||
interface
|
||||
subroutine adjust_indices_c() bind(C)
|
||||
end subroutine adjust_indices_c
|
||||
subroutine adjust_indices() bind(C)
|
||||
end subroutine adjust_indices
|
||||
|
||||
subroutine allocate_offset_tables(n_maps) bind(C)
|
||||
import C_INT
|
||||
|
|
@ -87,6 +87,11 @@ module input_xml
|
|||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_settings
|
||||
|
||||
subroutine read_materials(node_ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_materials
|
||||
|
||||
function find_root_universe() bind(C) result(root)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T) :: root
|
||||
|
|
@ -1051,7 +1056,7 @@ contains
|
|||
allocate(surfaces(n_surfaces))
|
||||
|
||||
do i = 1, n_surfaces
|
||||
surfaces(i) % ptr = surface_pointer_c(i - 1);
|
||||
surfaces(i) % ptr = surface_pointer(i - 1);
|
||||
|
||||
if (surfaces(i) % bc() /= BC_TRANSMIT) boundary_exists = .true.
|
||||
|
||||
|
|
@ -1090,7 +1095,7 @@ contains
|
|||
do i = 1, n_cells
|
||||
c => cells(i)
|
||||
|
||||
c % ptr = cell_pointer_c(i - 1)
|
||||
c % ptr = cell_pointer(i - 1)
|
||||
|
||||
! Initialize distribcell instances and distribcell index
|
||||
c % distribcell_index = NONE
|
||||
|
|
@ -1104,42 +1109,6 @@ contains
|
|||
// to_str(c % id()))
|
||||
end if
|
||||
|
||||
! Read material
|
||||
if (check_for_node(node_cell, "material")) then
|
||||
n_mats = node_word_count(node_cell, "material")
|
||||
|
||||
if (n_mats > 0) then
|
||||
allocate(sarray(n_mats))
|
||||
call get_node_array(node_cell, "material", sarray)
|
||||
|
||||
allocate(c % material(n_mats))
|
||||
do j = 1, n_mats
|
||||
select case(trim(to_lower(sarray(j))))
|
||||
case ('void')
|
||||
c % material(j) = MATERIAL_VOID
|
||||
case default
|
||||
c % material(j) = int(str_to_int(sarray(j)), 4)
|
||||
|
||||
! Check for error
|
||||
if (c % material(j) == ERROR_INT) then
|
||||
call fatal_error("Invalid material specified on cell " &
|
||||
// to_str(c % id()))
|
||||
end if
|
||||
end select
|
||||
end do
|
||||
|
||||
deallocate(sarray)
|
||||
|
||||
else
|
||||
allocate(c % material(1))
|
||||
c % material(1) = NONE
|
||||
end if
|
||||
|
||||
else
|
||||
allocate(c % material(1))
|
||||
c % material(1) = NONE
|
||||
end if
|
||||
|
||||
! Check for region specification (also under deprecated name surfaces)
|
||||
if (check_for_node(node_cell, "surfaces")) then
|
||||
call warning("The use of 'surfaces' is deprecated and will be &
|
||||
|
|
@ -1241,7 +1210,7 @@ contains
|
|||
n = node_word_count(node_cell, "temperature")
|
||||
if (n > 0) then
|
||||
! Make sure this is a "normal" cell.
|
||||
if (c % material(1) == NONE) call fatal_error("Cell " &
|
||||
if (c % fill() /= C_NONE) call fatal_error("Cell " &
|
||||
// trim(to_str(c % id())) // " was specified with a temperature &
|
||||
&but no material. Temperature specification is only valid for &
|
||||
&cells filled with a material.")
|
||||
|
|
@ -1304,7 +1273,7 @@ contains
|
|||
RECT_LATTICES: do i = 1, n_rlats
|
||||
allocate(RectLattice::lattices(i) % obj)
|
||||
lat => lattices(i) % obj
|
||||
lat % ptr = lattice_pointer_c(i - 1)
|
||||
lat % ptr = lattice_pointer(i - 1)
|
||||
select type(lat)
|
||||
type is (RectLattice)
|
||||
|
||||
|
|
@ -1320,7 +1289,7 @@ contains
|
|||
HEX_LATTICES: do i = 1, n_hlats
|
||||
allocate(HexLattice::lattices(n_rlats + i) % obj)
|
||||
lat => lattices(n_rlats + i) % obj
|
||||
lat % ptr = lattice_pointer_c(n_rlats + i - 1)
|
||||
lat % ptr = lattice_pointer(n_rlats + i - 1)
|
||||
select type (lat)
|
||||
type is (HexLattice)
|
||||
|
||||
|
|
@ -1535,10 +1504,12 @@ contains
|
|||
call doc % load_file(filename)
|
||||
root = doc % document_element()
|
||||
|
||||
call read_materials(root % ptr)
|
||||
|
||||
! Get pointer to list of XML <material>
|
||||
call get_node_list(root, "material", node_mat_list)
|
||||
|
||||
! Allocate cells array
|
||||
! Allocate materials array
|
||||
n_materials = size(node_mat_list)
|
||||
allocate(materials(n_materials))
|
||||
allocate(material_temps(n_materials))
|
||||
|
|
@ -1551,16 +1522,11 @@ contains
|
|||
do i = 1, n_materials
|
||||
mat => materials(i)
|
||||
|
||||
mat % ptr = material_pointer(i - 1)
|
||||
|
||||
! Get pointer to i-th material node
|
||||
node_mat = node_mat_list(i)
|
||||
|
||||
! Copy material id
|
||||
if (check_for_node(node_mat, "id")) then
|
||||
call get_node_value(node_mat, "id", mat % id)
|
||||
else
|
||||
call fatal_error("Must specify id of material in materials XML file")
|
||||
end if
|
||||
|
||||
! Check if material is depletable
|
||||
if (check_for_node(node_mat, "depletable")) then
|
||||
call get_node_value(node_mat, "depletable", temp_str)
|
||||
|
|
@ -1568,12 +1534,6 @@ contains
|
|||
mat % depletable = .true.
|
||||
end if
|
||||
|
||||
! Check to make sure 'id' hasn't been used
|
||||
if (material_dict % has(mat % id)) then
|
||||
call fatal_error("Two or more materials use the same unique ID: " &
|
||||
// to_str(mat % id))
|
||||
end if
|
||||
|
||||
! Copy material name
|
||||
if (check_for_node(node_mat, "name")) then
|
||||
call get_node_value(node_mat, "name", mat % name)
|
||||
|
|
@ -1591,7 +1551,7 @@ contains
|
|||
node_dens = node_mat % child("density")
|
||||
else
|
||||
call fatal_error("Must specify density element in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Copy units
|
||||
|
|
@ -1621,7 +1581,7 @@ contains
|
|||
sum_density = .false.
|
||||
if (val <= ZERO) then
|
||||
call fatal_error("Need to specify a positive density on material " &
|
||||
// trim(to_str(mat % id)) // ".")
|
||||
// trim(to_str(mat % id())) // ".")
|
||||
end if
|
||||
|
||||
! Adjust material density based on specified units
|
||||
|
|
@ -1636,7 +1596,7 @@ contains
|
|||
mat % density = 1.0e-24_8 * val
|
||||
case default
|
||||
call fatal_error("Unkwown units '" // trim(units) &
|
||||
// "' specified on material " // trim(to_str(mat % id)))
|
||||
// "' specified on material " // trim(to_str(mat % id())))
|
||||
end select
|
||||
end if
|
||||
|
||||
|
|
@ -1645,7 +1605,7 @@ contains
|
|||
|
||||
if (size(node_ele_list) > 0) then
|
||||
call fatal_error("Unable to add an element to material " &
|
||||
// trim(to_str(mat % id)) // " since the element option has &
|
||||
// trim(to_str(mat % id())) // " since the element option has &
|
||||
&been removed from the xml input. Elements can only be added via &
|
||||
&the Python API, which will expand elements into their natural &
|
||||
&nuclides.")
|
||||
|
|
@ -1658,7 +1618,7 @@ contains
|
|||
if (.not. check_for_node(node_mat, "nuclide") .and. &
|
||||
.not. check_for_node(node_mat, "macroscopic")) then
|
||||
call fatal_error("No macroscopic data or nuclides specified on &
|
||||
&material " // trim(to_str(mat % id)))
|
||||
&material " // trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Create list of macroscopic x/s based on those specified, just treat
|
||||
|
|
@ -1672,7 +1632,7 @@ contains
|
|||
& mode!")
|
||||
else if (size(node_macro_list) > 1) then
|
||||
call fatal_error("Only one macroscopic object permitted per material, " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
else if (size(node_macro_list) == 1) then
|
||||
|
||||
node_nuc = node_macro_list(1)
|
||||
|
|
@ -1680,7 +1640,7 @@ contains
|
|||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on macroscopic data in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
|
|
@ -1710,7 +1670,7 @@ contains
|
|||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on nuclide in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
|
|
@ -1849,7 +1809,7 @@ contains
|
|||
if (.not. (all(mat % atom_density >= ZERO) .or. &
|
||||
all(mat % atom_density <= ZERO))) then
|
||||
call fatal_error("Cannot mix atom and weight percents in material " &
|
||||
// to_str(mat % id))
|
||||
// to_str(mat % id()))
|
||||
end if
|
||||
|
||||
! Determine density if it is a sum value
|
||||
|
|
@ -1930,7 +1890,7 @@ contains
|
|||
end if
|
||||
|
||||
! Add material to dictionary
|
||||
call material_dict % set(mat % id, i)
|
||||
call material_dict % set(mat % id(), i)
|
||||
end do
|
||||
|
||||
! Set total number of nuclides and S(a,b) tables
|
||||
|
|
@ -3249,7 +3209,7 @@ contains
|
|||
! Check if the specified tally mesh exists
|
||||
if (mesh_dict % has(meshid)) then
|
||||
pl % meshlines_mesh => meshes(mesh_dict % get(meshid))
|
||||
if (meshes(meshid) % type /= LATTICE_RECT) then
|
||||
if (meshes(meshid) % type /= MESH_REGULAR) then
|
||||
call fatal_error("Non-rectangular mesh specified in &
|
||||
&meshlines for plot " // trim(to_str(pl % id)))
|
||||
end if
|
||||
|
|
@ -3849,15 +3809,15 @@ contains
|
|||
|
||||
do i = 1, n_cells
|
||||
! Ignore non-normal cells and cells with defined temperature.
|
||||
if (cells(i) % material(1) == NONE) cycle
|
||||
if (cells(i) % fill() /= C_NONE) cycle
|
||||
if (cells(i) % sqrtkT(1) >= ZERO) cycle
|
||||
|
||||
! Set the number of temperatures equal to the number of materials.
|
||||
deallocate(cells(i) % sqrtkT)
|
||||
allocate(cells(i) % sqrtkT(size(cells(i) % material)))
|
||||
allocate(cells(i) % sqrtkT(cells(i) % material_size()))
|
||||
|
||||
! Check each of the cell materials for temperature data.
|
||||
do j = 1, size(cells(i) % material)
|
||||
do j = 1, cells(i) % material_size()
|
||||
! Arbitrarily set void regions to 0K.
|
||||
if (cells(i) % material(j) == MATERIAL_VOID) then
|
||||
cells(i) % sqrtkT(j) = ZERO
|
||||
|
|
@ -3925,45 +3885,6 @@ contains
|
|||
|
||||
end subroutine read_multipole_data
|
||||
|
||||
!===============================================================================
|
||||
! ADJUST_INDICES changes the values for 'surfaces' for each cell and the
|
||||
! material index assigned to each to the indices in the surfaces and material
|
||||
! array rather than the unique IDs assigned to each surface and material. Also
|
||||
! assigns boundary conditions to surfaces based on those read into the bc_dict
|
||||
! dictionary
|
||||
!===============================================================================
|
||||
|
||||
subroutine adjust_indices()
|
||||
|
||||
integer :: i ! index for various purposes
|
||||
integer :: j ! index for various purposes
|
||||
integer :: id ! user-specified id
|
||||
|
||||
call adjust_indices_c()
|
||||
|
||||
do i = 1, n_cells
|
||||
associate (c => cells(i))
|
||||
|
||||
! =======================================================================
|
||||
! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL
|
||||
|
||||
if (c % material(1) /= NONE) then
|
||||
do j = 1, size(c % material)
|
||||
id = c % material(j)
|
||||
if (id == MATERIAL_VOID) then
|
||||
else if (material_dict % has(id)) then
|
||||
c % material(j) = material_dict % get(id)
|
||||
else
|
||||
call fatal_error("Could not find material " // trim(to_str(id)) &
|
||||
// " specified on cell " // trim(to_str(c % id())))
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
|
||||
end subroutine adjust_indices
|
||||
|
||||
!===============================================================================
|
||||
! PREPARE_DISTRIBCELL initializes any distribcell filters present and sets the
|
||||
! offsets for distribcells
|
||||
|
|
@ -3987,7 +3908,7 @@ contains
|
|||
|
||||
! Find all cells with multiple (distributed) materials or temperatures.
|
||||
do i = 1, n_cells
|
||||
if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then
|
||||
if (cells(i) % material_size() > 1 .or. size(cells(i) % sqrtkT) > 1) then
|
||||
call cell_list % add(i)
|
||||
end if
|
||||
end do
|
||||
|
|
@ -3996,10 +3917,10 @@ contains
|
|||
! number of respective cell instances.
|
||||
do i = 1, n_cells
|
||||
associate (c => cells(i))
|
||||
if (size(c % material) > 1) then
|
||||
if (size(c % material) /= c % n_instances()) then
|
||||
if (c % material_size() > 1) then
|
||||
if (c % material_size() /= c % n_instances()) then
|
||||
call fatal_error("Cell " // trim(to_str(c % id())) // " was &
|
||||
&specified with " // trim(to_str(size(c % material))) &
|
||||
&specified with " // trim(to_str(c % material_size())) &
|
||||
// " materials but has " // trim(to_str(c % n_instances())) &
|
||||
// " distributed instances. The number of materials must &
|
||||
&equal one or the number of instances.")
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ std::unordered_map<int32_t, int32_t> lattice_map;
|
|||
Lattice::Lattice(pugi::xml_node lat_node)
|
||||
{
|
||||
if (check_for_node(lat_node, "id")) {
|
||||
id = stoi(get_node_value(lat_node, "id"));
|
||||
id = std::stoi(get_node_value(lat_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of lattice in geometry XML file.");
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ Lattice::Lattice(pugi::xml_node lat_node)
|
|||
}
|
||||
|
||||
if (check_for_node(lat_node, "outer")) {
|
||||
outer = stoi(get_node_value(lat_node, "outer"));
|
||||
outer = std::stoi(get_node_value(lat_node, "outer"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,14 +141,14 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
|
|||
std::string dimension_str {get_node_value(lat_node, "dimension")};
|
||||
std::vector<std::string> dimension_words {split(dimension_str)};
|
||||
if (dimension_words.size() == 2) {
|
||||
n_cells[0] = stoi(dimension_words[0]);
|
||||
n_cells[1] = stoi(dimension_words[1]);
|
||||
n_cells[0] = std::stoi(dimension_words[0]);
|
||||
n_cells[1] = std::stoi(dimension_words[1]);
|
||||
n_cells[2] = 1;
|
||||
is_3d = false;
|
||||
} else if (dimension_words.size() == 3) {
|
||||
n_cells[0] = stoi(dimension_words[0]);
|
||||
n_cells[1] = stoi(dimension_words[1]);
|
||||
n_cells[2] = stoi(dimension_words[2]);
|
||||
n_cells[0] = std::stoi(dimension_words[0]);
|
||||
n_cells[1] = std::stoi(dimension_words[1]);
|
||||
n_cells[2] = std::stoi(dimension_words[2]);
|
||||
is_3d = true;
|
||||
} else {
|
||||
fatal_error("Rectangular lattice must be two or three dimensions.");
|
||||
|
|
@ -195,7 +195,7 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
|
|||
for (int ix = 0; ix < nx; ix++) {
|
||||
int indx1 = nx*ny*iz + nx*(ny-iy-1) + ix;
|
||||
int indx2 = nx*ny*iz + nx*iy + ix;
|
||||
universes[indx1] = stoi(univ_words[indx2]);
|
||||
universes[indx1] = std::stoi(univ_words[indx2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -400,9 +400,9 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
: Lattice {lat_node}
|
||||
{
|
||||
// Read the number of lattice cells in each dimension.
|
||||
n_rings = stoi(get_node_value(lat_node, "n_rings"));
|
||||
n_rings = std::stoi(get_node_value(lat_node, "n_rings"));
|
||||
if (check_for_node(lat_node, "n_axial")) {
|
||||
n_axial = stoi(get_node_value(lat_node, "n_axial"));
|
||||
n_axial = std::stoi(get_node_value(lat_node, "n_axial"));
|
||||
is_3d = true;
|
||||
} else {
|
||||
n_axial = 1;
|
||||
|
|
@ -476,7 +476,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
@ -505,7 +505,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
@ -528,7 +528,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
|
|||
91
src/material.cpp
Normal file
91
src/material.cpp
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#include "material.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "error.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
std::vector<Material*> global_materials;
|
||||
std::unordered_map<int32_t, int32_t> material_map;
|
||||
|
||||
//==============================================================================
|
||||
// Material implementation
|
||||
//==============================================================================
|
||||
|
||||
Material::Material(pugi::xml_node material_node)
|
||||
{
|
||||
if (check_for_node(material_node, "id")) {
|
||||
id = std::stoi(get_node_value(material_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of material in materials XML file.");
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-method functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_materials(pugi::xml_node* node)
|
||||
{
|
||||
// Loop over XML material elements and populate the array.
|
||||
for (pugi::xml_node material_node : node->children("material")) {
|
||||
global_materials.push_back(new Material(material_node));
|
||||
}
|
||||
global_materials.shrink_to_fit();
|
||||
|
||||
// Populate the material map.
|
||||
for (int i = 0; i < global_materials.size(); i++) {
|
||||
int32_t mid = global_materials[i]->id;
|
||||
auto search = material_map.find(mid);
|
||||
if (search == material_map.end()) {
|
||||
material_map[mid] = i;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Two or more materials use the same unique ID: " << mid;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
Material* material_pointer(int32_t indx) {return global_materials[indx];}
|
||||
|
||||
int32_t material_id(Material* mat) {return mat->id;}
|
||||
|
||||
void material_set_id(Material* mat, int32_t id, int32_t index)
|
||||
{
|
||||
mat->id = id;
|
||||
//TODO: off-by-one
|
||||
material_map[id] = index - 1;
|
||||
}
|
||||
|
||||
void extend_materials_c(int32_t n)
|
||||
{
|
||||
global_materials.reserve(global_materials.size() + n);
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
global_materials.push_back(new Material());
|
||||
}
|
||||
}
|
||||
|
||||
void free_memory_material_c()
|
||||
{
|
||||
for (Material *mat : global_materials) {delete mat;}
|
||||
global_materials.clear();
|
||||
material_map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
35
src/material.h
Normal file
35
src/material.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef OPENMC_MATERIAL_H
|
||||
#define OPENMC_MATERIAL_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
class Material;
|
||||
extern std::vector<Material*> global_materials;
|
||||
extern std::unordered_map<int32_t, int32_t> material_map;
|
||||
|
||||
//==============================================================================
|
||||
//! A substance with constituent nuclides and thermal scattering data
|
||||
//==============================================================================
|
||||
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
int32_t id; //!< Unique ID
|
||||
|
||||
Material() {};
|
||||
|
||||
explicit Material(pugi::xml_node material_node);
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
#endif // OPENMC_MATERIAL_H
|
||||
|
|
@ -27,13 +27,41 @@ module material_header
|
|||
public :: openmc_material_set_density
|
||||
public :: openmc_material_set_densities
|
||||
public :: openmc_material_set_id
|
||||
public :: material_pointer
|
||||
|
||||
interface
|
||||
function material_pointer(mat_ind) bind(C) result(ptr)
|
||||
import C_PTR, C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: mat_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function material_pointer
|
||||
|
||||
function material_id_c(mat_ptr) bind(C, name='material_id') result(id)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T) :: id
|
||||
end function material_id_c
|
||||
|
||||
subroutine material_set_id_c(mat_ptr, id, index) &
|
||||
bind(C, name='material_set_id')
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T), intent(in), value :: id
|
||||
integer(C_INT32_T), intent(in), value :: index
|
||||
end subroutine material_set_id_c
|
||||
|
||||
subroutine extend_materials_c(n) bind(C)
|
||||
import C_INT32_t
|
||||
integer(C_INT32_T), intent(in), value :: n
|
||||
end subroutine extend_materials_c
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL describes a material by its constituent nuclides
|
||||
!===============================================================================
|
||||
|
||||
type, public :: Material
|
||||
integer :: id ! unique identifier
|
||||
type(C_PTR) :: ptr
|
||||
character(len=104) :: name = "" ! User-defined name
|
||||
integer :: n_nuclides = 0 ! number of nuclides
|
||||
integer, allocatable :: nuclide(:) ! index in nuclides array
|
||||
|
|
@ -67,6 +95,8 @@ module material_header
|
|||
logical, allocatable :: p0(:)
|
||||
|
||||
contains
|
||||
procedure :: id => material_id
|
||||
procedure :: set_id => material_set_id
|
||||
procedure :: set_density => material_set_density
|
||||
procedure :: init_nuclide_index => material_init_nuclide_index
|
||||
procedure :: assign_sab_tables => material_assign_sab_tables
|
||||
|
|
@ -88,6 +118,19 @@ contains
|
|||
! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm.
|
||||
!===============================================================================
|
||||
|
||||
function material_id(this) result(id)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T) :: id
|
||||
id = material_id_c(this % ptr)
|
||||
end function material_id
|
||||
|
||||
subroutine material_set_id(this, id, index)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T), intent(in) :: id
|
||||
integer(C_INT32_T), intent(in) :: index
|
||||
call material_set_id_c(this % ptr, id, index)
|
||||
end subroutine material_set_id
|
||||
|
||||
function material_set_density(this, density) result(err)
|
||||
class(Material), intent(inout) :: this
|
||||
real(8), intent(in) :: density
|
||||
|
|
@ -185,7 +228,7 @@ contains
|
|||
if (.not. found) then
|
||||
call fatal_error("S(a,b) table " // trim(this % &
|
||||
sab_names(k)) // " did not match any nuclide on material " &
|
||||
// trim(to_str(this % id)))
|
||||
// trim(to_str(this % id())))
|
||||
end if
|
||||
end do ASSIGN_SAB
|
||||
|
||||
|
|
@ -195,7 +238,7 @@ contains
|
|||
if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then
|
||||
call fatal_error(trim( &
|
||||
nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) &
|
||||
// " in material " // trim(to_str(this % id)) // " was found &
|
||||
// " in material " // trim(to_str(this % id())) // " was found &
|
||||
&in multiple S(a,b) tables. Each nuclide can only appear in &
|
||||
&one S(a,b) table per material.")
|
||||
end if
|
||||
|
|
@ -444,6 +487,11 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine free_memory_material()
|
||||
interface
|
||||
subroutine free_memory_material_c() bind(C)
|
||||
end subroutine free_memory_material_c
|
||||
end interface
|
||||
call free_memory_material_c()
|
||||
n_materials = 0
|
||||
if (allocated(materials)) deallocate(materials)
|
||||
call material_dict % clear()
|
||||
|
|
@ -460,6 +508,7 @@ contains
|
|||
integer(C_INT32_T), optional, intent(out) :: index_end
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: i
|
||||
type(Material), allocatable :: temp(:) ! temporary materials array
|
||||
|
||||
if (n_materials == 0) then
|
||||
|
|
@ -481,6 +530,12 @@ contains
|
|||
if (present(index_end)) index_end = n_materials + n
|
||||
n_materials = n_materials + n
|
||||
|
||||
! Extend the C++ materials array and get pointers to the C++ objects
|
||||
call extend_materials_c(n)
|
||||
do i = n_materials - n, n_materials
|
||||
materials(i) % ptr = material_pointer(i - 1)
|
||||
end do
|
||||
|
||||
err = 0
|
||||
end function openmc_extend_materials
|
||||
|
||||
|
|
@ -606,7 +661,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
id = materials(index) % id
|
||||
id = materials(index) % id()
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
|
|
@ -622,7 +677,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= n_materials) then
|
||||
materials(index) % id = id
|
||||
call materials(index) % set_id(id, index)
|
||||
call material_dict % set(id, index)
|
||||
err = 0
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ module nuclide_header
|
|||
FIT_T, FIT_A, FIT_F, MultipoleArray
|
||||
use message_passing
|
||||
use multipole_header, only: MultipoleArray
|
||||
use product_header, only: AngleEnergyContainer
|
||||
use random_lcg, only: prn, future_prn, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use sab_header, only: SAlphaBeta, sab_tables
|
||||
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
|
||||
use settings
|
||||
use stl_vector, only: VectorInt, VectorReal
|
||||
use string
|
||||
|
|
@ -642,29 +640,33 @@ contains
|
|||
if (rx % MT >= N_2N0 .and. rx % MT <= N_2NC .and. find(MTs, N_2N) /= -1) cycle
|
||||
|
||||
do t = 1, n_temperature
|
||||
j = rx % xs(t) % threshold
|
||||
n = size(rx % xs(t) % value)
|
||||
j = rx % xs_threshold(t)
|
||||
n = rx % xs_size(t)
|
||||
|
||||
! Add contribution to total cross section
|
||||
this % xs(t) % value(XS_TOTAL,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_TOTAL,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_TOTAL,k) = this % xs(t) % &
|
||||
value(XS_TOTAL,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
|
||||
! Calculate photon production cross section
|
||||
do k = 1, size(rx % products)
|
||||
if (rx % products(k) % particle == PHOTON) then
|
||||
do k = 1, rx % products_size()
|
||||
if (rx % product_particle(k) == PHOTON) then
|
||||
do l = 1, n
|
||||
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) = &
|
||||
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) + &
|
||||
rx % xs(t) % value(l) * rx % products(k) % &
|
||||
yield % evaluate(this % grid(t) % energy(l+j-1))
|
||||
rx % xs(t, l) * rx % product_yield(k, &
|
||||
this % grid(t) % energy(l+j-1))
|
||||
end do
|
||||
end if
|
||||
end do
|
||||
|
||||
! Add contribution to absorption cross section
|
||||
if (is_disappearance(rx % MT)) then
|
||||
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
end if
|
||||
|
||||
! Information about fission reactions
|
||||
|
|
@ -680,39 +682,20 @@ contains
|
|||
! Add contribution to fission cross section
|
||||
if (is_fission(rx % MT)) then
|
||||
this % fissionable = .true.
|
||||
this % xs(t) % value(XS_FISSION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_FISSION,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_FISSION,k) = this % xs(t) % &
|
||||
value(XS_FISSION,k) + rx % xs(t, k - j + 1)
|
||||
|
||||
! Also need to add fission cross sections to absorption
|
||||
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
|
||||
! Also need to add fission cross sections to absorption
|
||||
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
|
||||
! Keep track of this reaction for easy searching later
|
||||
if (t == 1) then
|
||||
i_fission = i_fission + 1
|
||||
this % index_fission(i_fission) = i
|
||||
this % n_fission = this % n_fission + 1
|
||||
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
! Before the secondary distribution refactor, when the angle/energy
|
||||
! distribution was uncorrelated, no angle was actually sampled. With
|
||||
! the refactor, an angle is always sampled for an uncorrelated
|
||||
! distribution even when no angle distribution exists in the ACE file
|
||||
! (isotropic is assumed). To preserve the RNG stream, we explicitly
|
||||
! mark fission reactions so that we avoid the angle sampling.
|
||||
do k = 1, size(rx % products)
|
||||
if (rx % products(k) % particle == NEUTRON) then
|
||||
do m = 1, size(rx % products(k) % distribution)
|
||||
associate (aedist => rx % products(k) % distribution(m) % obj)
|
||||
select type (aedist)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
aedist % fission = .true.
|
||||
end select
|
||||
end associate
|
||||
end do
|
||||
end if
|
||||
end do
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
end if
|
||||
end if ! fission
|
||||
end do ! temperature
|
||||
|
|
@ -721,12 +704,13 @@ contains
|
|||
|
||||
! Determine number of delayed neutron precursors
|
||||
if (this % fissionable) then
|
||||
do i = 1, size(this % reactions(this % index_fission(1)) % products)
|
||||
if (this % reactions(this % index_fission(1)) % products(i) % &
|
||||
emission_mode == EMISSION_DELAYED) then
|
||||
this % n_precursor = this % n_precursor + 1
|
||||
end if
|
||||
end do
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
do i = 1, rx % products_size()
|
||||
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
|
||||
this % n_precursor = this % n_precursor + 1
|
||||
end if
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
|
||||
! Calculate nu-fission cross section
|
||||
|
|
@ -761,36 +745,30 @@ contains
|
|||
|
||||
select case (emission_mode)
|
||||
case (EMISSION_PROMPT)
|
||||
associate (product => this % reactions(this % index_fission(1)) % products(1))
|
||||
nu = product % yield % evaluate(E)
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
nu = rx % product_yield(1, E)
|
||||
end associate
|
||||
|
||||
case (EMISSION_DELAYED)
|
||||
if (this % n_precursor > 0) then
|
||||
if (present(group) .and. group < &
|
||||
size(this % reactions(this % index_fission(1)) % products)) then
|
||||
! If delayed group specified, determine yield immediately
|
||||
associate(p => this % reactions(this % index_fission(1)) % products(1 + group))
|
||||
nu = p % yield % evaluate(E)
|
||||
end associate
|
||||
associate(rx => this % reactions(this % index_fission(1)))
|
||||
if (present(group) .and. group < rx % products_size()) then
|
||||
! If delayed group specified, determine yield immediately
|
||||
nu = rx % product_yield(1 + group, E)
|
||||
else
|
||||
nu = ZERO
|
||||
|
||||
else
|
||||
nu = ZERO
|
||||
do i = 2, rx % products_size()
|
||||
! Skip any non-neutron products
|
||||
if (rx % product_particle(i) /= NEUTRON) exit
|
||||
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
do i = 2, size(rx % products)
|
||||
associate (product => rx % products(i))
|
||||
! Skip any non-neutron products
|
||||
if (product % particle /= NEUTRON) exit
|
||||
|
||||
! Evaluate yield
|
||||
if (product % emission_mode == EMISSION_DELAYED) then
|
||||
nu = nu + product % yield % evaluate(E)
|
||||
end if
|
||||
end associate
|
||||
! Evaluate yield
|
||||
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
|
||||
nu = nu + rx % product_yield(i, E)
|
||||
end if
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
nu = ZERO
|
||||
end if
|
||||
|
|
@ -799,8 +777,8 @@ contains
|
|||
if (allocated(this % total_nu)) then
|
||||
nu = this % total_nu % evaluate(E)
|
||||
else
|
||||
associate (product => this % reactions(this % index_fission(1)) % products(1))
|
||||
nu = product % yield % evaluate(E)
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
nu = rx % product_yield(1, E)
|
||||
end associate
|
||||
end if
|
||||
end select
|
||||
|
|
@ -868,6 +846,7 @@ contains
|
|||
integer :: i_high ! upper logarithmic mapping index
|
||||
integer :: i_rxn ! reaction index
|
||||
integer :: j ! index in DEPLETION_RX
|
||||
integer :: threshold ! threshold energy index
|
||||
real(8) :: f ! interp factor on nuclide energy grid
|
||||
real(8) :: kT ! temperature in eV
|
||||
real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables
|
||||
|
|
@ -1009,10 +988,11 @@ contains
|
|||
! need to specifically check its threshold index
|
||||
i_rxn = this % reaction_index(DEPLETION_RX(1))
|
||||
if (i_rxn > 0) then
|
||||
associate (xs => this % reactions(i_rxn) % xs(i_temp))
|
||||
associate (rx => this % reactions(i_rxn))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
micro_xs % reaction(1) = (ONE - f) * &
|
||||
xs % value(i_grid - xs % threshold + 1) + &
|
||||
f * xs % value(i_grid - xs % threshold + 2)
|
||||
rx % xs(i_temp, i_grid - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_grid - threshold + 2)
|
||||
end associate
|
||||
end if
|
||||
|
||||
|
|
@ -1022,11 +1002,12 @@ contains
|
|||
! reaction xs appropriately
|
||||
i_rxn = this % reaction_index(DEPLETION_RX(j))
|
||||
if (i_rxn > 0) then
|
||||
associate (xs => this % reactions(i_rxn) % xs(i_temp))
|
||||
if (i_grid >= xs % threshold) then
|
||||
associate (rx => this % reactions(i_rxn))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid >= threshold) then
|
||||
micro_xs % reaction(j) = (ONE - f) * &
|
||||
xs % value(i_grid - xs % threshold + 1) + &
|
||||
f * xs % value(i_grid - xs % threshold + 2)
|
||||
rx % xs(i_temp, i_grid - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_grid - threshold + 2)
|
||||
elseif (j >= 4) then
|
||||
! One can show that the the threshold for (n,(x+1)n) is always
|
||||
! higher than the threshold for (n,xn). Thus, if we are below
|
||||
|
|
@ -1091,8 +1072,9 @@ contains
|
|||
f = micro_xs % interp_factor
|
||||
|
||||
if (i_temp > 0) then
|
||||
associate (xs => this % reactions(1) % xs(i_temp) % value)
|
||||
micro_xs % elastic = (ONE - f) * xs(i_grid) + f * xs(i_grid + 1)
|
||||
associate (rx => this % reactions(1))
|
||||
micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + &
|
||||
f * rx % xs(i_temp, i_grid + 1)
|
||||
end associate
|
||||
else
|
||||
! For multipole, elastic is total - absorption
|
||||
|
|
@ -1459,6 +1441,7 @@ contains
|
|||
integer :: i_energy ! index for energy
|
||||
integer :: i_low ! band index at lower bounding energy
|
||||
integer :: i_up ! band index at upper bounding energy
|
||||
integer :: threshold ! threshold energy index
|
||||
real(8) :: f ! interpolation factor
|
||||
real(8) :: r ! pseudo-random number
|
||||
real(8) :: elastic ! elastic cross section
|
||||
|
|
@ -1551,10 +1534,11 @@ contains
|
|||
f = micro_xs % interp_factor
|
||||
|
||||
! Determine inelastic scattering cross section
|
||||
associate (xs => this % reactions(this % urr_inelastic) % xs(i_temp))
|
||||
if (i_energy >= xs % threshold) then
|
||||
inelastic = (ONE - f) * xs % value(i_energy - xs % threshold + 1) + &
|
||||
f * xs % value(i_energy - xs % threshold + 2)
|
||||
associate (rx => this % reactions(this % urr_inelastic))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_energy >= threshold) then
|
||||
inelastic = (ONE - f) * rx % xs(i_temp, i_energy - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_energy - threshold + 2)
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ module physics
|
|||
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use sab_header, only: sab_tables
|
||||
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
|
||||
use settings
|
||||
use simulation_header
|
||||
use string, only: to_str
|
||||
|
|
@ -506,6 +505,7 @@ contains
|
|||
integer :: i
|
||||
integer :: i_grid
|
||||
integer :: i_temp
|
||||
integer :: threshold
|
||||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
|
|
@ -545,13 +545,14 @@ contains
|
|||
FISSION_REACTION_LOOP: do i = 1, nuc % n_fission
|
||||
i_reaction = nuc % index_fission(i)
|
||||
|
||||
associate (xs => nuc % reactions(i_reaction) % xs(i_temp))
|
||||
associate (rx => nuc % reactions(i_reaction))
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < xs % threshold) cycle
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
! add to cumulative probability
|
||||
prob = prob + ((ONE - f) * xs % value(i_grid - xs % threshold + 1) &
|
||||
+ f*(xs % value(i_grid - xs % threshold + 2)))
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
|
||||
end associate
|
||||
|
||||
! Create fission bank sites if fission occurs
|
||||
|
|
@ -593,17 +594,17 @@ contains
|
|||
! Loop through each reaction type
|
||||
REACTION_LOOP: do i_reaction = 1, size(nuc % reactions)
|
||||
associate (rx => nuc % reactions(i_reaction))
|
||||
threshold = rx % xs(i_temp) % threshold
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
do i_product = 1, size(rx % products)
|
||||
if (rx % products(i_product) % particle == PHOTON) then
|
||||
do i_product = 1, rx % products_size()
|
||||
if (rx % product_particle(i_product) == PHOTON) then
|
||||
! add to cumulative probability
|
||||
yield = rx % products(i_product) % yield % evaluate(E)
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp) % value(i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp) % value(i_grid - threshold + 2))) * yield
|
||||
yield = rx % product_yield(i_product, E)
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2))) * yield
|
||||
|
||||
if (prob > cutoff) return
|
||||
last_valid_reaction = i_reaction
|
||||
|
|
@ -672,6 +673,7 @@ contains
|
|||
integer :: j
|
||||
integer :: i_temp
|
||||
integer :: i_grid
|
||||
integer :: threshold
|
||||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
|
|
@ -750,14 +752,14 @@ contains
|
|||
&// trim(nuc % name))
|
||||
end if
|
||||
|
||||
associate (rx => nuc % reactions(i), &
|
||||
xs => nuc % reactions(i) % xs(i_temp))
|
||||
associate (rx => nuc % reactions(i))
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < xs % threshold) cycle
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
! add to cumulative probability
|
||||
prob = prob + ((ONE - f)*xs % value(i_grid - xs % threshold + 1) &
|
||||
+ f*(xs % value(i_grid - xs % threshold + 2)))
|
||||
prob = prob + ((ONE - f)*rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
|
||||
end associate
|
||||
end do
|
||||
|
||||
|
|
@ -839,14 +841,7 @@ contains
|
|||
vel = sqrt(dot_product(v_n, v_n))
|
||||
|
||||
! Sample scattering angle
|
||||
select type (dist => rxn % products(1) % distribution(1) % obj)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
if (allocated(dist % angle % energy)) then
|
||||
mu_cm = dist % angle % sample(E)
|
||||
else
|
||||
mu_cm = TWO*prn() - ONE
|
||||
end if
|
||||
end select
|
||||
mu_cm = rxn % sample_elastic_mu(E)
|
||||
|
||||
! Determine direction cosines in CM
|
||||
uvw_cm = v_n/vel
|
||||
|
|
@ -1581,7 +1576,7 @@ contains
|
|||
do group = 1, nuc % n_precursor
|
||||
|
||||
! determine delayed neutron precursor yield for group j
|
||||
yield = rxn % products(1 + group) % yield % evaluate(E_in)
|
||||
yield = rxn % product_yield(1 + group, E_in)
|
||||
|
||||
! Check if this group is sampled
|
||||
prob = prob + yield
|
||||
|
|
@ -1600,7 +1595,7 @@ contains
|
|||
do
|
||||
! sample from energy/angle distribution -- note that mu has already been
|
||||
! sampled above and doesn't need to be resampled
|
||||
call rxn % products(1 + group) % sample(E_in, site % E, mu)
|
||||
call rxn % product_sample(1 + group, E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
|
@ -1624,7 +1619,7 @@ contains
|
|||
! sample from prompt neutron energy distribution
|
||||
n_sample = 0
|
||||
do
|
||||
call rxn % products(1) % sample(E_in, site % E, mu)
|
||||
call rxn % product_sample(1, E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
|
@ -1663,7 +1658,7 @@ contains
|
|||
E_in = p % E
|
||||
|
||||
! sample outgoing energy and scattering cosine
|
||||
call rxn % products(1) % sample(E_in, E, mu)
|
||||
call rxn % product_sample(1, E_in, E, mu)
|
||||
|
||||
! if scattering system is in center-of-mass, transfer cosine of scattering
|
||||
! angle and outgoing energy from CM to LAB
|
||||
|
|
@ -1692,7 +1687,7 @@ contains
|
|||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
||||
! evaluate yield
|
||||
yield = rxn % products(1) % yield % evaluate(E_in)
|
||||
yield = rxn % product_yield(1, E_in)
|
||||
if (mod(yield, ONE) == ZERO) then
|
||||
! If yield is integral, create exactly that many secondary particles
|
||||
do i = 1, nint(yield) - 1
|
||||
|
|
@ -1740,8 +1735,8 @@ contains
|
|||
call sample_photon_product(i_nuclide, p % E, i_reaction, i_product)
|
||||
|
||||
! Sample the outgoing energy and angle
|
||||
call nuclides(i_nuclide) % reactions(i_reaction) % products(i_product) &
|
||||
% sample(p % E, E, mu)
|
||||
call nuclides(i_nuclide) % reactions(i_reaction) % &
|
||||
product_sample(i_product, p % E, E, mu)
|
||||
|
||||
! Sample the new direction
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
161
src/reaction.cpp
Normal 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
65
src/reaction.h
Normal 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
|
||||
|
|
@ -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
107
src/reaction_product.cpp
Normal 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
56
src/reaction_product.h
Normal 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
|
||||
|
|
@ -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
23
src/search.h
Normal 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
|
||||
244
src/secondary_correlated.cpp
Normal file
244
src/secondary_correlated.cpp
Normal 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
|
||||
52
src/secondary_correlated.h
Normal file
52
src/secondary_correlated.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
//! \file secondary_correlated.h
|
||||
//! Correlated angle-energy distribution
|
||||
|
||||
#ifndef OPENMC_SECONDARY_CORRELATED_H
|
||||
#define OPENMC_SECONDARY_CORRELATED_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "xtensor/xtensor.hpp"
|
||||
#include "angle_energy.h"
|
||||
#include "endf.h"
|
||||
#include "distribution.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Correlated angle-energy distribution corresponding to ACE law 61 and ENDF
|
||||
//! File 6, LAW=1, LANG!=2.
|
||||
//==============================================================================
|
||||
|
||||
class CorrelatedAngleEnergy : public AngleEnergy {
|
||||
public:
|
||||
explicit CorrelatedAngleEnergy(hid_t group);
|
||||
|
||||
//! Sample distribution for an angle and energy
|
||||
//! \param[in] E_in Incoming energy in [eV]
|
||||
//! \param[out] E_out Outgoing energy in [eV]
|
||||
//! \param[out] mu Outgoing cosine with respect to current direction
|
||||
void sample(double E_in, double& E_out, double& mu) const;
|
||||
private:
|
||||
//! Outgoing energy/angle at a single incoming energy
|
||||
struct CorrTable {
|
||||
int n_discrete; //!< Number of discrete lines
|
||||
Interpolation interpolation; //!< Interpolation law
|
||||
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV]
|
||||
xt::xtensor<double, 1> p; //!< Probability density
|
||||
xt::xtensor<double, 1> c; //!< Cumulative distribution
|
||||
std::vector<UPtrDist> angle; //!< Angle distribution
|
||||
};
|
||||
|
||||
int n_region_; //!< Number of interpolation regions
|
||||
std::vector<int> breakpoints_; //!< Breakpoints between regions
|
||||
std::vector<Interpolation> interpolation_; //!< Interpolation laws
|
||||
std::vector<double> energy_; //!< Energies [eV] at which distributions
|
||||
//!< are tabulated
|
||||
std::vector<CorrTable> distribution_; //!< Distribution at each energy
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_SECONDARY_CORRELATED_H
|
||||
|
|
@ -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
223
src/secondary_kalbach.cpp
Normal 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
54
src/secondary_kalbach.h
Normal 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
|
||||
|
|
@ -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
65
src/secondary_nbody.cpp
Normal 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
37
src/secondary_nbody.h
Normal 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
|
||||
|
|
@ -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
|
||||
74
src/secondary_uncorrelated.cpp
Normal file
74
src/secondary_uncorrelated.cpp
Normal 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
|
||||
44
src/secondary_uncorrelated.h
Normal file
44
src/secondary_uncorrelated.h
Normal 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
|
||||
|
|
@ -7,7 +7,7 @@ module simulation
|
|||
#endif
|
||||
|
||||
use bank_header, only: source_bank
|
||||
use cmfd_execute, only: cmfd_init_batch, execute_cmfd
|
||||
use cmfd_execute, only: cmfd_init_batch, cmfd_tally_init, execute_cmfd
|
||||
use cmfd_header, only: cmfd_on
|
||||
use constants, only: ZERO
|
||||
use eigenvalue, only: count_source_for_ufs, calculate_average_keff, &
|
||||
|
|
@ -317,6 +317,7 @@ contains
|
|||
#ifdef OPENMC_MPI
|
||||
integer :: mpi_err ! MPI error code
|
||||
#endif
|
||||
character(MAX_FILE_LEN) :: filename
|
||||
|
||||
! Reduce tallies onto master process and accumulate
|
||||
call time_tallies % start()
|
||||
|
|
@ -349,13 +350,22 @@ contains
|
|||
|
||||
! Write out state point if it's been specified for this batch
|
||||
if (statepoint_batch % contains(current_batch)) then
|
||||
err = openmc_statepoint_write()
|
||||
if (sourcepoint_batch % contains(current_batch) .and. source_write &
|
||||
.and. .not. source_separate) then
|
||||
err = openmc_statepoint_write(write_source=.true._C_BOOL)
|
||||
else
|
||||
err = openmc_statepoint_write(write_source=.false._C_BOOL)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Write out source point if it's been specified for this batch
|
||||
if ((sourcepoint_batch % contains(current_batch) .or. source_latest) .and. &
|
||||
source_write) then
|
||||
call write_source_point()
|
||||
! Write out a separate source point if it's been specified for this batch
|
||||
if (sourcepoint_batch % contains(current_batch) .and. source_write &
|
||||
.and. source_separate) call write_source_point()
|
||||
|
||||
! Write a continously-overwritten source point if requested.
|
||||
if (source_latest) then
|
||||
filename = trim(path_output) // 'source' // '.h5'
|
||||
call write_source_point(filename)
|
||||
end if
|
||||
|
||||
end subroutine finalize_batch
|
||||
|
|
@ -428,6 +438,9 @@ contains
|
|||
! Allocate tally results arrays if they're not allocated yet
|
||||
call configure_tallies()
|
||||
|
||||
! Activate the CMFD tallies
|
||||
call cmfd_tally_init()
|
||||
|
||||
! Set up material nuclide index mapping
|
||||
do i = 1, n_materials
|
||||
call materials(i) % init_nuclide_index()
|
||||
|
|
@ -568,6 +581,13 @@ contains
|
|||
! Write tally results to tallies.out
|
||||
if (output_tallies .and. master) call write_tallies()
|
||||
|
||||
! Deactivate all tallies
|
||||
if (allocated(tallies)) then
|
||||
do i = 1, n_tallies
|
||||
tallies(i) % obj % active = .false.
|
||||
end do
|
||||
end if
|
||||
|
||||
! Stop timers and show timing statistics
|
||||
call time_finalize%stop()
|
||||
call time_total%stop()
|
||||
|
|
|
|||
|
|
@ -58,10 +58,12 @@ contains
|
|||
! OPENMC_STATEPOINT_WRITE writes an HDF5 statepoint file to disk
|
||||
!===============================================================================
|
||||
|
||||
function openmc_statepoint_write(filename) result(err) bind(C)
|
||||
type(C_PTR), intent(in), optional :: filename
|
||||
function openmc_statepoint_write(filename, write_source) result(err) bind(C)
|
||||
type(C_PTR), intent(in), optional :: filename
|
||||
logical(C_BOOL), intent(in), optional :: write_source
|
||||
integer(C_INT) :: err
|
||||
|
||||
logical :: write_source_
|
||||
integer :: i, j, k
|
||||
integer :: i_xs
|
||||
integer, allocatable :: id_array(:)
|
||||
|
|
@ -69,13 +71,17 @@ contains
|
|||
integer(HID_T) :: cmfd_group, tallies_group, tally_group, meshes_group, &
|
||||
filters_group, filter_group, derivs_group, &
|
||||
deriv_group, runtime_group
|
||||
integer(C_INT) :: ignored_err
|
||||
real(C_DOUBLE) :: k_combined(2)
|
||||
character(MAX_WORD_LEN), allocatable :: str_array(:)
|
||||
character(C_CHAR), pointer :: string(:)
|
||||
character(len=:, kind=C_CHAR), allocatable :: filename_
|
||||
character(MAX_WORD_LEN, kind=C_CHAR) :: temp_name
|
||||
logical :: parallel
|
||||
|
||||
err = 0
|
||||
|
||||
! Set the filename
|
||||
if (present(filename)) then
|
||||
call c_f_pointer(filename, string, [MAX_FILE_LEN])
|
||||
filename_ = to_f_string(string)
|
||||
|
|
@ -86,6 +92,13 @@ contains
|
|||
filename_ = trim(filename_) // '.h5'
|
||||
end if
|
||||
|
||||
! Determine whether or not to write the source bank
|
||||
if (present(write_source)) then
|
||||
write_source_ = write_source
|
||||
else
|
||||
write_source_ = .true.
|
||||
end if
|
||||
|
||||
! Write message
|
||||
call write_message("Creating state point " // trim(filename_) // "...", 5)
|
||||
|
||||
|
|
@ -138,10 +151,10 @@ contains
|
|||
call write_dataset(file_id, "current_batch", current_batch)
|
||||
|
||||
! Indicate whether source bank is stored in statepoint
|
||||
if (source_separate) then
|
||||
call write_attribute(file_id, "source_present", 0)
|
||||
else
|
||||
if (write_source_) then
|
||||
call write_attribute(file_id, "source_present", 1)
|
||||
else
|
||||
call write_attribute(file_id, "source_present", 0)
|
||||
end if
|
||||
|
||||
! Write out information for eigenvalue run
|
||||
|
|
@ -156,7 +169,7 @@ contains
|
|||
call write_dataset(file_id, "k_col_abs", k_col_abs)
|
||||
call write_dataset(file_id, "k_col_tra", k_col_tra)
|
||||
call write_dataset(file_id, "k_abs_tra", k_abs_tra)
|
||||
err = openmc_get_keff(k_combined)
|
||||
ignored_err = openmc_get_keff(k_combined)
|
||||
call write_dataset(file_id, "k_combined", k_combined)
|
||||
|
||||
! Write out CMFD info
|
||||
|
|
@ -432,17 +445,33 @@ contains
|
|||
|
||||
call file_close(file_id)
|
||||
end if
|
||||
|
||||
#ifdef PHDF5
|
||||
parallel = .true.
|
||||
#else
|
||||
parallel = .false.
|
||||
#endif
|
||||
|
||||
! Write the source bank if desired
|
||||
if (write_source_) then
|
||||
if (master .or. parallel) then
|
||||
file_id = file_open(filename_, 'a', parallel=.true.)
|
||||
end if
|
||||
call write_source_bank(file_id, work_index, source_bank)
|
||||
if (master .or. parallel) call file_close(file_id)
|
||||
end if
|
||||
end function openmc_statepoint_write
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_SOURCE_POINT
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_source_point()
|
||||
subroutine write_source_point(filename)
|
||||
character(MAX_FILE_LEN), intent(in), optional :: filename
|
||||
|
||||
logical :: parallel
|
||||
integer(HID_T) :: file_id
|
||||
character(MAX_FILE_LEN) :: filename
|
||||
character(MAX_FILE_LEN) :: filename_
|
||||
|
||||
! When using parallel HDF5, the file is written to collectively by all
|
||||
! processes. With MPI-only, the file is opened and written by the master
|
||||
|
|
@ -454,47 +483,20 @@ contains
|
|||
parallel = .false.
|
||||
#endif
|
||||
|
||||
! Check to write out source for a specified batch
|
||||
if (sourcepoint_batch%contains(current_batch)) then
|
||||
if (source_separate) then
|
||||
filename = trim(path_output) // 'source.' // &
|
||||
& zero_padded(current_batch, count_digits(n_max_batches))
|
||||
filename = trim(filename) // '.h5'
|
||||
call write_message("Creating source file " // trim(filename) &
|
||||
// "...", 5)
|
||||
|
||||
! Create separate source file
|
||||
if (master .or. parallel) then
|
||||
file_id = file_open(filename, 'w', parallel=.true.)
|
||||
call write_attribute(file_id, "filetype", 'source')
|
||||
end if
|
||||
else
|
||||
filename = trim(path_output) // 'statepoint.' // &
|
||||
zero_padded(current_batch, count_digits(n_max_batches))
|
||||
filename = trim(filename) // '.h5'
|
||||
|
||||
if (master .or. parallel) then
|
||||
file_id = file_open(filename, 'a', parallel=.true.)
|
||||
end if
|
||||
end if
|
||||
|
||||
call write_source_bank(file_id, work_index, source_bank)
|
||||
if (master .or. parallel) call file_close(file_id)
|
||||
if (present(filename)) then
|
||||
filename_ = filename
|
||||
else
|
||||
filename_ = trim(path_output) // 'source.' // &
|
||||
& zero_padded(current_batch, count_digits(n_max_batches))
|
||||
filename_ = trim(filename_) // '.h5'
|
||||
end if
|
||||
|
||||
! Also check to write source separately in overwritten file
|
||||
if (source_latest) then
|
||||
filename = trim(path_output) // 'source' // '.h5'
|
||||
call write_message("Creating source file " // trim(filename) // "...", 5)
|
||||
if (master .or. parallel) then
|
||||
file_id = file_open(filename, 'w', parallel=.true.)
|
||||
call write_attribute(file_id, "filetype", 'source')
|
||||
end if
|
||||
|
||||
call write_source_bank(file_id, work_index, source_bank)
|
||||
|
||||
if (master .or. parallel) call file_close(file_id)
|
||||
if (master .or. parallel) then
|
||||
file_id = file_open(filename_, 'w', parallel=.true.)
|
||||
call write_attribute(file_id, "filetype", 'source')
|
||||
end if
|
||||
call write_source_bank(file_id, work_index, source_bank)
|
||||
if (master .or. parallel) call file_close(file_id)
|
||||
|
||||
end subroutine write_source_point
|
||||
|
||||
|
|
|
|||
|
|
@ -198,20 +198,20 @@ contains
|
|||
case (FILL_MATERIAL)
|
||||
call write_dataset(cell_group, "fill_type", "material")
|
||||
|
||||
if (size(c % material) == 1) then
|
||||
if (c % material_size() == 1) then
|
||||
if (c % material(1) == MATERIAL_VOID) then
|
||||
call write_dataset(cell_group, "material", MATERIAL_VOID)
|
||||
else
|
||||
call write_dataset(cell_group, "material", &
|
||||
materials(c % material(1)) % id)
|
||||
materials(c % material(1)) % id())
|
||||
end if
|
||||
else
|
||||
allocate(cell_materials(size(c % material)))
|
||||
do j = 1, size(c % material)
|
||||
allocate(cell_materials(c % material_size()))
|
||||
do j = 1, c % material_size()
|
||||
if (c % material(j) == MATERIAL_VOID) then
|
||||
cell_materials(j) = MATERIAL_VOID
|
||||
else
|
||||
cell_materials(j) = materials(c % material(j)) % id
|
||||
cell_materials(j) = materials(c % material(j)) % id()
|
||||
end if
|
||||
end do
|
||||
call write_dataset(cell_group, "material", cell_materials)
|
||||
|
|
@ -333,7 +333,7 @@ contains
|
|||
do i = 1, n_materials
|
||||
m => materials(i)
|
||||
material_group = create_group(materials_group, "material " // &
|
||||
trim(to_str(m%id)))
|
||||
trim(to_str(m%id())))
|
||||
|
||||
if (m % depletable) then
|
||||
call write_attribute(material_group, "depletable", 1)
|
||||
|
|
|
|||
157
src/surface.cpp
157
src/surface.cpp
|
|
@ -27,7 +27,7 @@ extern "C" const int BC_PERIODIC {3};
|
|||
|
||||
int32_t n_surfaces;
|
||||
|
||||
Surface **surfaces_c;
|
||||
std::vector<Surface*> global_surfaces;
|
||||
|
||||
std::map<int, int> surface_map;
|
||||
|
||||
|
|
@ -141,17 +141,17 @@ void read_coeffs(pugi::xml_node surf_node, int surf_id, double &c1, double &c2,
|
|||
Surface::Surface(pugi::xml_node surf_node)
|
||||
{
|
||||
if (check_for_node(surf_node, "id")) {
|
||||
id = stoi(get_node_value(surf_node, "id"));
|
||||
id = std::stoi(get_node_value(surf_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of surface in geometry XML file.");
|
||||
}
|
||||
|
||||
if (check_for_node(surf_node, "name")) {
|
||||
name = get_node_value(surf_node, "name");
|
||||
name = get_node_value(surf_node, "name", false);
|
||||
}
|
||||
|
||||
if (check_for_node(surf_node, "boundary")) {
|
||||
std::string surf_bc = get_node_value(surf_node, "boundary");
|
||||
std::string surf_bc = get_node_value(surf_node, "boundary", true, true);
|
||||
|
||||
if (surf_bc == "transmission" || surf_bc == "transmit" ||surf_bc.empty()) {
|
||||
bc = BC_TRANSMIT;
|
||||
|
|
@ -247,7 +247,7 @@ PeriodicSurface::PeriodicSurface(pugi::xml_node surf_node)
|
|||
: Surface {surf_node}
|
||||
{
|
||||
if (check_for_node(surf_node, "periodic_surface_id")) {
|
||||
i_periodic = stoi(get_node_value(surf_node, "periodic_surface_id"));
|
||||
i_periodic = std::stoi(get_node_value(surf_node, "periodic_surface_id"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -298,8 +298,8 @@ void SurfaceXPlane::to_hdf5_inner(hid_t group_id) const
|
|||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
bool SurfaceXPlane::periodic_translate(const PeriodicSurface *other, Position& r,
|
||||
Direction& u) const
|
||||
bool SurfaceXPlane::periodic_translate(const PeriodicSurface* other,
|
||||
Position& r, Direction& u) const
|
||||
{
|
||||
Direction other_n = other->normal(r);
|
||||
if (other_n.x == 1 and other_n.y == 0 and other_n.z == 0) {
|
||||
|
|
@ -359,8 +359,8 @@ void SurfaceYPlane::to_hdf5_inner(hid_t group_id) const
|
|||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other, Position& r,
|
||||
Direction& u) const
|
||||
bool SurfaceYPlane::periodic_translate(const PeriodicSurface *other,
|
||||
Position& r, Direction& u) const
|
||||
{
|
||||
Direction other_n = other->normal(r);
|
||||
if (other_n.x == 0 and other_n.y == 1 and other_n.z == 0) {
|
||||
|
|
@ -421,8 +421,8 @@ void SurfaceZPlane::to_hdf5_inner(hid_t group_id) const
|
|||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
bool SurfaceZPlane::periodic_translate(const PeriodicSurface *other, Position& r,
|
||||
Direction& u) const
|
||||
bool SurfaceZPlane::periodic_translate(const PeriodicSurface* other,
|
||||
Position& r, Direction& u) const
|
||||
{
|
||||
// Assume the other plane is aligned along z. Just change the z coord.
|
||||
r.z = z0;
|
||||
|
|
@ -478,7 +478,7 @@ void SurfacePlane::to_hdf5_inner(hid_t group_id) const
|
|||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
bool SurfacePlane::periodic_translate(const PeriodicSurface *other, Position& r,
|
||||
bool SurfacePlane::periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const
|
||||
{
|
||||
// This function assumes the other plane shares this plane's normal direction.
|
||||
|
|
@ -582,18 +582,18 @@ axis_aligned_cylinder_normal(Position r, double offset1, double offset2)
|
|||
SurfaceXCylinder::SurfaceXCylinder(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, y0, z0, r);
|
||||
read_coeffs(surf_node, id, y0, z0, radius);
|
||||
}
|
||||
|
||||
double SurfaceXCylinder::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cylinder_evaluate<1, 2>(r, y0, z0, this->r);
|
||||
return axis_aligned_cylinder_evaluate<1, 2>(r, y0, z0, radius);
|
||||
}
|
||||
|
||||
double SurfaceXCylinder::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cylinder_distance<0, 1, 2>(r, u, coincident, y0, z0,
|
||||
this->r);
|
||||
radius);
|
||||
}
|
||||
|
||||
Direction SurfaceXCylinder::normal(Position r) const
|
||||
|
|
@ -605,7 +605,7 @@ Direction SurfaceXCylinder::normal(Position r) const
|
|||
void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "x-cylinder", false);
|
||||
std::array<double, 3> coeffs {{y0, z0, r}};
|
||||
std::array<double, 3> coeffs {{y0, z0, radius}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -616,18 +616,18 @@ void SurfaceXCylinder::to_hdf5_inner(hid_t group_id) const
|
|||
SurfaceYCylinder::SurfaceYCylinder(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, z0, r);
|
||||
read_coeffs(surf_node, id, x0, z0, radius);
|
||||
}
|
||||
|
||||
double SurfaceYCylinder::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cylinder_evaluate<0, 2>(r, x0, z0, this->r);
|
||||
return axis_aligned_cylinder_evaluate<0, 2>(r, x0, z0, radius);
|
||||
}
|
||||
|
||||
double SurfaceYCylinder::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cylinder_distance<1, 0, 2>(r, u, coincident, x0, z0,
|
||||
this->r);
|
||||
radius);
|
||||
}
|
||||
|
||||
Direction SurfaceYCylinder::normal(Position r) const
|
||||
|
|
@ -638,7 +638,7 @@ Direction SurfaceYCylinder::normal(Position r) const
|
|||
void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "y-cylinder", false);
|
||||
std::array<double, 3> coeffs {{x0, z0, r}};
|
||||
std::array<double, 3> coeffs {{x0, z0, radius}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -649,18 +649,18 @@ void SurfaceYCylinder::to_hdf5_inner(hid_t group_id) const
|
|||
SurfaceZCylinder::SurfaceZCylinder(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, y0, r);
|
||||
read_coeffs(surf_node, id, x0, y0, radius);
|
||||
}
|
||||
|
||||
double SurfaceZCylinder::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cylinder_evaluate<0, 1>(r, x0, y0, this->r);
|
||||
return axis_aligned_cylinder_evaluate<0, 1>(r, x0, y0, radius);
|
||||
}
|
||||
|
||||
double SurfaceZCylinder::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cylinder_distance<2, 0, 1>(r, u, coincident, x0, y0,
|
||||
this->r);
|
||||
radius);
|
||||
}
|
||||
|
||||
Direction SurfaceZCylinder::normal(Position r) const
|
||||
|
|
@ -671,7 +671,7 @@ Direction SurfaceZCylinder::normal(Position r) const
|
|||
void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "z-cylinder", false);
|
||||
std::array<double, 3> coeffs {{x0, y0, r}};
|
||||
std::array<double, 3> coeffs {{x0, y0, radius}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -682,7 +682,7 @@ void SurfaceZCylinder::to_hdf5_inner(hid_t group_id) const
|
|||
SurfaceSphere::SurfaceSphere(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, y0, z0, r);
|
||||
read_coeffs(surf_node, id, x0, y0, z0, radius);
|
||||
}
|
||||
|
||||
double SurfaceSphere::evaluate(Position r) const
|
||||
|
|
@ -690,7 +690,7 @@ double SurfaceSphere::evaluate(Position r) const
|
|||
const double x = r.x - x0;
|
||||
const double y = r.y - y0;
|
||||
const double z = r.z - z0;
|
||||
return x*x + y*y + z*z - this->r*this->r;
|
||||
return x*x + y*y + z*z - radius*radius;
|
||||
}
|
||||
|
||||
double SurfaceSphere::distance(Position r, Direction u, bool coincident) const
|
||||
|
|
@ -699,7 +699,7 @@ double SurfaceSphere::distance(Position r, Direction u, bool coincident) const
|
|||
const double y = r.y - y0;
|
||||
const double z = r.z - z0;
|
||||
const double k = x*u.x + y*u.y + z*u.z;
|
||||
const double c = x*x + y*y + z*z - this->r*this->r;
|
||||
const double c = x*x + y*y + z*z - radius*radius;
|
||||
const double quad = k*k - c;
|
||||
|
||||
if (quad < 0.0) {
|
||||
|
|
@ -739,7 +739,7 @@ Direction SurfaceSphere::normal(Position r) const
|
|||
void SurfaceSphere::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "sphere", false);
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, r}};
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, radius}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -835,29 +835,29 @@ axis_aligned_cone_normal(Position r, double offset1, double offset2,
|
|||
SurfaceXCone::SurfaceXCone(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
|
||||
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceXCone::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_evaluate<0, 1, 2>(r, x0, y0, z0, r_sq);
|
||||
return axis_aligned_cone_evaluate<0, 1, 2>(r, x0, y0, z0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceXCone::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cone_distance<0, 1, 2>(r, u, coincident, x0, y0, z0,
|
||||
r_sq);
|
||||
radius_sq);
|
||||
}
|
||||
|
||||
Direction SurfaceXCone::normal(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_normal<0, 1, 2>(r, x0, y0, z0, r_sq);
|
||||
return axis_aligned_cone_normal<0, 1, 2>(r, x0, y0, z0, radius_sq);
|
||||
}
|
||||
|
||||
void SurfaceXCone::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "x-cone", false);
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -868,29 +868,29 @@ void SurfaceXCone::to_hdf5_inner(hid_t group_id) const
|
|||
SurfaceYCone::SurfaceYCone(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
|
||||
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceYCone::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_evaluate<1, 0, 2>(r, y0, x0, z0, r_sq);
|
||||
return axis_aligned_cone_evaluate<1, 0, 2>(r, y0, x0, z0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceYCone::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cone_distance<1, 0, 2>(r, u, coincident, y0, x0, z0,
|
||||
r_sq);
|
||||
radius_sq);
|
||||
}
|
||||
|
||||
Direction SurfaceYCone::normal(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_normal<1, 0, 2>(r, y0, x0, z0, r_sq);
|
||||
return axis_aligned_cone_normal<1, 0, 2>(r, y0, x0, z0, radius_sq);
|
||||
}
|
||||
|
||||
void SurfaceYCone::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "y-cone", false);
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -901,29 +901,29 @@ void SurfaceYCone::to_hdf5_inner(hid_t group_id) const
|
|||
SurfaceZCone::SurfaceZCone(pugi::xml_node surf_node)
|
||||
: Surface(surf_node)
|
||||
{
|
||||
read_coeffs(surf_node, id, x0, y0, z0, r_sq);
|
||||
read_coeffs(surf_node, id, x0, y0, z0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceZCone::evaluate(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_evaluate<2, 0, 1>(r, z0, x0, y0, r_sq);
|
||||
return axis_aligned_cone_evaluate<2, 0, 1>(r, z0, x0, y0, radius_sq);
|
||||
}
|
||||
|
||||
double SurfaceZCone::distance(Position r, Direction u, bool coincident) const
|
||||
{
|
||||
return axis_aligned_cone_distance<2, 0, 1>(r, u, coincident, z0, x0, y0,
|
||||
r_sq);
|
||||
radius_sq);
|
||||
}
|
||||
|
||||
Direction SurfaceZCone::normal(Position r) const
|
||||
{
|
||||
return axis_aligned_cone_normal<2, 0, 1>(r, z0, x0, y0, r_sq);
|
||||
return axis_aligned_cone_normal<2, 0, 1>(r, z0, x0, y0, radius_sq);
|
||||
}
|
||||
|
||||
void SurfaceZCone::to_hdf5_inner(hid_t group_id) const
|
||||
{
|
||||
write_string(group_id, "type", "z-cone", false);
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, r_sq}};
|
||||
std::array<double, 4> coeffs {{x0, y0, z0, radius_sq}};
|
||||
write_dataset(group_id, "coefficients", coeffs);
|
||||
}
|
||||
|
||||
|
|
@ -1023,7 +1023,7 @@ void SurfaceQuadric::to_hdf5_inner(hid_t group_id) const
|
|||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_surfaces(pugi::xml_node *node)
|
||||
read_surfaces(pugi::xml_node* node)
|
||||
{
|
||||
// Count the number of surfaces.
|
||||
for (pugi::xml_node surf_node: node->children("surface")) {n_surfaces++;}
|
||||
|
|
@ -1031,52 +1031,50 @@ read_surfaces(pugi::xml_node *node)
|
|||
fatal_error("No surfaces found in geometry.xml!");
|
||||
}
|
||||
|
||||
// Allocate the array of Surface pointers.
|
||||
surfaces_c = new Surface* [n_surfaces];
|
||||
|
||||
// Loop over XML surface elements and populate the array.
|
||||
global_surfaces.reserve(n_surfaces);
|
||||
{
|
||||
pugi::xml_node surf_node;
|
||||
int i_surf;
|
||||
for (surf_node = node->child("surface"), i_surf = 0; surf_node;
|
||||
surf_node = surf_node.next_sibling("surface"), i_surf++) {
|
||||
std::string surf_type = get_node_value(surf_node, "type");
|
||||
std::string surf_type = get_node_value(surf_node, "type", true, true);
|
||||
|
||||
if (surf_type == "x-plane") {
|
||||
surfaces_c[i_surf] = new SurfaceXPlane(surf_node);
|
||||
global_surfaces.push_back(new SurfaceXPlane(surf_node));
|
||||
|
||||
} else if (surf_type == "y-plane") {
|
||||
surfaces_c[i_surf] = new SurfaceYPlane(surf_node);
|
||||
global_surfaces.push_back(new SurfaceYPlane(surf_node));
|
||||
|
||||
} else if (surf_type == "z-plane") {
|
||||
surfaces_c[i_surf] = new SurfaceZPlane(surf_node);
|
||||
global_surfaces.push_back(new SurfaceZPlane(surf_node));
|
||||
|
||||
} else if (surf_type == "plane") {
|
||||
surfaces_c[i_surf] = new SurfacePlane(surf_node);
|
||||
global_surfaces.push_back(new SurfacePlane(surf_node));
|
||||
|
||||
} else if (surf_type == "x-cylinder") {
|
||||
surfaces_c[i_surf] = new SurfaceXCylinder(surf_node);
|
||||
global_surfaces.push_back(new SurfaceXCylinder(surf_node));
|
||||
|
||||
} else if (surf_type == "y-cylinder") {
|
||||
surfaces_c[i_surf] = new SurfaceYCylinder(surf_node);
|
||||
global_surfaces.push_back(new SurfaceYCylinder(surf_node));
|
||||
|
||||
} else if (surf_type == "z-cylinder") {
|
||||
surfaces_c[i_surf] = new SurfaceZCylinder(surf_node);
|
||||
global_surfaces.push_back(new SurfaceZCylinder(surf_node));
|
||||
|
||||
} else if (surf_type == "sphere") {
|
||||
surfaces_c[i_surf] = new SurfaceSphere(surf_node);
|
||||
global_surfaces.push_back(new SurfaceSphere(surf_node));
|
||||
|
||||
} else if (surf_type == "x-cone") {
|
||||
surfaces_c[i_surf] = new SurfaceXCone(surf_node);
|
||||
global_surfaces.push_back(new SurfaceXCone(surf_node));
|
||||
|
||||
} else if (surf_type == "y-cone") {
|
||||
surfaces_c[i_surf] = new SurfaceYCone(surf_node);
|
||||
global_surfaces.push_back(new SurfaceYCone(surf_node));
|
||||
|
||||
} else if (surf_type == "z-cone") {
|
||||
surfaces_c[i_surf] = new SurfaceZCone(surf_node);
|
||||
global_surfaces.push_back(new SurfaceZCone(surf_node));
|
||||
|
||||
} else if (surf_type == "quadric") {
|
||||
surfaces_c[i_surf] = new SurfaceQuadric(surf_node);
|
||||
global_surfaces.push_back(new SurfaceQuadric(surf_node));
|
||||
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
|
|
@ -1088,7 +1086,7 @@ read_surfaces(pugi::xml_node *node)
|
|||
|
||||
// Fill the surface map.
|
||||
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
|
||||
int id = surfaces_c[i_surf]->id;
|
||||
int id = global_surfaces[i_surf]->id;
|
||||
auto in_map = surface_map.find(id);
|
||||
if (in_map == surface_map.end()) {
|
||||
surface_map[id] = i_surf;
|
||||
|
|
@ -1104,10 +1102,10 @@ read_surfaces(pugi::xml_node *node)
|
|||
zmin {INFTY}, zmax {-INFTY};
|
||||
int i_xmin, i_xmax, i_ymin, i_ymax, i_zmin, i_zmax;
|
||||
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
|
||||
if (surfaces_c[i_surf]->bc == BC_PERIODIC) {
|
||||
if (global_surfaces[i_surf]->bc == BC_PERIODIC) {
|
||||
// Downcast to the PeriodicSurface type.
|
||||
Surface *surf_base = surfaces_c[i_surf];
|
||||
PeriodicSurface *surf = dynamic_cast<PeriodicSurface *>(surf_base);
|
||||
Surface* surf_base = global_surfaces[i_surf];
|
||||
PeriodicSurface* surf = dynamic_cast<PeriodicSurface*>(surf_base);
|
||||
|
||||
// Make sure this surface inherits from PeriodicSurface.
|
||||
if (!surf) {
|
||||
|
|
@ -1149,14 +1147,14 @@ read_surfaces(pugi::xml_node *node)
|
|||
|
||||
// Set i_periodic for periodic BC surfaces.
|
||||
for (int i_surf = 0; i_surf < n_surfaces; i_surf++) {
|
||||
if (surfaces_c[i_surf]->bc == BC_PERIODIC) {
|
||||
if (global_surfaces[i_surf]->bc == BC_PERIODIC) {
|
||||
// Downcast to the PeriodicSurface type.
|
||||
Surface *surf_base = surfaces_c[i_surf];
|
||||
PeriodicSurface *surf = dynamic_cast<PeriodicSurface *>(surf_base);
|
||||
Surface* surf_base = global_surfaces[i_surf];
|
||||
PeriodicSurface* surf = dynamic_cast<PeriodicSurface*>(surf_base);
|
||||
|
||||
// Also try downcasting to the SurfacePlane type (which must be handled
|
||||
// differently).
|
||||
SurfacePlane *surf_p = dynamic_cast<SurfacePlane *>(surf);
|
||||
SurfacePlane* surf_p = dynamic_cast<SurfacePlane*>(surf);
|
||||
|
||||
if (!surf_p) {
|
||||
// This is not a SurfacePlane.
|
||||
|
|
@ -1198,7 +1196,7 @@ read_surfaces(pugi::xml_node *node)
|
|||
}
|
||||
|
||||
// Make sure the opposite surface is also periodic.
|
||||
if (surfaces_c[surf->i_periodic]->bc != BC_PERIODIC) {
|
||||
if (global_surfaces[surf->i_periodic]->bc != BC_PERIODIC) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Could not find matching surface for periodic boundary "
|
||||
"condition on surface " << surf->id;
|
||||
|
|
@ -1213,13 +1211,13 @@ read_surfaces(pugi::xml_node *node)
|
|||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
Surface* surface_pointer(int surf_ind) {return surfaces_c[surf_ind];}
|
||||
Surface* surface_pointer(int surf_ind) {return global_surfaces[surf_ind];}
|
||||
|
||||
int surface_id(Surface *surf) {return surf->id;}
|
||||
int surface_id(Surface* surf) {return surf->id;}
|
||||
|
||||
int surface_bc(Surface *surf) {return surf->bc;}
|
||||
int surface_bc(Surface* surf) {return surf->bc;}
|
||||
|
||||
void surface_reflect(Surface *surf, double xyz[3], double uvw[3])
|
||||
void surface_reflect(Surface* surf, double xyz[3], double uvw[3])
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
|
|
@ -1230,7 +1228,7 @@ extern "C" {
|
|||
uvw[2] = u.z;
|
||||
}
|
||||
|
||||
void surface_normal(Surface *surf, double xyz[3], double uvw[3])
|
||||
void surface_normal(Surface* surf, double xyz[3], double uvw[3])
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u = surf->normal(r);
|
||||
|
|
@ -1239,12 +1237,12 @@ extern "C" {
|
|||
uvw[2] = u.z;
|
||||
}
|
||||
|
||||
void surface_to_hdf5(Surface *surf, hid_t group) {surf->to_hdf5(group);}
|
||||
void surface_to_hdf5(Surface* surf, hid_t group) {surf->to_hdf5(group);}
|
||||
|
||||
int surface_i_periodic(PeriodicSurface *surf) {return surf->i_periodic;}
|
||||
int surface_i_periodic(PeriodicSurface* surf) {return surf->i_periodic;}
|
||||
|
||||
bool
|
||||
surface_periodic(PeriodicSurface *surf, PeriodicSurface *other, double xyz[3],
|
||||
surface_periodic(PeriodicSurface* surf, PeriodicSurface* other, double xyz[3],
|
||||
double uvw[3])
|
||||
{
|
||||
Position r {xyz};
|
||||
|
|
@ -1264,9 +1262,8 @@ extern "C" {
|
|||
|
||||
void free_memory_surfaces_c()
|
||||
{
|
||||
for (int i = 0; i < n_surfaces; i++) {delete surfaces_c[i];}
|
||||
delete surfaces_c;
|
||||
surfaces_c = nullptr;
|
||||
for (Surface* surf : global_surfaces) {delete surf;}
|
||||
global_surfaces.clear();
|
||||
n_surfaces = 0;
|
||||
surface_map.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <map>
|
||||
#include <limits> // For numeric_limits
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "pugixml.hpp"
|
||||
|
|
@ -18,6 +19,7 @@ namespace openmc {
|
|||
// Module constant declarations (defined in .cpp)
|
||||
//==============================================================================
|
||||
|
||||
// TODO: Convert to enum
|
||||
extern "C" const int BC_TRANSMIT;
|
||||
extern "C" const int BC_VACUUM;
|
||||
extern "C" const int BC_REFLECT;
|
||||
|
|
@ -30,7 +32,7 @@ extern "C" const int BC_PERIODIC;
|
|||
extern "C" int32_t n_surfaces;
|
||||
|
||||
class Surface;
|
||||
extern Surface **surfaces_c;
|
||||
extern std::vector<Surface*> global_surfaces;
|
||||
|
||||
extern std::map<int, int> surface_map;
|
||||
|
||||
|
|
@ -74,10 +76,9 @@ public:
|
|||
bool sense(Position r, Direction u) const;
|
||||
|
||||
//! Determine the direction of a ray reflected from the surface.
|
||||
//! \param r The point at which the ray is incident.
|
||||
//! \param u A direction. This is both an input and an output parameter.
|
||||
//! It specifies the icident direction on input and the reflected direction
|
||||
//! on output.
|
||||
//! \param[in] r The point at which the ray is incident.
|
||||
//! \param[in] u Incident direction of the ray
|
||||
//! \return Outgoing direction of the ray
|
||||
Direction reflect(Position r, Direction u) const;
|
||||
|
||||
//! Evaluate the equation describing the surface.
|
||||
|
|
@ -131,7 +132,7 @@ public:
|
|||
//! periodicity.
|
||||
//! \return true if this surface and its partner make a rotationally-periodic
|
||||
//! boundary condition.
|
||||
virtual bool periodic_translate(const PeriodicSurface *other, Position& r,
|
||||
virtual bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const = 0;
|
||||
|
||||
//! Get the bounding box for this surface.
|
||||
|
|
@ -153,8 +154,8 @@ public:
|
|||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
|
||||
const;
|
||||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
};
|
||||
|
||||
|
|
@ -173,8 +174,8 @@ public:
|
|||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
|
||||
const;
|
||||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
};
|
||||
|
||||
|
|
@ -193,8 +194,8 @@ public:
|
|||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
|
||||
const;
|
||||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
};
|
||||
|
||||
|
|
@ -213,8 +214,8 @@ public:
|
|||
double distance(Position r, Direction u, bool coincident) const;
|
||||
Direction normal(Position r) const;
|
||||
void to_hdf5_inner(hid_t group_id) const;
|
||||
bool periodic_translate(const PeriodicSurface *other, Position& r, Direction& u)
|
||||
const;
|
||||
bool periodic_translate(const PeriodicSurface* other, Position& r,
|
||||
Direction& u) const;
|
||||
BoundingBox bounding_box() const;
|
||||
};
|
||||
|
||||
|
|
@ -227,7 +228,7 @@ public:
|
|||
|
||||
class SurfaceXCylinder : public Surface
|
||||
{
|
||||
double y0, z0, r;
|
||||
double y0, z0, radius;
|
||||
public:
|
||||
explicit SurfaceXCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -245,7 +246,7 @@ public:
|
|||
|
||||
class SurfaceYCylinder : public Surface
|
||||
{
|
||||
double x0, z0, r;
|
||||
double x0, z0, radius;
|
||||
public:
|
||||
explicit SurfaceYCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -263,7 +264,7 @@ public:
|
|||
|
||||
class SurfaceZCylinder : public Surface
|
||||
{
|
||||
double x0, y0, r;
|
||||
double x0, y0, radius;
|
||||
public:
|
||||
explicit SurfaceZCylinder(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -281,7 +282,7 @@ public:
|
|||
|
||||
class SurfaceSphere : public Surface
|
||||
{
|
||||
double x0, y0, z0, r;
|
||||
double x0, y0, z0, radius;
|
||||
public:
|
||||
explicit SurfaceSphere(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -299,7 +300,7 @@ public:
|
|||
|
||||
class SurfaceXCone : public Surface
|
||||
{
|
||||
double x0, y0, z0, r_sq;
|
||||
double x0, y0, z0, radius_sq;
|
||||
public:
|
||||
explicit SurfaceXCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -317,7 +318,7 @@ public:
|
|||
|
||||
class SurfaceYCone : public Surface
|
||||
{
|
||||
double x0, y0, z0, r_sq;
|
||||
double x0, y0, z0, radius_sq;
|
||||
public:
|
||||
explicit SurfaceYCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -335,7 +336,7 @@ public:
|
|||
|
||||
class SurfaceZCone : public Surface
|
||||
{
|
||||
double x0, y0, z0, r_sq;
|
||||
double x0, y0, z0, radius_sq;
|
||||
public:
|
||||
explicit SurfaceZCone(pugi::xml_node surf_node);
|
||||
double evaluate(Position r) const;
|
||||
|
|
@ -368,16 +369,16 @@ public:
|
|||
|
||||
extern "C" {
|
||||
Surface* surface_pointer(int surf_ind);
|
||||
int surface_id(Surface *surf);
|
||||
int surface_bc(Surface *surf);
|
||||
bool surface_sense(Surface *surf, double xyz[3], double uvw[3]);
|
||||
void surface_reflect(Surface *surf, double xyz[3], double uvw[3]);
|
||||
double surface_distance(Surface *surf, double xyz[3], double uvw[3],
|
||||
int surface_id(Surface* surf);
|
||||
int surface_bc(Surface* surf);
|
||||
bool surface_sense(Surface* surf, double xyz[3], double uvw[3]);
|
||||
void surface_reflect(Surface* surf, double xyz[3], double uvw[3]);
|
||||
double surface_distance(Surface* surf, double xyz[3], double uvw[3],
|
||||
bool coincident);
|
||||
void surface_normal(Surface *surf, double xyz[3], double uvw[3]);
|
||||
void surface_to_hdf5(Surface *surf, hid_t group);
|
||||
int surface_i_periodic(PeriodicSurface *surf);
|
||||
bool surface_periodic(PeriodicSurface *surf, PeriodicSurface *other,
|
||||
void surface_normal(Surface* surf, double xyz[3], double uvw[3]);
|
||||
void surface_to_hdf5(Surface* surf, hid_t group);
|
||||
int surface_i_periodic(PeriodicSurface* surf);
|
||||
bool surface_periodic(PeriodicSurface* surf, PeriodicSurface* other,
|
||||
double xyz[3], double uvw[3]);
|
||||
void free_memory_surfaces_c();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,13 +8,12 @@ module surface_header
|
|||
implicit none
|
||||
|
||||
interface
|
||||
pure function surface_pointer_c(surf_ind) &
|
||||
bind(C, name='surface_pointer') result(ptr)
|
||||
pure function surface_pointer(surf_ind) bind(C) result(ptr)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
integer(C_INT), intent(in), value :: surf_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function surface_pointer_c
|
||||
end function surface_pointer
|
||||
|
||||
pure function surface_id_c(surf_ptr) bind(C, name='surface_id') result(id)
|
||||
use ISO_C_BINDING
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ contains
|
|||
integer :: k ! loop index for bank sites
|
||||
integer :: d_bin ! delayed group bin index
|
||||
integer :: dg_filter ! index of delayed group filter
|
||||
integer :: threshold ! threshold energy index
|
||||
real(8) :: yield ! delayed neutron yield
|
||||
real(8) :: atom_density_ ! atom/b-cm
|
||||
real(8) :: f ! interpolation factor
|
||||
|
|
@ -227,7 +228,7 @@ contains
|
|||
! Get yield and apply to score
|
||||
associate (rxn => nuclides(p % event_nuclide) % reactions(m))
|
||||
score = p % last_wgt * flux &
|
||||
* rxn % products(1) % yield % evaluate(E)
|
||||
* rxn % product_yield(1, E)
|
||||
end associate
|
||||
end if
|
||||
|
||||
|
|
@ -633,7 +634,7 @@ contains
|
|||
score = p % absorb_wgt * yield * &
|
||||
micro_xs(p % event_nuclide) % fission &
|
||||
/ micro_xs(p % event_nuclide) % absorption &
|
||||
* rxn % products(1 + d) % decay_rate * flux
|
||||
* rxn % product_decay_rate(1 + d) * flux
|
||||
end associate
|
||||
|
||||
! Tally to bin
|
||||
|
|
@ -657,9 +658,8 @@ contains
|
|||
! rxn % products array to be exceeded. Hence, we use the size
|
||||
! of this array and not the MAX_DELAYED_GROUPS constant for
|
||||
! this loop.
|
||||
do d = 1, size(rxn % products) - 2
|
||||
|
||||
score = score + rxn % products(1 + d) % decay_rate * &
|
||||
do d = 1, rxn % products_size() - 2
|
||||
score = score + rxn % product_decay_rate(1 + d) * &
|
||||
p % absorb_wgt &
|
||||
* micro_xs(p % event_nuclide) % fission &
|
||||
* nuclides(p % event_nuclide) % &
|
||||
|
|
@ -699,7 +699,7 @@ contains
|
|||
|
||||
! determine score based on bank site weight and keff.
|
||||
score = score + keff * fission_bank(n_bank - p % n_bank + k) &
|
||||
% wgt * rxn % products(1 + g) % decay_rate * flux
|
||||
% wgt * rxn % product_decay_rate(1 + g) * flux
|
||||
end associate
|
||||
|
||||
! if the delayed group filter is present, tally to corresponding
|
||||
|
|
@ -755,7 +755,7 @@ contains
|
|||
|
||||
! Compute the score and tally to bin
|
||||
score = micro_xs(i_nuclide) % fission * yield * flux * &
|
||||
atom_density * rxn % products(1 + d) % decay_rate
|
||||
atom_density * rxn % product_decay_rate(1 + d)
|
||||
end associate
|
||||
|
||||
! Tally to bin
|
||||
|
|
@ -778,11 +778,10 @@ contains
|
|||
! groups since this could cause the range of the rxn % products
|
||||
! array to be exceeded. Hence, we use the size of this array
|
||||
! and not the MAX_DELAYED_GROUPS constant for this loop.
|
||||
do d = 1, size(rxn % products) - 2
|
||||
|
||||
do d = 1, rxn % products_size() - 2
|
||||
score = score + micro_xs(i_nuclide) % fission * flux * &
|
||||
nuclides(i_nuclide) % nu(E, EMISSION_DELAYED) * &
|
||||
atom_density * rxn % products(1 + d) % decay_rate
|
||||
atom_density * rxn % product_decay_rate(1 + d)
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
|
|
@ -824,7 +823,7 @@ contains
|
|||
! Compute the score
|
||||
score = micro_xs(i_nuc) % fission * yield * flux * &
|
||||
atom_density_ &
|
||||
* rxn % products(1 + d) % decay_rate
|
||||
* rxn % product_decay_rate(1 + d)
|
||||
end associate
|
||||
|
||||
! Tally to bin
|
||||
|
|
@ -860,13 +859,13 @@ contains
|
|||
! rxn % products array to be exceeded. Hence, we use the
|
||||
! size of this array and not the MAX_DELAYED_GROUPS
|
||||
! constant for this loop.
|
||||
do d = 1, size(rxn % products) - 2
|
||||
do d = 1, rxn % products_size() - 2
|
||||
|
||||
! Accumulate the contribution from each nuclide
|
||||
score = score + micro_xs(i_nuc) % fission &
|
||||
* nuclides(i_nuc) % nu(E, EMISSION_DELAYED) &
|
||||
* atom_density_ * flux &
|
||||
* rxn % products(1 + d) % decay_rate
|
||||
* rxn % product_decay_rate(1 + d)
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
|
|
@ -1132,12 +1131,12 @@ contains
|
|||
i_energy = micro_xs(i_nuclide) % index_grid
|
||||
f = micro_xs(i_nuclide) % interp_factor
|
||||
|
||||
associate (xs => nuclides(i_nuclide) % reactions(m) &
|
||||
% xs(i_temp))
|
||||
if (i_energy >= xs % threshold) then
|
||||
score = ((ONE - f) * xs % value(i_energy - &
|
||||
xs % threshold + 1) + f * xs % value(i_energy - &
|
||||
xs % threshold + 2)) * atom_density * flux
|
||||
associate (rx => nuclides(i_nuclide) % reactions(m))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_energy >= threshold) then
|
||||
score = ((ONE - f) * rx % xs(i_temp, i_energy - &
|
||||
threshold + 1) + f * rx % xs(i_temp, i_energy - &
|
||||
threshold + 2)) * atom_density * flux
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
|
|
@ -1165,12 +1164,12 @@ contains
|
|||
i_energy = micro_xs(i_nuc) % index_grid
|
||||
f = micro_xs(i_nuc) % interp_factor
|
||||
|
||||
associate (xs => nuclides(i_nuc) % reactions(m) &
|
||||
% xs(i_temp))
|
||||
if (i_energy >= xs % threshold) then
|
||||
score = score + ((ONE - f) * xs % value(i_energy - &
|
||||
xs % threshold + 1) + f * xs % value(i_energy - &
|
||||
xs % threshold + 2)) * atom_density_ * flux
|
||||
associate (rx => nuclides(i_nuc) % reactions(m))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_energy >= threshold) then
|
||||
score = score + ((ONE - f) * rx % xs(i_temp, i_energy - &
|
||||
threshold + 1) + f * rx % xs(i_temp, i_energy - &
|
||||
threshold + 2)) * atom_density_ * flux
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
|
|
@ -3044,7 +3043,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id == deriv % diff_material) then
|
||||
if (materials(p % material) % id() == deriv % diff_material) then
|
||||
score = score * (flux_deriv + ONE &
|
||||
/ materials(p % material) % density_gpcc)
|
||||
else
|
||||
|
|
@ -3065,7 +3064,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id == deriv % diff_material) then
|
||||
if (materials(p % material) % id() == deriv % diff_material) then
|
||||
score = score * (flux_deriv + ONE &
|
||||
/ materials(p % material) % density_gpcc)
|
||||
else
|
||||
|
|
@ -3107,7 +3106,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id == deriv % diff_material &
|
||||
if (materials(p % material) % id() == deriv % diff_material &
|
||||
.and. p % event_nuclide == deriv % diff_nuclide) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
|
|
@ -3129,7 +3128,7 @@ contains
|
|||
|
||||
case (ESTIMATOR_COLLISION)
|
||||
scoring_diff_nuclide = &
|
||||
(materials(p % material) % id == deriv % diff_material) &
|
||||
(materials(p % material) % id() == deriv % diff_material) &
|
||||
.and. (i_nuclide == deriv % diff_nuclide)
|
||||
|
||||
select case (score_bin)
|
||||
|
|
@ -3139,7 +3138,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % total /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % total &
|
||||
|
|
@ -3153,7 +3152,7 @@ contains
|
|||
|
||||
case (SCORE_SCATTER)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % total - material_xs % absorption /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ (micro_xs(deriv % diff_nuclide) % total &
|
||||
|
|
@ -3169,7 +3168,7 @@ contains
|
|||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % absorption /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % absorption &
|
||||
|
|
@ -3183,7 +3182,7 @@ contains
|
|||
|
||||
case (SCORE_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % fission /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % fission &
|
||||
|
|
@ -3197,7 +3196,7 @@ contains
|
|||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % nu_fission /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % nu_fission &
|
||||
|
|
@ -3243,7 +3242,7 @@ contains
|
|||
score = score * flux_deriv
|
||||
|
||||
case (SCORE_TOTAL)
|
||||
if (materials(p % material) % id == deriv % diff_material .and. &
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % total > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
|
|
@ -3268,7 +3267,7 @@ contains
|
|||
end if
|
||||
|
||||
case (SCORE_SCATTER)
|
||||
if (materials(p % material) % id == deriv % diff_material .and. &
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
(micro_xs(p % event_nuclide) % total &
|
||||
- micro_xs(p % event_nuclide) % absorption) > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3296,7 +3295,7 @@ contains
|
|||
end if
|
||||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (materials(p % material) % id == deriv % diff_material .and. &
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % absorption > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
|
|
@ -3321,7 +3320,7 @@ contains
|
|||
end if
|
||||
|
||||
case (SCORE_FISSION)
|
||||
if (materials(p % material) % id == deriv % diff_material .and. &
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % fission > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
|
|
@ -3346,7 +3345,7 @@ contains
|
|||
end if
|
||||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id == deriv % diff_material .and. &
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % nu_fission > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
|
|
@ -3386,7 +3385,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % total > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3405,7 +3404,7 @@ contains
|
|||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % total)
|
||||
else if (materials(p % material) % id == deriv % diff_material &
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
.and. material_xs % total > ZERO) then
|
||||
dsig_t = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -3424,7 +3423,7 @@ contains
|
|||
|
||||
case (SCORE_SCATTER)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
(material_xs % total - material_xs % absorption) > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3445,7 +3444,7 @@ contains
|
|||
end associate
|
||||
score = score * (flux_deriv + cum_dsig &
|
||||
/ (material_xs % total - material_xs % absorption))
|
||||
else if ( materials(p % material) % id == deriv % diff_material &
|
||||
else if ( materials(p % material) % id() == deriv % diff_material &
|
||||
.and. (material_xs % total - material_xs % absorption) > ZERO)&
|
||||
then
|
||||
dsig_t = ZERO
|
||||
|
|
@ -3467,7 +3466,7 @@ contains
|
|||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % absorption > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3486,7 +3485,7 @@ contains
|
|||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % absorption)
|
||||
else if (materials(p % material) % id == deriv % diff_material &
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
.and. material_xs % absorption > ZERO) then
|
||||
dsig_a = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -3505,7 +3504,7 @@ contains
|
|||
|
||||
case (SCORE_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % fission > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3524,7 +3523,7 @@ contains
|
|||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % fission)
|
||||
else if (materials(p % material) % id == deriv % diff_material &
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
.and. material_xs % fission > ZERO) then
|
||||
dsig_f = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -3543,7 +3542,7 @@ contains
|
|||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id == deriv % diff_material .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_xs % nu_fission > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
|
|
@ -3564,7 +3563,7 @@ contains
|
|||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % nu_fission)
|
||||
else if (materials(p % material) % id == deriv % diff_material &
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
.and. material_xs % nu_fission > ZERO) then
|
||||
dsig_f = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -3615,7 +3614,7 @@ contains
|
|||
|
||||
case (DIFF_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material) then
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
! phi is proportional to e^(-Sigma_tot * dist)
|
||||
! (1 / phi) * (d_phi / d_rho) = - (d_Sigma_tot / d_rho) * dist
|
||||
! (1 / phi) * (d_phi / d_rho) = - Sigma_tot / rho * dist
|
||||
|
|
@ -3626,7 +3625,7 @@ contains
|
|||
|
||||
case (DIFF_NUCLIDE_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material) then
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
! phi is proportional to e^(-Sigma_tot * dist)
|
||||
! (1 / phi) * (d_phi / d_N) = - (d_Sigma_tot / d_N) * dist
|
||||
! (1 / phi) * (d_phi / d_N) = - sigma_tot * dist
|
||||
|
|
@ -3637,7 +3636,7 @@ contains
|
|||
|
||||
case (DIFF_TEMPERATURE)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material) then
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
do l=1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
if (nuc % mp_present .and. &
|
||||
|
|
@ -3691,7 +3690,7 @@ contains
|
|||
|
||||
case (DIFF_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material) then
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
! phi is proportional to Sigma_s
|
||||
! (1 / phi) * (d_phi / d_rho) = (d_Sigma_s / d_rho) / Sigma_s
|
||||
! (1 / phi) * (d_phi / d_rho) = 1 / rho
|
||||
|
|
@ -3702,7 +3701,7 @@ contains
|
|||
|
||||
case (DIFF_NUCLIDE_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material &
|
||||
if (mat % id() == deriv % diff_material &
|
||||
.and. p % event_nuclide == deriv % diff_nuclide) then
|
||||
! Find the index in this material for the diff_nuclide.
|
||||
do j = 1, mat % n_nuclides
|
||||
|
|
@ -3723,7 +3722,7 @@ contains
|
|||
|
||||
case (DIFF_TEMPERATURE)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id == deriv % diff_material) then
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
do l=1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
if (mat % nuclide(l) == p % event_nuclide .and. &
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ contains
|
|||
type_ = 'zernike'
|
||||
type is (ParticleFilter)
|
||||
type_ = 'particle'
|
||||
type is (ZernikeRadialFilter)
|
||||
type_ = 'zernikeradial'
|
||||
end select
|
||||
|
||||
! Convert Fortran string to null-terminated C string. We assume the
|
||||
|
|
@ -164,6 +166,8 @@ contains
|
|||
allocate(UniverseFilter :: filters(index) % obj)
|
||||
case ('zernike')
|
||||
allocate(ZernikeFilter :: filters(index) % obj)
|
||||
case ('zernikeradial')
|
||||
allocate(ZernikeRadialFilter :: filters(index) % obj)
|
||||
case default
|
||||
err = E_UNASSIGNED
|
||||
call set_errmsg("Unknown filter type: " // trim(type_))
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ contains
|
|||
|
||||
allocate(material_ids(size(this % materials)))
|
||||
do i = 1, size(this % materials)
|
||||
material_ids(i) = materials(this % materials(i)) % id
|
||||
material_ids(i) = materials(this % materials(i)) % id()
|
||||
end do
|
||||
call write_dataset(filter_group, "bins", material_ids)
|
||||
end subroutine to_statepoint_material
|
||||
|
|
@ -110,7 +110,7 @@ contains
|
|||
integer, intent(in) :: bin
|
||||
character(MAX_LINE_LEN) :: label
|
||||
|
||||
label = "Material " // to_str(materials(this % materials(bin)) % id)
|
||||
label = "Material " // to_str(materials(this % materials(bin)) % id())
|
||||
end function text_label_material
|
||||
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ module tally_filter_zernike
|
|||
use constants
|
||||
use error
|
||||
use hdf5_interface
|
||||
use math, only: calc_zn
|
||||
use math, only: calc_zn, calc_zn_rad
|
||||
use particle_header, only: Particle
|
||||
use string, only: to_str
|
||||
use tally_filter_header
|
||||
|
|
@ -13,6 +13,10 @@ module tally_filter_zernike
|
|||
|
||||
implicit none
|
||||
private
|
||||
public :: openmc_zernike_filter_get_order
|
||||
public :: openmc_zernike_filter_get_params
|
||||
public :: openmc_zernike_filter_set_order
|
||||
public :: openmc_zernike_filter_set_params
|
||||
|
||||
!===============================================================================
|
||||
! ZERNIKEFILTER gives Zernike polynomial moments of a particle's position
|
||||
|
|
@ -24,19 +28,43 @@ module tally_filter_zernike
|
|||
real(8) :: y
|
||||
real(8) :: r
|
||||
contains
|
||||
procedure :: from_xml
|
||||
procedure :: get_all_bins
|
||||
procedure :: to_statepoint
|
||||
procedure :: text_label
|
||||
procedure :: calc_n_bins => calc_n_bins_zn
|
||||
procedure :: from_xml => from_xml_zn
|
||||
procedure :: get_all_bins => get_all_bins_zn
|
||||
procedure :: to_statepoint => to_statepoint_zn
|
||||
procedure :: text_label => text_label_zn
|
||||
end type ZernikeFilter
|
||||
|
||||
!===============================================================================
|
||||
! ZERNIKERADIALFILTER gives even order radial Zernike polynomial moments of a
|
||||
! particle's position
|
||||
!===============================================================================
|
||||
|
||||
type, public, extends(ZernikeFilter) :: ZernikeRadialFilter
|
||||
contains
|
||||
procedure :: calc_n_bins => calc_n_bins_zn_rad
|
||||
! Inherit from_xml from ZernikeFilter
|
||||
procedure :: get_all_bins => get_all_bins_zn_rad
|
||||
procedure :: to_statepoint => to_statepoint_zn_rad
|
||||
procedure :: text_label => text_label_zn_rad
|
||||
end type ZernikeRadialFilter
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! ZernikeFilter methods
|
||||
!===============================================================================
|
||||
|
||||
subroutine from_xml(this, node)
|
||||
function calc_n_bins_zn(this) result(n_bins)
|
||||
class(ZernikeFilter), intent(in) :: this
|
||||
integer :: n
|
||||
integer :: n_bins
|
||||
|
||||
n = this % order
|
||||
n_bins = ((n+1) * (n+2))/2
|
||||
end function calc_n_bins_zn
|
||||
|
||||
subroutine from_xml_zn(this, node)
|
||||
class(ZernikeFilter), intent(inout) :: this
|
||||
type(XMLNode), intent(in) :: node
|
||||
|
||||
|
|
@ -50,10 +78,10 @@ contains
|
|||
! Get specified order
|
||||
call get_node_value(node, "order", n)
|
||||
this % order = n
|
||||
this % n_bins = ((n + 1)*(n + 2))/2
|
||||
end subroutine from_xml
|
||||
this % n_bins = this % calc_n_bins()
|
||||
end subroutine from_xml_zn
|
||||
|
||||
subroutine get_all_bins(this, p, estimator, match)
|
||||
subroutine get_all_bins_zn(this, p, estimator, match)
|
||||
class(ZernikeFilter), intent(in) :: this
|
||||
type(Particle), intent(in) :: p
|
||||
integer, intent(in) :: estimator
|
||||
|
|
@ -67,18 +95,20 @@ contains
|
|||
x = p % coord(1) % xyz(1) - this % x
|
||||
y = p % coord(1) % xyz(2) - this % y
|
||||
r = sqrt(x*x + y*y)/this % r
|
||||
theta = atan2(y, x)
|
||||
if (r <= ONE) then
|
||||
theta = atan2(y, x)
|
||||
|
||||
! Get moments for Zernike polynomial orders 0..n
|
||||
call calc_zn(this % order, r, theta, zn)
|
||||
|
||||
! Get moments for Zernike polynomial orders 0..n
|
||||
call calc_zn(this % order, r, theta, zn)
|
||||
do i = 1, this % n_bins
|
||||
call match % bins % push_back(i)
|
||||
call match % weights % push_back(zn(i))
|
||||
end do
|
||||
endif
|
||||
end subroutine get_all_bins_zn
|
||||
|
||||
do i = 1, this % n_bins
|
||||
call match % bins % push_back(i)
|
||||
call match % weights % push_back(zn(i))
|
||||
end do
|
||||
end subroutine get_all_bins
|
||||
|
||||
subroutine to_statepoint(this, filter_group)
|
||||
subroutine to_statepoint_zn(this, filter_group)
|
||||
class(ZernikeFilter), intent(in) :: this
|
||||
integer(HID_T), intent(in) :: filter_group
|
||||
|
||||
|
|
@ -88,9 +118,9 @@ contains
|
|||
call write_dataset(filter_group, "x", this % x)
|
||||
call write_dataset(filter_group, "y", this % y)
|
||||
call write_dataset(filter_group, "r", this % r)
|
||||
end subroutine to_statepoint
|
||||
end subroutine to_statepoint_zn
|
||||
|
||||
function text_label(this, bin) result(label)
|
||||
function text_label_zn(this, bin) result(label)
|
||||
class(ZernikeFilter), intent(in) :: this
|
||||
integer, intent(in) :: bin
|
||||
character(MAX_LINE_LEN) :: label
|
||||
|
|
@ -108,7 +138,66 @@ contains
|
|||
exit
|
||||
end if
|
||||
end do
|
||||
end function text_label
|
||||
end function text_label_zn
|
||||
|
||||
!===============================================================================
|
||||
! ZernikeRadialFilter methods
|
||||
!===============================================================================
|
||||
|
||||
function calc_n_bins_zn_rad(this) result(num_n_bins)
|
||||
class(ZernikeRadialFilter), intent(in) :: this
|
||||
integer :: n
|
||||
integer :: num_n_bins
|
||||
|
||||
n = this % order
|
||||
num_n_bins = n/2 + 1
|
||||
end function calc_n_bins_zn_rad
|
||||
|
||||
subroutine get_all_bins_zn_rad(this, p, estimator, match)
|
||||
class(ZernikeRadialFilter), intent(in) :: this
|
||||
type(Particle), intent(in) :: p
|
||||
integer, intent(in) :: estimator
|
||||
type(TallyFilterMatch), intent(inout) :: match
|
||||
|
||||
integer :: i
|
||||
real(8) :: x, y, r
|
||||
real(C_DOUBLE) :: zn_rad(this % n_bins)
|
||||
|
||||
! Determine normalized (r,theta) positions
|
||||
x = p % coord(1) % xyz(1) - this % x
|
||||
y = p % coord(1) % xyz(2) - this % y
|
||||
r = sqrt(x*x + y*y)/this % r
|
||||
if (r <= ONE) then
|
||||
|
||||
! Get moments for even order Zernike polynomial orders 0..n
|
||||
call calc_zn_rad(this % order, r, zn_rad)
|
||||
|
||||
do i = 1, this % n_bins
|
||||
call match % bins % push_back(i)
|
||||
call match % weights % push_back(zn_rad(i))
|
||||
end do
|
||||
endif
|
||||
end subroutine get_all_bins_zn_rad
|
||||
|
||||
subroutine to_statepoint_zn_rad(this, filter_group)
|
||||
class(ZernikeRadialFilter), intent(in) :: this
|
||||
integer(HID_T), intent(in) :: filter_group
|
||||
|
||||
call write_dataset(filter_group, "type", "zernikeradial")
|
||||
call write_dataset(filter_group, "n_bins", this % n_bins)
|
||||
call write_dataset(filter_group, "order", this % order)
|
||||
call write_dataset(filter_group, "x", this % x)
|
||||
call write_dataset(filter_group, "y", this % y)
|
||||
call write_dataset(filter_group, "r", this % r)
|
||||
end subroutine to_statepoint_zn_rad
|
||||
|
||||
function text_label_zn_rad(this, bin) result(label)
|
||||
class(ZernikeRadialFilter), intent(in) :: this
|
||||
integer, intent(in) :: bin
|
||||
character(MAX_LINE_LEN) :: label
|
||||
|
||||
label = "Zernike expansion, Z" // trim(to_str(2*(bin-1))) // ",0"
|
||||
end function text_label_zn_rad
|
||||
|
||||
!===============================================================================
|
||||
! C API FUNCTIONS
|
||||
|
|
@ -125,6 +214,8 @@ contains
|
|||
select type (f => filters(index) % obj)
|
||||
type is (ZernikeFilter)
|
||||
order = f % order
|
||||
type is (ZernikeRadialFilter)
|
||||
order = f % order
|
||||
class default
|
||||
err = E_INVALID_TYPE
|
||||
call set_errmsg("Not a Zernike filter.")
|
||||
|
|
@ -144,7 +235,7 @@ contains
|
|||
err = verify_filter(index)
|
||||
if (err == 0) then
|
||||
select type (f => filters(index) % obj)
|
||||
type is (ZernikeFilter)
|
||||
class is (ZernikeFilter)
|
||||
x = f % x
|
||||
y = f % y
|
||||
r = f % r
|
||||
|
|
@ -168,6 +259,9 @@ contains
|
|||
type is (ZernikeFilter)
|
||||
f % order = order
|
||||
f % n_bins = ((order + 1)*(order + 2))/2
|
||||
type is (ZernikeRadialFilter)
|
||||
f % order = order
|
||||
f % n_bins = order/2 + 1
|
||||
class default
|
||||
err = E_INVALID_TYPE
|
||||
call set_errmsg("Not a Zernike filter.")
|
||||
|
|
@ -187,7 +281,7 @@ contains
|
|||
err = verify_filter(index)
|
||||
if (err == 0) then
|
||||
select type (f => filters(index) % obj)
|
||||
type is (ZernikeFilter)
|
||||
class is (ZernikeFilter)
|
||||
if (present(x)) f % x = x
|
||||
if (present(y)) f % y = y
|
||||
if (present(r)) f % r = r
|
||||
|
|
|
|||
|
|
@ -340,6 +340,9 @@ contains
|
|||
type is (ZernikeFilter)
|
||||
j = FILTER_ZERNIKE
|
||||
this % estimator = ESTIMATOR_COLLISION
|
||||
type is (ZernikeRadialFilter)
|
||||
j = FILTER_ZERNIKE_RADIAL
|
||||
this % estimator = ESTIMATOR_COLLISION
|
||||
type is (ParticleFilter)
|
||||
j = FILTER_PARTICLE
|
||||
end select
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ contains
|
|||
i_material = p % material
|
||||
if (i_material /= MATERIAL_VOID) then
|
||||
do i_domain = 1, size(this % domain_id)
|
||||
if (materials(i_material) % id == this % domain_id(i_domain)) then
|
||||
if (materials(i_material) % id() == this % domain_id(i_domain)) then
|
||||
call check_hit(i_domain, i_material, indices, hits, n_mat)
|
||||
end if
|
||||
end do
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "xml_interface.h"
|
||||
|
||||
#include <algorithm> // for std::transform
|
||||
#include <algorithm> // for transform
|
||||
#include <sstream>
|
||||
|
||||
#include "error.h"
|
||||
|
|
@ -9,10 +9,11 @@
|
|||
namespace openmc {
|
||||
|
||||
std::string
|
||||
get_node_value(pugi::xml_node node, const char *name)
|
||||
get_node_value(pugi::xml_node node, const char* name, bool lowercase,
|
||||
bool strip)
|
||||
{
|
||||
// Search for either an attribute or child tag and get the data as a char*.
|
||||
const pugi::char_t *value_char;
|
||||
const pugi::char_t* value_char;
|
||||
if (node.attribute(name)) {
|
||||
value_char = node.attribute(name).value();
|
||||
} else if (node.child(name)) {
|
||||
|
|
@ -23,14 +24,18 @@ get_node_value(pugi::xml_node node, const char *name)
|
|||
<< node.name() << "\" XML node";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
std::string value {value_char};
|
||||
|
||||
// Convert to lowercase string.
|
||||
std::string value(value_char);
|
||||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||||
// Convert to lower-case if needed
|
||||
if (lowercase) {
|
||||
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
|
||||
}
|
||||
|
||||
// Remove whitespace.
|
||||
value.erase(0, value.find_first_not_of(" \t\r\n"));
|
||||
value.erase(value.find_last_not_of(" \t\r\n") + 1);
|
||||
// Strip leading/trailing whitespace if needed
|
||||
if (strip) {
|
||||
value.erase(0, value.find_first_not_of(" \t\r\n"));
|
||||
value.erase(value.find_last_not_of(" \t\r\n") + 1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef XML_INTERFACE_H
|
||||
#define XML_INTERFACE_H
|
||||
|
||||
#include <sstream> // for stringstream
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -15,7 +16,25 @@ check_for_node(pugi::xml_node node, const char *name)
|
|||
return node.attribute(name) || node.child(name);
|
||||
}
|
||||
|
||||
std::string get_node_value(pugi::xml_node node, const char *name);
|
||||
std::string get_node_value(pugi::xml_node node, const char *name,
|
||||
bool lowercase=false, bool strip=false);
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> get_node_array(pugi::xml_node node, const char* name,
|
||||
bool lowercase=false)
|
||||
{
|
||||
// Get value of node attribute/child
|
||||
std::string s {get_node_value(node, name, lowercase)};
|
||||
|
||||
// Read values one by one into vector
|
||||
std::stringstream iss {s};
|
||||
T value;
|
||||
std::vector<T> values;
|
||||
while (iss >> value)
|
||||
values.push_back(value);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
#endif // XML_INTERFACE_H
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
import numpy as np
|
||||
import openmc
|
||||
from pkg_resources import parse_version
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def numpy_version_requirement():
|
||||
assert parse_version(np.__version__) >= parse_version("1.14"), \
|
||||
"Regression tests require NumPy 1.14 or greater"
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', autouse=True)
|
||||
def setup_regression_test(request):
|
||||
# Reset autogenerated IDs assigned to OpenMC objects
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from openmc.data import JOULE_PER_EV
|
|||
import openmc.deplete
|
||||
|
||||
from tests.regression_tests import config
|
||||
from .example_geometry import generate_problem
|
||||
from example_geometry import generate_problem
|
||||
|
||||
|
||||
def test_full(run_in_tmpdir):
|
||||
|
|
@ -34,15 +34,15 @@ def test_full(run_in_tmpdir):
|
|||
# OpenMC-specific settings
|
||||
settings = openmc.Settings()
|
||||
settings.particles = 100
|
||||
settings.batches = 100
|
||||
settings.inactive = 40
|
||||
settings.batches = 10
|
||||
settings.inactive = 0
|
||||
space = openmc.stats.Box(lower_left, upper_right)
|
||||
settings.source = openmc.Source(space=space)
|
||||
settings.seed = 1
|
||||
settings.verbosity = 3
|
||||
|
||||
# Create operator
|
||||
chain_file = Path(__file__).parents[1] / 'chain_simple.xml'
|
||||
chain_file = Path(__file__).parents[2] / 'chain_simple.xml'
|
||||
op = openmc.deplete.Operator(geometry, settings, chain_file)
|
||||
op.round_number = True
|
||||
|
||||
BIN
tests/regression_tests/deplete/test_reference.h5
Normal file
BIN
tests/regression_tests/deplete/test_reference.h5
Normal file
Binary file not shown.
|
|
@ -1,2 +1,2 @@
|
|||
energyfunction nuclide score mean std. dev.
|
||||
0 02180f5f310ee4 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03
|
||||
0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.00e-01 9.97e-03
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@
|
|||
<bins>0.0 0.625 20000000.0</bins>
|
||||
</filter>
|
||||
<filter id="4" type="mu">
|
||||
<bins>-1.0 -0.818181818182 -0.636363636364 -0.454545454545 -0.272727272727 -0.0909090909091 0.0909090909091 0.272727272727 0.454545454545 0.636363636364 0.818181818182 1.0</bins>
|
||||
<bins>-1.0 -0.8181818181818181 -0.6363636363636364 -0.4545454545454546 -0.2727272727272727 -0.09090909090909083 0.09090909090909083 0.2727272727272727 0.4545454545454546 0.6363636363636365 0.8181818181818183 1.0</bins>
|
||||
</filter>
|
||||
<filter id="17" type="material">
|
||||
<bins>2</bins>
|
||||
|
|
|
|||
|
|
@ -37,5 +37,5 @@ Cell
|
|||
Fill = Material 2
|
||||
Region = -1
|
||||
Rotation = None
|
||||
Temperature = [ 500. 700. 0. 800.]
|
||||
Temperature = [500. 700. 0. 800.]
|
||||
Translation = None
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@
|
|||
</space>
|
||||
<angle type="isotropic" />
|
||||
<energy interpolation="histogram" type="tabular">
|
||||
<parameters>1.0 1.38949549437 1.93069772888 2.68269579528 3.72759372031 5.17947467923 7.19685673001 10.0 13.8949549437 19.3069772888 26.8269579528 37.2759372031 51.7947467923 71.9685673001 100.0 138.949549437 193.069772888 268.269579528 372.759372031 517.947467923 719.685673001 1000.0 1389.49549437 1930.69772888 2682.69579528 3727.59372031 5179.47467923 7196.85673001 10000.0 13894.9549437 19306.9772888 26826.9579528 37275.9372031 51794.7467923 71968.5673001 100000.0 138949.549437 193069.772888 268269.579528 372759.372031 517947.467923 719685.673001 1000000.0 1389495.49437 1930697.72888 2682695.79528 3727593.72031 5179474.67923 7196856.73001 10000000.0 0.0 2.90864392994e-08 5.80533561806e-08 8.67817193689e-08 1.15153477858e-07 1.43052046006e-07 1.70362782612e-07 1.96973462002e-07 2.22774735186e-07 2.47660579198e-07 2.71528732767e-07 2.9428111653e-07 3.15824236062e-07 3.36069566065e-07 3.54933914133e-07 3.72339762616e-07 3.88215587147e-07 4.02496150558e-07 4.15122770952e-07 4.26043562837e-07 4.35213650335e-07 4.42595351592e-07 4.48158333612e-07 4.5187973691e-07 4.53744269441e-07 4.53744269441e-07 4.5187973691e-07 4.48158333612e-07 4.42595351592e-07 4.35213650335e-07 4.26043562837e-07 4.15122770952e-07 4.02496150558e-07 3.88215587147e-07 3.72339762616e-07 3.54933914133e-07 3.36069566065e-07 3.15824236062e-07 2.9428111653e-07 2.71528732767e-07 2.47660579198e-07 2.22774735186e-07 1.96973462002e-07 1.70362782612e-07 1.43052046006e-07 1.15153477858e-07 8.67817193689e-08 5.80533561806e-08 2.90864392994e-08 5.55962111528e-23</parameters>
|
||||
<parameters>1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23</parameters>
|
||||
</energy>
|
||||
</source>
|
||||
</settings>
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@
|
|||
<bins>0.0 4000000.0 20000000.0</bins>
|
||||
</filter>
|
||||
<filter id="2" type="polar">
|
||||
<bins>0.0 0.785398163397 3.14159265359</bins>
|
||||
<bins>0.0 0.7853981633974483 3.141592653589793</bins>
|
||||
</filter>
|
||||
<filter id="3" type="azimuthal">
|
||||
<bins>0.0 0.785398163397 3.14159265359</bins>
|
||||
<bins>0.0 0.7853981633974483 3.141592653589793</bins>
|
||||
</filter>
|
||||
<filter id="4" type="surface">
|
||||
<bins>1</bins>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue