mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-28 14:15:42 -04:00
Merge branch 'develop' into cmfd-capi
This commit is contained in:
commit
0a0168eaf0
218 changed files with 53530 additions and 4299 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,8 +288,6 @@ set_target_properties(faddeeva PROPERTIES
|
|||
|
||||
add_library(libopenmc SHARED
|
||||
src/algorithm.F90
|
||||
src/angle_distribution.F90
|
||||
src/angleenergy_header.F90
|
||||
src/bank_header.F90
|
||||
src/api.F90
|
||||
src/cmfd_data.F90
|
||||
|
|
@ -298,7 +304,6 @@ add_library(libopenmc SHARED
|
|||
src/eigenvalue.F90
|
||||
src/endf.F90
|
||||
src/endf_header.F90
|
||||
src/energy_distribution.F90
|
||||
src/error.F90
|
||||
src/geometry.F90
|
||||
src/geometry_header.F90
|
||||
|
|
@ -326,17 +331,12 @@ add_library(libopenmc SHARED
|
|||
src/physics_mg.F90
|
||||
src/plot.F90
|
||||
src/plot_header.F90
|
||||
src/product_header.F90
|
||||
src/progress_header.F90
|
||||
src/pugixml/pugixml_f.F90
|
||||
src/random_lcg.F90
|
||||
src/reaction_header.F90
|
||||
src/relaxng
|
||||
src/sab_header.F90
|
||||
src/secondary_correlated.F90
|
||||
src/secondary_kalbach.F90
|
||||
src/secondary_nbody.F90
|
||||
src/secondary_uncorrelated.F90
|
||||
src/set_header.F90
|
||||
src/settings.F90
|
||||
src/simulation_header.F90
|
||||
|
|
@ -384,25 +384,40 @@ add_library(libopenmc SHARED
|
|||
src/tallies/trigger.F90
|
||||
src/tallies/trigger_header.F90
|
||||
src/cell.cpp
|
||||
src/distribution.cpp
|
||||
src/distribution_angle.cpp
|
||||
src/distribution_energy.cpp
|
||||
src/distribution_multi.cpp
|
||||
src/distribution_spatial.cpp
|
||||
src/endf.cpp
|
||||
src/initialize.cpp
|
||||
src/finalize.cpp
|
||||
src/geometry_aux.cpp
|
||||
src/hdf5_interface.cpp
|
||||
src/lattice.cpp
|
||||
src/material.cpp
|
||||
src/math_functions.cpp
|
||||
src/message_passing.cpp
|
||||
src/mgxs.cpp
|
||||
src/mgxs_interface.cpp
|
||||
src/particle.cpp
|
||||
src/plot.cpp
|
||||
src/position.cpp
|
||||
src/pugixml/pugixml_c.cpp
|
||||
src/random_lcg.cpp
|
||||
src/reaction.cpp
|
||||
src/reaction_product.cpp
|
||||
src/secondary_correlated.cpp
|
||||
src/secondary_kalbach.cpp
|
||||
src/secondary_nbody.cpp
|
||||
src/secondary_uncorrelated.cpp
|
||||
src/scattdata.cpp
|
||||
src/settings.cpp
|
||||
src/simulation.cpp
|
||||
src/state_point.cpp
|
||||
src/string_functions.cpp
|
||||
src/surface.cpp
|
||||
src/thermal.cpp
|
||||
src/xml_interface.cpp
|
||||
src/xsdata.cpp)
|
||||
set_target_properties(libopenmc PROPERTIES
|
||||
|
|
@ -449,7 +464,7 @@ endif()
|
|||
# target_link_libraries treats any arguments starting with - but not -l as
|
||||
# linker flags. Thus, we can pass both linker flags and libraries together.
|
||||
target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} pugixml
|
||||
faddeeva)
|
||||
faddeeva xtensor)
|
||||
|
||||
#===============================================================================
|
||||
# openmc executable
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
include CMakeLists.txt
|
||||
include LICENSE
|
||||
include schemas.xml
|
||||
include pyproject.toml
|
||||
include openmc/data/reconstruct.pyx
|
||||
include docs/source/_templates/layout.html
|
||||
include docs/sphinxext/LICENSE
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 19 KiB |
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/
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ Each ``material`` element can have the following attributes or sub-elements:
|
|||
|
||||
*Default*: ""
|
||||
|
||||
:depletable:
|
||||
Boolean value indicating whether the material is depletable.
|
||||
|
||||
:volume:
|
||||
Volume of the material in cm^3.
|
||||
|
||||
:temperature:
|
||||
An element with no attributes which is used to set the default temperature
|
||||
of the material in Kelvin.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -57,9 +57,11 @@ extern "C" {
|
|||
int openmc_material_add_nuclide(int32_t index, const char name[], double density);
|
||||
int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n);
|
||||
int openmc_material_get_id(int32_t index, int32_t* id);
|
||||
int openmc_material_get_volume(int32_t index, double* volume);
|
||||
int openmc_material_set_density(int32_t index, double density);
|
||||
int openmc_material_set_densities(int32_t index, int n, const char** name, const double* density);
|
||||
int openmc_material_set_id(int32_t index, int32_t id);
|
||||
int openmc_material_set_volume(int32_t index, double volume);
|
||||
int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n);
|
||||
int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins);
|
||||
int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh);
|
||||
|
|
@ -96,6 +98,7 @@ extern "C" {
|
|||
int openmc_sphharm_filter_set_order(int32_t index, int order);
|
||||
int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]);
|
||||
int openmc_statepoint_write(const char filename[]);
|
||||
int openmc_tally_allocate(int32_t index, const char* type);
|
||||
int openmc_tally_get_active(int32_t index, bool* active);
|
||||
int openmc_tally_get_estimator(int32_t index, int32_t* estimator);
|
||||
int openmc_tally_get_id(int32_t index, int32_t* id);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -61,7 +61,7 @@ _dll.openmc_simulation_init.restype = c_int
|
|||
_dll.openmc_simulation_init.errcheck = _error_handler
|
||||
_dll.openmc_simulation_finalize.restype = c_int
|
||||
_dll.openmc_simulation_finalize.errcheck = _error_handler
|
||||
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)]
|
||||
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p), POINTER(c_bool)]
|
||||
_dll.openmc_statepoint_write.restype = c_int
|
||||
_dll.openmc_statepoint_write.errcheck = _error_handler
|
||||
|
||||
|
|
@ -314,7 +314,7 @@ def source_bank():
|
|||
return as_array(ptr, (n.value,)).view(bank_dtype)
|
||||
|
||||
|
||||
def statepoint_write(filename=None):
|
||||
def statepoint_write(filename=None, write_source=True):
|
||||
"""Write a statepoint file.
|
||||
|
||||
Parameters
|
||||
|
|
@ -322,11 +322,13 @@ def statepoint_write(filename=None):
|
|||
filename : str or None
|
||||
Path to the statepoint to write. If None is passed, a default name that
|
||||
contains the current batch will be written.
|
||||
write_source : bool
|
||||
Whether or not to include the source bank in the statepoint.
|
||||
|
||||
"""
|
||||
if filename is not None:
|
||||
filename = c_char_p(filename.encode())
|
||||
_dll.openmc_statepoint_write(filename)
|
||||
_dll.openmc_statepoint_write(filename, c_bool(write_source))
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from weakref import WeakValueDictionary
|
|||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from openmc.exceptions import AllocationError, InvalidIDError, OpenMCError
|
||||
from . import _dll, Nuclide
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler
|
||||
|
|
@ -32,6 +32,9 @@ _dll.openmc_material_get_densities.argtypes = [
|
|||
POINTER(c_int)]
|
||||
_dll.openmc_material_get_densities.restype = c_int
|
||||
_dll.openmc_material_get_densities.errcheck = _error_handler
|
||||
_dll.openmc_material_get_volume.argtypes = [c_int32, POINTER(c_double)]
|
||||
_dll.openmc_material_get_volume.restype = c_int
|
||||
_dll.openmc_material_get_volume.errcheck = _error_handler
|
||||
_dll.openmc_material_set_density.argtypes = [c_int32, c_double]
|
||||
_dll.openmc_material_set_density.restype = c_int
|
||||
_dll.openmc_material_set_density.errcheck = _error_handler
|
||||
|
|
@ -42,6 +45,9 @@ _dll.openmc_material_set_densities.errcheck = _error_handler
|
|||
_dll.openmc_material_set_id.argtypes = [c_int32, c_int32]
|
||||
_dll.openmc_material_set_id.restype = c_int
|
||||
_dll.openmc_material_set_id.errcheck = _error_handler
|
||||
_dll.openmc_material_set_volume.argtypes = [c_int32, c_double]
|
||||
_dll.openmc_material_set_volume.restype = c_int
|
||||
_dll.openmc_material_set_volume.errcheck = _error_handler
|
||||
|
||||
|
||||
class Material(_FortranObjectWithID):
|
||||
|
|
@ -113,6 +119,19 @@ class Material(_FortranObjectWithID):
|
|||
def id(self, mat_id):
|
||||
_dll.openmc_material_set_id(self._index, mat_id)
|
||||
|
||||
@property
|
||||
def volume(self):
|
||||
volume = c_double()
|
||||
try:
|
||||
_dll.openmc_material_get_volume(self._index, volume)
|
||||
except OpenMCError:
|
||||
return None
|
||||
return volume.value
|
||||
|
||||
@volume.setter
|
||||
def volume(self, volume):
|
||||
_dll.openmc_material_set_volume(self._index, volume)
|
||||
|
||||
@property
|
||||
def nuclides(self):
|
||||
return self._get_densities()[0]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ _dll.openmc_get_tally_index.errcheck = _error_handler
|
|||
_dll.openmc_global_tallies.argtypes = [POINTER(POINTER(c_double))]
|
||||
_dll.openmc_global_tallies.restype = c_int
|
||||
_dll.openmc_global_tallies.errcheck = _error_handler
|
||||
_dll.openmc_tally_allocate.argtypes = [c_int32, c_char_p]
|
||||
_dll.openmc_tally_allocate.restype = c_int
|
||||
_dll.openmc_tally_allocate.errcheck = _error_handler
|
||||
_dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)]
|
||||
_dll.openmc_tally_get_active.restype = c_int
|
||||
_dll.openmc_tally_get_active.errcheck = _error_handler
|
||||
|
|
@ -101,7 +104,6 @@ _TALLY_TYPES = {
|
|||
}
|
||||
|
||||
|
||||
|
||||
def global_tallies():
|
||||
"""Mean and standard deviation of the mean for each global tally.
|
||||
|
||||
|
|
@ -196,7 +198,7 @@ class Tally(_FortranObjectWithID):
|
|||
|
||||
index = c_int32()
|
||||
_dll.openmc_extend_tallies(1, index, None)
|
||||
_dll.openmc_tally_set_type(index, b'generic')
|
||||
_dll.openmc_tally_allocate(index, b'generic')
|
||||
index = index.value
|
||||
else:
|
||||
index = mapping[uid]._index
|
||||
|
|
@ -224,17 +226,13 @@ class Tally(_FortranObjectWithID):
|
|||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
_dll.openmc_tally_update_type(self._index, type.encode())
|
||||
_dll.openmc_tally_set_type(self._index, type.encode())
|
||||
|
||||
@property
|
||||
def estimator(self):
|
||||
estimator = c_int32()
|
||||
try:
|
||||
_dll.openmc_tally_get_estimator(self._index, estimator)
|
||||
except AllocationError:
|
||||
return ""
|
||||
else:
|
||||
return _ESTIMATORS[estimator.value]
|
||||
_dll.openmc_tally_get_estimator(self._index, estimator)
|
||||
return _ESTIMATORS[estimator.value]
|
||||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
|
|
@ -353,6 +351,7 @@ class Tally(_FortranObjectWithID):
|
|||
return std_dev
|
||||
|
||||
def reset(self):
|
||||
"""Reset results and num_realizations of tally"""
|
||||
_dll.openmc_tally_reset(self._index)
|
||||
|
||||
def ci_width(self, alpha=0.05):
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -284,6 +284,8 @@ class Material(IDManagerMixin):
|
|||
# Create the Material
|
||||
material = cls(mat_id, name)
|
||||
material.depletable = bool(group.attrs['depletable'])
|
||||
if 'volume' in group.attrs:
|
||||
material.volume = group.attrs['volume']
|
||||
|
||||
# Read the names of the S(a,b) tables for this Material and add them
|
||||
if 'sab_names' in group:
|
||||
|
|
@ -833,6 +835,9 @@ class Material(IDManagerMixin):
|
|||
if self._depletable:
|
||||
element.set("depletable", "true")
|
||||
|
||||
if self._volume:
|
||||
element.set("volume", str(self._volume))
|
||||
|
||||
# Create temperature XML subelement
|
||||
if self.temperature is not None:
|
||||
subelement = ET.SubElement(element, "temperature")
|
||||
|
|
|
|||
2
pyproject.toml
Normal file
2
pyproject.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[build-system]
|
||||
requires = ["setuptools", "wheel", "numpy", "cython"]
|
||||
|
|
@ -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
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
module angleenergy_header
|
||||
|
||||
use hdf5_interface, only: HID_T
|
||||
|
||||
!===============================================================================
|
||||
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
|
||||
! distribution that is a function of incoming energy. Each derived type must
|
||||
! implement a sample() subroutine that returns an outgoing energy and scattering
|
||||
! cosine given an incoming energy.
|
||||
!===============================================================================
|
||||
|
||||
type, abstract :: AngleEnergy
|
||||
contains
|
||||
procedure(angleenergy_sample_), deferred :: sample
|
||||
procedure(angleenergy_from_hdf5_), deferred :: from_hdf5
|
||||
end type AngleEnergy
|
||||
|
||||
abstract interface
|
||||
subroutine angleenergy_sample_(this, E_in, E_out, mu)
|
||||
import AngleEnergy
|
||||
class(AngleEnergy), intent(in) :: this
|
||||
real(8), intent(in) :: E_in
|
||||
real(8), intent(out) :: E_out
|
||||
real(8), intent(out) :: mu
|
||||
end subroutine angleenergy_sample_
|
||||
|
||||
subroutine angleenergy_from_hdf5_(this, group_id)
|
||||
import AngleEnergy, HID_T
|
||||
class(AngleEnergy), intent(inout) :: this
|
||||
integer(HID_T), intent(in) :: group_id
|
||||
end subroutine angleenergy_from_hdf5_
|
||||
end interface
|
||||
|
||||
type :: AngleEnergyContainer
|
||||
class(AngleEnergy), allocatable :: obj
|
||||
end type AngleEnergyContainer
|
||||
|
||||
end module angleenergy_header
|
||||
16
src/api.F90
16
src/api.F90
|
|
@ -25,7 +25,7 @@ module openmc_api
|
|||
use tally_header
|
||||
use tally_filter_header
|
||||
use tally_filter
|
||||
use tally, only: openmc_tally_set_type
|
||||
use tally, only: openmc_tally_allocate
|
||||
use simulation
|
||||
use string, only: to_f_string
|
||||
use timer_header
|
||||
|
|
@ -37,8 +37,6 @@ module openmc_api
|
|||
public :: openmc_calculate_volumes
|
||||
public :: openmc_cell_filter_get_bins
|
||||
public :: openmc_cell_get_id
|
||||
public :: openmc_cell_get_fill
|
||||
public :: openmc_cell_set_fill
|
||||
public :: openmc_cell_set_id
|
||||
public :: openmc_cell_set_temperature
|
||||
public :: openmc_energy_filter_get_bins
|
||||
|
|
@ -89,6 +87,7 @@ module openmc_api
|
|||
public :: openmc_simulation_init
|
||||
public :: openmc_source_bank
|
||||
public :: openmc_source_set_strength
|
||||
public :: openmc_tally_allocate
|
||||
public :: openmc_tally_get_estimator
|
||||
public :: openmc_tally_get_id
|
||||
public :: openmc_tally_get_filters
|
||||
|
|
@ -222,7 +221,7 @@ contains
|
|||
if (p % material == MATERIAL_VOID) then
|
||||
id = 0
|
||||
else
|
||||
id = materials(p % material) % id
|
||||
id = materials(p % material) % id()
|
||||
end if
|
||||
end if
|
||||
instance = p % cell_instance - 1
|
||||
|
|
@ -267,7 +266,6 @@ contains
|
|||
if (allocated(tallies)) then
|
||||
do i = 1, size(tallies)
|
||||
associate (t => tallies(i) % obj)
|
||||
t % active = .false.
|
||||
t % n_realizations = 0
|
||||
if (allocated(t % results)) then
|
||||
t % results(:, :, :) = ZERO
|
||||
|
|
@ -286,14 +284,6 @@ contains
|
|||
k_abs_tra = ZERO
|
||||
k_sum(:) = ZERO
|
||||
|
||||
! Clear active tally lists
|
||||
call active_analog_tallies % clear()
|
||||
call active_tracklength_tallies % clear()
|
||||
call active_meshsurf_tallies % clear()
|
||||
call active_collision_tallies % clear()
|
||||
call active_surface_tallies % clear()
|
||||
call active_tallies % clear()
|
||||
|
||||
! Reset timers
|
||||
call time_total % reset()
|
||||
call time_total % reset()
|
||||
|
|
|
|||
210
src/cell.cpp
210
src/cell.cpp
|
|
@ -1,7 +1,6 @@
|
|||
#include "cell.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -9,6 +8,8 @@
|
|||
#include "error.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "lattice.h"
|
||||
#include "material.h"
|
||||
#include "openmc.h"
|
||||
#include "surface.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
|
|
@ -19,14 +20,13 @@ namespace openmc {
|
|||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
// TODO: Convert to enum
|
||||
constexpr int32_t OP_LEFT_PAREN {std::numeric_limits<int32_t>::max()};
|
||||
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
|
||||
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
|
||||
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
|
||||
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
|
||||
|
||||
extern "C" double FP_PRECISION;
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
|
@ -199,52 +199,64 @@ generate_rpn(int32_t cell_id, std::vector<int32_t> infix)
|
|||
Cell::Cell(pugi::xml_node cell_node)
|
||||
{
|
||||
if (check_for_node(cell_node, "id")) {
|
||||
id = stoi(get_node_value(cell_node, "id"));
|
||||
id = std::stoi(get_node_value(cell_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of cell in geometry XML file.");
|
||||
}
|
||||
|
||||
//TODO: don't automatically lowercase cell and surface names
|
||||
if (check_for_node(cell_node, "name")) {
|
||||
name = get_node_value(cell_node, "name");
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "universe")) {
|
||||
universe = stoi(get_node_value(cell_node, "universe"));
|
||||
universe = std::stoi(get_node_value(cell_node, "universe"));
|
||||
} else {
|
||||
universe = 0;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "fill")) {
|
||||
fill = stoi(get_node_value(cell_node, "fill"));
|
||||
} else {
|
||||
fill = C_NONE;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "material")) {
|
||||
//TODO: read material ids.
|
||||
material.push_back(C_NONE+1);
|
||||
material.shrink_to_fit();
|
||||
} else {
|
||||
material.push_back(C_NONE);
|
||||
material.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Make sure that either material or fill was specified.
|
||||
if ((material[0] == C_NONE) && (fill == C_NONE)) {
|
||||
// Make sure that either material or fill was specified, but not both.
|
||||
bool fill_present = check_for_node(cell_node, "fill");
|
||||
bool material_present = check_for_node(cell_node, "material");
|
||||
if (!(fill_present || material_present)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Neither material nor fill was specified for cell " << id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
// Make sure that material and fill haven't been specified simultaneously.
|
||||
if ((material[0] != C_NONE) && (fill != C_NONE)) {
|
||||
if (fill_present && material_present) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Cell " << id << " has both a material and a fill specified; "
|
||||
<< "only one can be specified per cell";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
if (fill_present) {
|
||||
fill = std::stoi(get_node_value(cell_node, "fill"));
|
||||
} else {
|
||||
fill = C_NONE;
|
||||
}
|
||||
|
||||
// Read the material element. There can be zero materials (filled with a
|
||||
// universe), more than one material (distribmats), and some materials may
|
||||
// be "void".
|
||||
if (material_present) {
|
||||
std::vector<std::string> mats
|
||||
{get_node_array<std::string>(cell_node, "material", true)};
|
||||
if (mats.size() > 0) {
|
||||
material.reserve(mats.size());
|
||||
for (std::string mat : mats) {
|
||||
if (mat.compare("void") == 0) {
|
||||
material.push_back(MATERIAL_VOID);
|
||||
} else {
|
||||
material.push_back(std::stoi(mat));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "An empty material element was specified for cell " << id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the region specification.
|
||||
std::string region_spec;
|
||||
if (check_for_node(cell_node, "region")) {
|
||||
|
|
@ -279,33 +291,31 @@ Cell::Cell(pugi::xml_node cell_node)
|
|||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
Cell::contains(Position r, Direction u, int32_t on_surface) const
|
||||
{
|
||||
if (simple) {
|
||||
return contains_simple(xyz, uvw, on_surface);
|
||||
return contains_simple(r, u, on_surface);
|
||||
} else {
|
||||
return contains_complex(xyz, uvw, on_surface);
|
||||
return contains_complex(r, u, on_surface);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
std::pair<double, int32_t>
|
||||
Cell::distance(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
Cell::distance(Position r, Direction u, int32_t on_surface) const
|
||||
{
|
||||
double min_dist {INFTY};
|
||||
int32_t i_surf {std::numeric_limits<int32_t>::max()};
|
||||
|
||||
for (int32_t token : rpn) {
|
||||
// Ignore this token if it corresponds to an operator rather than a region.
|
||||
if (token >= OP_UNION) {continue;}
|
||||
if (token >= OP_UNION) continue;
|
||||
|
||||
// Calculate the distance to this surface.
|
||||
// Note the off-by-one indexing
|
||||
bool coincident {token == on_surface};
|
||||
double d {surfaces_c[abs(token)-1]->distance(xyz, uvw, coincident)};
|
||||
double d {global_surfaces[abs(token)-1]->distance(r, u, coincident)};
|
||||
|
||||
// Check if this distance is the new minimum.
|
||||
if (d < min_dist) {
|
||||
|
|
@ -346,7 +356,8 @@ Cell::to_hdf5(hid_t cell_group) const
|
|||
region_spec << " |";
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token);
|
||||
region_spec << " "
|
||||
<< copysign(global_surfaces[abs(token)-1]->id, token);
|
||||
}
|
||||
}
|
||||
write_string(cell_group, "region", region_spec.str(), false);
|
||||
|
|
@ -356,8 +367,7 @@ Cell::to_hdf5(hid_t cell_group) const
|
|||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains_simple(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
Cell::contains_simple(Position r, Direction u, int32_t on_surface) const
|
||||
{
|
||||
for (int32_t token : rpn) {
|
||||
if (token < OP_UNION) {
|
||||
|
|
@ -370,7 +380,7 @@ Cell::contains_simple(const double xyz[3], const double uvw[3],
|
|||
return false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);
|
||||
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
|
||||
if (sense != (token > 0)) {return false;}
|
||||
}
|
||||
}
|
||||
|
|
@ -381,8 +391,7 @@ Cell::contains_simple(const double xyz[3], const double uvw[3],
|
|||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains_complex(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
Cell::contains_complex(Position r, Direction u, int32_t on_surface) const
|
||||
{
|
||||
// Make a stack of booleans. We don't know how big it needs to be, but we do
|
||||
// know that rpn.size() is an upper-bound.
|
||||
|
|
@ -413,7 +422,7 @@ Cell::contains_complex(const double xyz[3], const double uvw[3],
|
|||
stack[i_stack] = false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);;
|
||||
bool sense = global_surfaces[abs(token)-1]->sense(r, u);
|
||||
stack[i_stack] = (sense == (token > 0));
|
||||
}
|
||||
}
|
||||
|
|
@ -435,7 +444,7 @@ Cell::contains_complex(const double xyz[3], const double uvw[3],
|
|||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_cells(pugi::xml_node *node)
|
||||
read_cells(pugi::xml_node* node)
|
||||
{
|
||||
// Count the number of cells.
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {n_cells++;}
|
||||
|
|
@ -443,10 +452,8 @@ read_cells(pugi::xml_node *node)
|
|||
fatal_error("No cells found in geometry.xml!");
|
||||
}
|
||||
|
||||
// Allocate the vector of Cells.
|
||||
global_cells.reserve(n_cells);
|
||||
|
||||
// Loop over XML cell elements and populate the array.
|
||||
global_cells.reserve(n_cells);
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {
|
||||
global_cells.push_back(new Cell(cell_node));
|
||||
}
|
||||
|
|
@ -464,6 +471,67 @@ read_cells(pugi::xml_node *node)
|
|||
global_universes[it->second]->cells.push_back(i);
|
||||
}
|
||||
}
|
||||
global_universes.shrink_to_fit();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// C-API functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n)
|
||||
{
|
||||
if (index >= 1 && index <= global_cells.size()) {
|
||||
//TODO: off-by-one
|
||||
Cell& c {*global_cells[index - 1]};
|
||||
*type = c.type;
|
||||
if (c.type == FILL_MATERIAL) {
|
||||
*indices = c.material.data();
|
||||
*n = c.material.size();
|
||||
} else {
|
||||
*indices = &c.fill;
|
||||
*n = 1;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in cells array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_cell_set_fill(int32_t index, int type, int32_t n,
|
||||
const int32_t* indices)
|
||||
{
|
||||
if (index >= 1 && index <= global_cells.size()) {
|
||||
//TODO: off-by-one
|
||||
Cell& c {*global_cells[index - 1]};
|
||||
if (type == FILL_MATERIAL) {
|
||||
c.type = FILL_MATERIAL;
|
||||
c.material.clear();
|
||||
for (int i = 0; i < n; i++) {
|
||||
int i_mat = indices[i];
|
||||
if (i_mat == MATERIAL_VOID) {
|
||||
c.material.push_back(MATERIAL_VOID);
|
||||
} else if (i_mat >= 1 && i_mat <= global_materials.size()) {
|
||||
//TODO: off-by-one
|
||||
c.material.push_back(i_mat - 1);
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
c.material.shrink_to_fit();
|
||||
} else if (type == FILL_UNIVERSE) {
|
||||
c.type = FILL_UNIVERSE;
|
||||
} else {
|
||||
c.type = FILL_LATTICE;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in cells array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -473,40 +541,50 @@ read_cells(pugi::xml_node *node)
|
|||
extern "C" {
|
||||
Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];}
|
||||
|
||||
int32_t cell_id(Cell *c) {return c->id;}
|
||||
int32_t cell_id(Cell* c) {return c->id;}
|
||||
|
||||
void cell_set_id(Cell *c, int32_t id) {c->id = id;}
|
||||
void cell_set_id(Cell* c, int32_t id) {c->id = id;}
|
||||
|
||||
int cell_type(Cell *c) {return c->type;}
|
||||
int cell_type(Cell* c) {return c->type;}
|
||||
|
||||
void cell_set_type(Cell *c, int type) {c->type = type;}
|
||||
int32_t cell_universe(Cell* c) {return c->universe;}
|
||||
|
||||
int32_t cell_universe(Cell *c) {return c->universe;}
|
||||
int32_t cell_fill(Cell* c) {return c->fill;}
|
||||
|
||||
void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;}
|
||||
int32_t cell_n_instances(Cell* c) {return c->n_instances;}
|
||||
|
||||
int32_t cell_fill(Cell *c) {return c->fill;}
|
||||
int cell_material_size(Cell* c) {return c->material.size();}
|
||||
|
||||
int32_t* cell_fill_ptr(Cell *c) {return &c->fill;}
|
||||
|
||||
int32_t cell_n_instances(Cell *c) {return c->n_instances;}
|
||||
|
||||
bool cell_simple(Cell *c) {return c->simple;}
|
||||
|
||||
bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface)
|
||||
{return c->contains(xyz, uvw, on_surface);}
|
||||
|
||||
void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface,
|
||||
double *min_dist, int32_t *i_surf)
|
||||
//TODO: off-by-one
|
||||
int32_t cell_material(Cell* c, int i)
|
||||
{
|
||||
std::pair<double, int32_t> out = c->distance(xyz, uvw, on_surface);
|
||||
int32_t mat = c->material[i-1];
|
||||
if (mat == MATERIAL_VOID) return MATERIAL_VOID;
|
||||
return mat + 1;
|
||||
}
|
||||
|
||||
bool cell_simple(Cell* c) {return c->simple;}
|
||||
|
||||
bool cell_contains(Cell* c, double xyz[3], double uvw[3], int32_t on_surface)
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
return c->contains(r, u, on_surface);
|
||||
}
|
||||
|
||||
void cell_distance(Cell* c, double xyz[3], double uvw[3], int32_t on_surface,
|
||||
double* min_dist, int32_t* i_surf)
|
||||
{
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
std::pair<double, int32_t> out = c->distance(r, u, on_surface);
|
||||
*min_dist = out.first;
|
||||
*i_surf = out.second;
|
||||
}
|
||||
|
||||
int32_t cell_offset(Cell *c, int map) {return c->offset[map];}
|
||||
int32_t cell_offset(Cell* c, int map) {return c->offset[map];}
|
||||
|
||||
void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);}
|
||||
void cell_to_hdf5(Cell* c, hid_t group) {c->to_hdf5(group);}
|
||||
|
||||
void extend_cells_c(int32_t n)
|
||||
{
|
||||
|
|
|
|||
28
src/cell.h
28
src/cell.h
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef CELL_H
|
||||
#define CELL_H
|
||||
#ifndef OPENMC_CELL_H
|
||||
#define OPENMC_CELL_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
|
@ -9,6 +10,8 @@
|
|||
#include "hdf5.h"
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include "position.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -16,6 +19,7 @@ namespace openmc {
|
|||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
// TODO: Convert to enum
|
||||
extern "C" int FILL_MATERIAL;
|
||||
extern "C" int FILL_UNIVERSE;
|
||||
extern "C" int FILL_LATTICE;
|
||||
|
|
@ -90,29 +94,27 @@ public:
|
|||
//! provides a performance benefit for the common case. In
|
||||
//! contains_complex, we evaluate the RPN expression using a stack, similar to
|
||||
//! how a RPN calculator would work.
|
||||
//! @param xyz[3] The 3D Cartesian coordinate to check.
|
||||
//! @param uvw[3] A direction used to "break ties" the coordinates are very
|
||||
//! \param r The 3D Cartesian coordinate to check.
|
||||
//! \param u A direction used to "break ties" the coordinates are very
|
||||
//! close to a surface.
|
||||
//! @param on_surface The signed index of a surface that the coordinate is
|
||||
//! \param on_surface The signed index of a surface that the coordinate is
|
||||
//! known to be on. This index takes precedence over surface sense
|
||||
//! calculations.
|
||||
bool
|
||||
contains(const double xyz[3], const double uvw[3], int32_t on_surface) const;
|
||||
contains(Position r, Direction u, int32_t on_surface) const;
|
||||
|
||||
//! Find the oncoming boundary of this cell.
|
||||
std::pair<double, int32_t>
|
||||
distance(const double xyz[3], const double uvw[3], int32_t on_surface) const;
|
||||
distance(Position r, Direction u, int32_t on_surface) const;
|
||||
|
||||
//! \brief Write cell information to an HDF5 group.
|
||||
//! @param group_id An HDF5 group id.
|
||||
//! \param group_id An HDF5 group id.
|
||||
void to_hdf5(hid_t group_id) const;
|
||||
|
||||
protected:
|
||||
bool contains_simple(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const;
|
||||
bool contains_complex(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const;
|
||||
bool contains_simple(Position r, Direction u, int32_t on_surface) const;
|
||||
bool contains_complex(Position r, Direction u, int32_t on_surface) const;
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
#endif // CELL_H
|
||||
#endif // OPENMC_CELL_H
|
||||
|
|
|
|||
|
|
@ -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
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ contains
|
|||
use error, only: fatal_error, warning
|
||||
use mesh_header, only: RegularMesh, openmc_extend_meshes
|
||||
use string
|
||||
use tally, only: openmc_tally_set_type
|
||||
use tally, only: openmc_tally_allocate
|
||||
use tally_header, only: openmc_extend_tallies
|
||||
use tally_filter_header
|
||||
use tally_filter
|
||||
|
|
@ -434,7 +434,7 @@ contains
|
|||
! Begin loop around tallies
|
||||
do i = 1, size(cmfd_tallies)
|
||||
! Allocate tally
|
||||
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR)
|
||||
err = openmc_tally_allocate(i_start + i - 1, C_CHAR_'generic' // C_NULL_CHAR)
|
||||
call openmc_get_tally_next_id(tally_id)
|
||||
err = openmc_tally_set_id(i_start + i - 1, tally_id)
|
||||
|
||||
|
|
@ -465,7 +465,7 @@ contains
|
|||
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
|
||||
|
||||
! Set tally type to volume
|
||||
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
|
||||
! Allocate and set filters
|
||||
allocate(filter_indices(n_filter))
|
||||
|
|
@ -493,7 +493,7 @@ contains
|
|||
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
|
||||
|
||||
! Set tally type to volume
|
||||
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
|
||||
! Set the incoming energy mesh filter index in the tally find_filter
|
||||
! array
|
||||
|
|
@ -542,7 +542,7 @@ contains
|
|||
|
||||
! Set macro bins
|
||||
t % score_bins(1) = SCORE_CURRENT
|
||||
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR)
|
||||
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'mesh-surface' // C_NULL_CHAR)
|
||||
|
||||
else if (i == 4) then
|
||||
! Set name
|
||||
|
|
@ -552,7 +552,7 @@ contains
|
|||
err = openmc_tally_set_estimator(i_start + i - 1, C_CHAR_'analog' // C_NULL_CHAR)
|
||||
|
||||
! Set tally type to volume
|
||||
err = openmc_tally_update_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
err = openmc_tally_set_type(i_start + i - 1, C_CHAR_'volume' // C_NULL_CHAR)
|
||||
|
||||
! Allocate and set filters
|
||||
n_filter = 2
|
||||
|
|
|
|||
|
|
@ -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 :: &
|
||||
|
|
|
|||
393
src/constants.h
393
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,274 @@ typedef std::vector<int> int_1dvec;
|
|||
typedef std::vector<std::vector<int> > int_2dvec;
|
||||
typedef std::vector<std::vector<std::vector<int> > > int_3dvec;
|
||||
|
||||
constexpr int MAX_SAMPLE {10000};
|
||||
// ============================================================================
|
||||
// VERSIONING NUMBERS
|
||||
|
||||
constexpr std::array<int, 3> VERSION {0, 10, 0};
|
||||
// OpenMC major, minor, and release numbers
|
||||
constexpr int VERSION_MAJOR {0};
|
||||
constexpr int VERSION_MINOR {10};
|
||||
constexpr int VERSION_RELEASE {0};
|
||||
constexpr std::array<int, 3> VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE};
|
||||
|
||||
// HDF5 data format
|
||||
constexpr int HDF5_VERSION[] {1, 0};
|
||||
|
||||
// Version numbers for binary files
|
||||
constexpr std::array<int, 2> VERSION_PARTICLE_RESTART {2, 0};
|
||||
constexpr std::array<int, 2> VERSION_TRACK {2, 0};
|
||||
constexpr std::array<int, 2> VERSION_SUMMARY {6, 0};
|
||||
constexpr std::array<int, 2> VERSION_VOLUME {1, 0};
|
||||
constexpr std::array<int, 2> VERSION_VOXEL {1, 0};
|
||||
constexpr std::array<int, 2> VERSION_MGXS_LIBRARY {1, 0};
|
||||
constexpr char VERSION_MULTIPOLE[] {"v0.2"};
|
||||
|
||||
// ============================================================================
|
||||
// ADJUSTABLE PARAMETERS
|
||||
|
||||
// NOTE: This is the only section of the constants module that should ever be
|
||||
// adjusted. Modifying constants in other sections may cause the code to fail.
|
||||
|
||||
// Monoatomic ideal-gas scattering treatment threshold
|
||||
constexpr double FREE_GAS_THRESHOLD {400.0};
|
||||
|
||||
// Significance level for confidence intervals
|
||||
constexpr double CONFIDENCE_LEVEL {0.95};
|
||||
|
||||
// Used for surface current tallies
|
||||
constexpr double TINY_BIT {1e-8};
|
||||
|
||||
// User for precision in geometry
|
||||
constexpr double FP_PRECISION {1e-14};
|
||||
constexpr double FP_REL_PRECISION {1e-5};
|
||||
constexpr double FP_COINCIDENT {1e-12};
|
||||
|
||||
// Maximum number of collisions/crossings
|
||||
constexpr int MAX_EVENTS {1000000};
|
||||
constexpr int MAX_SAMPLE {100000};
|
||||
|
||||
// Maximum number of words in a single line, length of line, and length of
|
||||
// single word
|
||||
constexpr int MAX_WORDS {500};
|
||||
constexpr int MAX_LINE_LEN {250};
|
||||
constexpr int MAX_WORD_LEN {150};
|
||||
constexpr int MAX_FILE_LEN {255};
|
||||
|
||||
// Physical Constants
|
||||
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
|
||||
// Maximum number of external source spatial resamples to encounter before an
|
||||
// error is thrown.
|
||||
constexpr int EXTSRC_REJECT_THRESHOLD {10000};
|
||||
constexpr double EXTSRC_REJECT_FRACTION {0.05};
|
||||
|
||||
// ============================================================================
|
||||
// MATH AND PHYSICAL CONSTANTS
|
||||
|
||||
// Values here are from the Committee on Data for Science and Technology
|
||||
// (CODATA) 2014 recommendation (doi:10.1103/RevModPhys.88.035009).
|
||||
|
||||
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
|
||||
// use so for now we will reuse the Fortran constant until we are OK with
|
||||
// modifying test results
|
||||
constexpr double PI {3.1415926535898};
|
||||
const double SQRT_PI {std::sqrt(PI)};
|
||||
constexpr double INFTY {std::numeric_limits<double>::max()};
|
||||
|
||||
// Physical constants
|
||||
constexpr double MASS_NEUTRON {1.00866491588}; // mass of a neutron in amu
|
||||
constexpr double MASS_NEUTRON_EV {939.5654133e6}; // mass of a neutron in eV/c^2
|
||||
constexpr double MASS_PROTON {1.007276466879}; // mass of a proton in amu
|
||||
constexpr double MASS_ELECTRON_EV {0.5109989461e6}; // electron mass energy equivalent in eV/c^2
|
||||
constexpr double FINE_STRUCTURE {137.035999139}; // inverse fine structure constant
|
||||
constexpr double PLANCK_C {1.2398419739062977e4}; // Planck's constant times c in eV-Angstroms
|
||||
constexpr double AMU {1.660539040e-27}; // 1 amu in kg
|
||||
constexpr double C_LIGHT {2.99792458e8}; // speed of light in m/s
|
||||
constexpr double N_AVOGADRO {0.6022140857}; // Avogadro's number in 10^24/mol
|
||||
constexpr double K_BOLTZMANN {8.6173303e-5}; // Boltzmann constant in eV/K
|
||||
|
||||
// Electron subshell labels
|
||||
constexpr char SUBSHELLS[][4] {
|
||||
"K ", "L1 ", "L2 ", "L3 ", "M1 ", "M2 ", "M3 ", "M4 ", "M5 ",
|
||||
"N1 ", "N2 ", "N3 ", "N4 ", "N5 ", "N6 ", "N7 ", "O1 ", "O2 ",
|
||||
"O3 ", "O4 ", "O5 ", "O6 ", "O7 ", "O8 ", "O9 ", "P1 ", "P2 ",
|
||||
"P3 ", "P4 ", "P5 ", "P6 ", "P7 ", "P8 ", "P9 ", "P10", "P11",
|
||||
"Q1 ", "Q2 ", "Q3 "
|
||||
};
|
||||
|
||||
// Void material
|
||||
// TODO: refactor and remove
|
||||
constexpr int MATERIAL_VOID {-1};
|
||||
|
||||
// ============================================================================
|
||||
// CROSS SECTION RELATED CONSTANTS
|
||||
|
||||
// Angular distribution type
|
||||
// TODO: Convert to enum
|
||||
constexpr int ANGLE_ISOTROPIC {1};
|
||||
constexpr int ANGLE_32_EQUI {2};
|
||||
constexpr int ANGLE_TABULAR {3};
|
||||
constexpr int ANGLE_LEGENDRE {4};
|
||||
constexpr int ANGLE_HISTOGRAM {5};
|
||||
|
||||
// Temperature treatment method
|
||||
// TODO: Convert to enum?
|
||||
constexpr int TEMPERATURE_NEAREST {1};
|
||||
constexpr int TEMPERATURE_INTERPOLATION {2};
|
||||
|
||||
// Reaction types
|
||||
// TODO: Convert to enum
|
||||
constexpr int TOTAL_XS {1};
|
||||
constexpr int ELASTIC {2};
|
||||
constexpr int N_NONELASTIC {3};
|
||||
constexpr int N_LEVEL {4};
|
||||
constexpr int MISC {5};
|
||||
constexpr int N_2ND {11};
|
||||
constexpr int N_2N {16};
|
||||
constexpr int N_3N {17};
|
||||
constexpr int N_FISSION {18};
|
||||
constexpr int N_F {19};
|
||||
constexpr int N_NF {20};
|
||||
constexpr int N_2NF {21};
|
||||
constexpr int N_NA {22};
|
||||
constexpr int N_N3A {23};
|
||||
constexpr int N_2NA {24};
|
||||
constexpr int N_3NA {25};
|
||||
constexpr int N_NP {28};
|
||||
constexpr int N_N2A {29};
|
||||
constexpr int N_2N2A {30};
|
||||
constexpr int N_ND {32};
|
||||
constexpr int N_NT {33};
|
||||
constexpr int N_N3HE {34};
|
||||
constexpr int N_ND2A {35};
|
||||
constexpr int N_NT2A {36};
|
||||
constexpr int N_4N {37};
|
||||
constexpr int N_3NF {38};
|
||||
constexpr int N_2NP {41};
|
||||
constexpr int N_3NP {42};
|
||||
constexpr int N_N2P {44};
|
||||
constexpr int N_NPA {45};
|
||||
constexpr int N_N1 {51};
|
||||
constexpr int N_N40 {90};
|
||||
constexpr int N_NC {91};
|
||||
constexpr int N_DISAPPEAR {101};
|
||||
constexpr int N_GAMMA {102};
|
||||
constexpr int N_P {103};
|
||||
constexpr int N_D {104};
|
||||
constexpr int N_T {105};
|
||||
constexpr int N_3HE {106};
|
||||
constexpr int N_A {107};
|
||||
constexpr int N_2A {108};
|
||||
constexpr int N_3A {109};
|
||||
constexpr int N_2P {111};
|
||||
constexpr int N_PA {112};
|
||||
constexpr int N_T2A {113};
|
||||
constexpr int N_D2A {114};
|
||||
constexpr int N_PD {115};
|
||||
constexpr int N_PT {116};
|
||||
constexpr int N_DA {117};
|
||||
constexpr int N_5N {152};
|
||||
constexpr int N_6N {153};
|
||||
constexpr int N_2NT {154};
|
||||
constexpr int N_TA {155};
|
||||
constexpr int N_4NP {156};
|
||||
constexpr int N_3ND {157};
|
||||
constexpr int N_NDA {158};
|
||||
constexpr int N_2NPA {159};
|
||||
constexpr int N_7N {160};
|
||||
constexpr int N_8N {161};
|
||||
constexpr int N_5NP {162};
|
||||
constexpr int N_6NP {163};
|
||||
constexpr int N_7NP {164};
|
||||
constexpr int N_4NA {165};
|
||||
constexpr int N_5NA {166};
|
||||
constexpr int N_6NA {167};
|
||||
constexpr int N_7NA {168};
|
||||
constexpr int N_4ND {169};
|
||||
constexpr int N_5ND {170};
|
||||
constexpr int N_6ND {171};
|
||||
constexpr int N_3NT {172};
|
||||
constexpr int N_4NT {173};
|
||||
constexpr int N_5NT {174};
|
||||
constexpr int N_6NT {175};
|
||||
constexpr int N_2N3HE {176};
|
||||
constexpr int N_3N3HE {177};
|
||||
constexpr int N_4N3HE {178};
|
||||
constexpr int N_3N2P {179};
|
||||
constexpr int N_3N3A {180};
|
||||
constexpr int N_3NPA {181};
|
||||
constexpr int N_DT {182};
|
||||
constexpr int N_NPD {183};
|
||||
constexpr int N_NPT {184};
|
||||
constexpr int N_NDT {185};
|
||||
constexpr int N_NP3HE {186};
|
||||
constexpr int N_ND3HE {187};
|
||||
constexpr int N_NT3HE {188};
|
||||
constexpr int N_NTA {189};
|
||||
constexpr int N_2N2P {190};
|
||||
constexpr int N_P3HE {191};
|
||||
constexpr int N_D3HE {192};
|
||||
constexpr int N_3HEA {193};
|
||||
constexpr int N_4N2P {194};
|
||||
constexpr int N_4N2A {195};
|
||||
constexpr int N_4NPA {196};
|
||||
constexpr int N_3P {197};
|
||||
constexpr int N_N3P {198};
|
||||
constexpr int N_3N2PA {199};
|
||||
constexpr int N_5N2P {200};
|
||||
constexpr int COHERENT {502};
|
||||
constexpr int INCOHERENT {504};
|
||||
constexpr int PAIR_PROD_ELEC {515};
|
||||
constexpr int PAIR_PROD {516};
|
||||
constexpr int PAIR_PROD_NUC {517};
|
||||
constexpr int PHOTOELECTRIC {522};
|
||||
constexpr int N_P0 {600};
|
||||
constexpr int N_PC {649};
|
||||
constexpr int N_D0 {650};
|
||||
constexpr int N_DC {699};
|
||||
constexpr int N_T0 {700};
|
||||
constexpr int N_TC {749};
|
||||
constexpr int N_3HE0 {750};
|
||||
constexpr int N_3HEC {799};
|
||||
constexpr int N_A0 {800};
|
||||
constexpr int N_AC {849};
|
||||
constexpr int N_2N0 {875};
|
||||
constexpr int N_2NC {891};
|
||||
|
||||
constexpr std::array<int, 6> DEPLETION_RX {N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N};
|
||||
|
||||
// Fission neutron emission (nu) type
|
||||
constexpr int NU_NONE {0}; // No nu values (non-fissionable)
|
||||
constexpr int NU_POLYNOMIAL {1}; // Nu values given by polynomial
|
||||
constexpr int NU_TABULAR {2}; // Nu values given by tabular distribution
|
||||
|
||||
// Library types
|
||||
constexpr int LIBRARY_NEUTRON {1};
|
||||
constexpr int LIBRARY_THERMAL {2};
|
||||
constexpr int LIBRARY_PHOTON {3};
|
||||
constexpr int LIBRARY_MULTIGROUP {4};
|
||||
|
||||
// Probability table parameters
|
||||
constexpr int URR_CUM_PROB {1};
|
||||
constexpr int URR_TOTAL {2};
|
||||
constexpr int URR_ELASTIC {3};
|
||||
constexpr int URR_FISSION {4};
|
||||
constexpr int URR_N_GAMMA {5};
|
||||
constexpr int URR_HEATING {6};
|
||||
|
||||
// Maximum number of partial fission reactions
|
||||
constexpr int PARTIAL_FISSION_MAX {4};
|
||||
|
||||
// Resonance elastic scattering methods
|
||||
// TODO: Convert to enum
|
||||
constexpr int RES_SCAT_ARES {1};
|
||||
constexpr int RES_SCAT_DBRC {2};
|
||||
constexpr int RES_SCAT_WCM {3};
|
||||
constexpr int RES_SCAT_CXS {4};
|
||||
|
||||
// Electron treatments
|
||||
// TODO: Convert to enum
|
||||
constexpr int ELECTRON_LED {1}; // Local Energy Deposition
|
||||
constexpr int ELECTRON_TTB {2}; // Thick Target Bremsstrahlung
|
||||
|
||||
// ============================================================================
|
||||
// MULTIGROUP RELATED
|
||||
|
||||
// MGXS Table Types
|
||||
// TODO: Convert to enum
|
||||
constexpr int MGXS_ISOTROPIC {1}; // Isotroically weighted data
|
||||
constexpr int MGXS_ANGLE {2}; // Data by angular bins
|
||||
|
||||
|
|
@ -53,18 +299,8 @@ constexpr double MACROSCOPIC_AWR {-2.};
|
|||
// Number of mu bins to use when converting Legendres to tabular type
|
||||
constexpr int DEFAULT_NMU {33};
|
||||
|
||||
// Temperature treatment method
|
||||
constexpr int TEMPERATURE_NEAREST {1};
|
||||
constexpr int TEMPERATURE_INTERPOLATION {2};
|
||||
|
||||
// TODO: cmath::M_PI has 3 more digits precision than the Fortran constant we
|
||||
// use so for now we will reuse the Fortran constant until we are OK with
|
||||
// modifying test results
|
||||
constexpr double PI {3.1415926535898};
|
||||
|
||||
const double SQRT_PI {std::sqrt(PI)};
|
||||
|
||||
// Mgxs::get_xs enumerated types
|
||||
// TODO: Convert to enum
|
||||
constexpr int MG_GET_XS_TOTAL {0};
|
||||
constexpr int MG_GET_XS_ABSORPTION {1};
|
||||
constexpr int MG_GET_XS_INVERSE_VELOCITY {2};
|
||||
|
|
@ -81,11 +317,120 @@ constexpr int MG_GET_XS_NU_FISSION {12};
|
|||
constexpr int MG_GET_XS_CHI_PROMPT {13};
|
||||
constexpr int MG_GET_XS_CHI_DELAYED {14};
|
||||
|
||||
extern "C" double FP_COINCIDENT;
|
||||
extern "C" double FP_PRECISION;
|
||||
constexpr double INFTY {std::numeric_limits<double>::max()};
|
||||
// ============================================================================
|
||||
// TALLY-RELATED CONSTANTS
|
||||
|
||||
// Tally result entries
|
||||
constexpr int RESULT_VALUE {1};
|
||||
constexpr int RESULT_SUM {2};
|
||||
constexpr int RESULT_SUM_SQ {3};
|
||||
|
||||
// Tally type
|
||||
// TODO: Convert to enum
|
||||
constexpr int TALLY_VOLUME {1};
|
||||
constexpr int TALLY_MESH_SURFACE {2};
|
||||
constexpr int TALLY_SURFACE {3};
|
||||
|
||||
// Tally estimator types
|
||||
// TODO: Convert to enum
|
||||
constexpr int ESTIMATOR_ANALOG {1};
|
||||
constexpr int ESTIMATOR_TRACKLENGTH {2};
|
||||
constexpr int ESTIMATOR_COLLISION {3};
|
||||
|
||||
// Event types for tallies
|
||||
// TODO: Convert to enum
|
||||
constexpr int EVENT_SURFACE {-2};
|
||||
constexpr int EVENT_LATTICE {-1};
|
||||
constexpr int EVENT_SCATTER {1};
|
||||
constexpr int EVENT_ABSORB {2};
|
||||
|
||||
// Tally score type -- if you change these, make sure you also update the
|
||||
// _SCORES dictionary in openmc/capi/tally.py
|
||||
// TODO: Convert to enum
|
||||
constexpr int SCORE_FLUX {-1}; // flux
|
||||
constexpr int SCORE_TOTAL {-2}; // total reaction rate
|
||||
constexpr int SCORE_SCATTER {-3}; // scattering rate
|
||||
constexpr int SCORE_NU_SCATTER {-4}; // scattering production rate
|
||||
constexpr int SCORE_ABSORPTION {-5}; // absorption rate
|
||||
constexpr int SCORE_FISSION {-6}; // fission rate
|
||||
constexpr int SCORE_NU_FISSION {-7}; // neutron production rate
|
||||
constexpr int SCORE_KAPPA_FISSION {-8}; // fission energy production rate
|
||||
constexpr int SCORE_CURRENT {-9}; // current
|
||||
constexpr int SCORE_EVENTS {-10}; // number of events
|
||||
constexpr int SCORE_DELAYED_NU_FISSION {-11}; // delayed neutron production rate
|
||||
constexpr int SCORE_PROMPT_NU_FISSION {-12}; // prompt neutron production rate
|
||||
constexpr int SCORE_INVERSE_VELOCITY {-13}; // flux-weighted inverse velocity
|
||||
constexpr int SCORE_FISS_Q_PROMPT {-14}; // prompt fission Q-value
|
||||
constexpr int SCORE_FISS_Q_RECOV {-15}; // recoverable fission Q-value
|
||||
constexpr int SCORE_DECAY_RATE {-16}; // delayed neutron precursor decay rate
|
||||
|
||||
// Tally map bin finding
|
||||
constexpr int NO_BIN_FOUND {-1};
|
||||
|
||||
// Tally filter and map types
|
||||
// TODO: Refactor to remove or convert to enum
|
||||
constexpr int FILTER_UNIVERSE {1};
|
||||
constexpr int FILTER_MATERIAL {2};
|
||||
constexpr int FILTER_CELL {3};
|
||||
constexpr int FILTER_CELLBORN {4};
|
||||
constexpr int FILTER_SURFACE {5};
|
||||
constexpr int FILTER_MESH {6};
|
||||
constexpr int FILTER_ENERGYIN {7};
|
||||
constexpr int FILTER_ENERGYOUT {8};
|
||||
constexpr int FILTER_DISTRIBCELL {9};
|
||||
constexpr int FILTER_MU {10};
|
||||
constexpr int FILTER_POLAR {11};
|
||||
constexpr int FILTER_AZIMUTHAL {12};
|
||||
constexpr int FILTER_DELAYEDGROUP {13};
|
||||
constexpr int FILTER_ENERGYFUNCTION {14};
|
||||
constexpr int FILTER_CELLFROM {15};
|
||||
constexpr int FILTER_MESHSURFACE {16};
|
||||
constexpr int FILTER_LEGENDRE {17};
|
||||
constexpr int FILTER_SPH_HARMONICS {18};
|
||||
constexpr int FILTER_SPTL_LEGENDRE {19};
|
||||
constexpr int FILTER_ZERNIKE {20};
|
||||
constexpr int FILTER_PARTICLE {21};
|
||||
|
||||
// Mesh types
|
||||
constexpr int MESH_REGULAR {1};
|
||||
|
||||
// Tally surface current directions
|
||||
constexpr int OUT_LEFT {1}; // x min
|
||||
constexpr int IN_LEFT {2}; // x min
|
||||
constexpr int OUT_RIGHT {3}; // x max
|
||||
constexpr int IN_RIGHT {4}; // x max
|
||||
constexpr int OUT_BACK {5}; // y min
|
||||
constexpr int IN_BACK {6}; // y min
|
||||
constexpr int OUT_FRONT {7}; // y max
|
||||
constexpr int IN_FRONT {8}; // y max
|
||||
constexpr int OUT_BOTTOM {9}; // z min
|
||||
constexpr int IN_BOTTOM {10}; // z min
|
||||
constexpr int OUT_TOP {11}; // z max
|
||||
constexpr int IN_TOP {12}; // z max
|
||||
|
||||
// Tally trigger types and threshold
|
||||
constexpr int VARIANCE {1};
|
||||
constexpr int RELATIVE_ERROR {2};
|
||||
constexpr int STANDARD_DEVIATION {3};
|
||||
|
||||
// Global tally parameters
|
||||
constexpr int K_COLLISION {1};
|
||||
constexpr int K_ABSORPTION {2};
|
||||
constexpr int K_TRACKLENGTH {3};
|
||||
constexpr int LEAKAGE {4};
|
||||
|
||||
// Differential tally independent variables
|
||||
constexpr int DIFF_DENSITY {1};
|
||||
constexpr int DIFF_NUCLIDE_DENSITY {2};
|
||||
constexpr int DIFF_TEMPERATURE {3};
|
||||
|
||||
constexpr int C_NONE {-1};
|
||||
|
||||
// Interpolation rules
|
||||
enum class Interpolation {
|
||||
histogram, lin_lin, lin_log, log_lin, log_log
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // CONSTANTS_H
|
||||
#endif // OPENMC_CONSTANTS_H
|
||||
|
|
|
|||
266
src/distribution.cpp
Normal file
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
|
||||
154
src/distribution.h
Normal file
154
src/distribution.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
//! \file distribution.h
|
||||
//! Univariate probability distributions
|
||||
|
||||
#ifndef OPENMC_DISTRIBUTION_H
|
||||
#define OPENMC_DISTRIBUTION_H
|
||||
|
||||
#include <cstddef> // for size_t
|
||||
#include <memory> // for unique_ptr
|
||||
#include <vector> // for vector
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Abstract class representing a univariate probability distribution
|
||||
//==============================================================================
|
||||
|
||||
class Distribution {
|
||||
public:
|
||||
virtual ~Distribution() = default;
|
||||
virtual double sample() const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! A discrete distribution (probability mass function)
|
||||
//==============================================================================
|
||||
|
||||
class Discrete : public Distribution {
|
||||
public:
|
||||
explicit Discrete(pugi::xml_node node);
|
||||
Discrete(const double* x, const double* p, int n);
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
std::vector<double> x_; //!< Possible outcomes
|
||||
std::vector<double> p_; //!< Probability of each outcome
|
||||
|
||||
//! Normalize distribution so that probabilities sum to unity
|
||||
void normalize();
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Uniform distribution over the interval [a,b]
|
||||
//==============================================================================
|
||||
|
||||
class Uniform : public Distribution {
|
||||
public:
|
||||
explicit Uniform(pugi::xml_node node);
|
||||
Uniform(double a, double b) : a_{a}, b_{b} {};
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double a_; //!< Lower bound of distribution
|
||||
double b_; //!< Upper bound of distribution
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Maxwellian distribution of form c*E*exp(-E/theta)
|
||||
//==============================================================================
|
||||
|
||||
class Maxwell : public Distribution {
|
||||
public:
|
||||
explicit Maxwell(pugi::xml_node node);
|
||||
Maxwell(double theta) : theta_{theta} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double theta_; //!< Factor in exponential [eV]
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Watt fission spectrum with form c*exp(-E/a)*sinh(sqrt(b*E))
|
||||
//==============================================================================
|
||||
|
||||
class Watt : public Distribution {
|
||||
public:
|
||||
explicit Watt(pugi::xml_node node);
|
||||
Watt(double a, double b) : a_{a}, b_{b} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
double a_; //!< Factor in exponential [eV]
|
||||
double b_; //!< Factor in square root [1/eV]
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Histogram or linear-linear interpolated tabular distribution
|
||||
//==============================================================================
|
||||
|
||||
class Tabular : public Distribution {
|
||||
public:
|
||||
explicit Tabular(pugi::xml_node node);
|
||||
Tabular(const double* x, const double* p, int n, Interpolation interp,
|
||||
const double* c=nullptr);
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
|
||||
// x property
|
||||
std::vector<double>& x() { return x_; }
|
||||
const std::vector<double>& x() const { return x_; }
|
||||
private:
|
||||
std::vector<double> x_; //!< tabulated independent variable
|
||||
std::vector<double> p_; //!< tabulated probability density
|
||||
std::vector<double> c_; //!< cumulative distribution at tabulated values
|
||||
Interpolation interp_; //!< interpolation rule
|
||||
|
||||
//! Initialize tabulated probability density function
|
||||
//! \param x Array of values for independent variable
|
||||
//! \param p Array of tabulated probabilities
|
||||
//! \param n Number of tabulated values
|
||||
void init(const double* x, const double* p, std::size_t n,
|
||||
const double* c=nullptr);
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Equiprobable distribution
|
||||
//==============================================================================
|
||||
|
||||
class Equiprobable : public Distribution {
|
||||
public:
|
||||
explicit Equiprobable(pugi::xml_node node);
|
||||
Equiprobable(const double* x, int n) : x_{x, x+n} { };
|
||||
|
||||
//! Sample a value from the distribution
|
||||
//! \return Sampled value
|
||||
double sample() const;
|
||||
private:
|
||||
std::vector<double> x_; //! Possible outcomes
|
||||
};
|
||||
|
||||
|
||||
using UPtrDist = std::unique_ptr<Distribution>;
|
||||
|
||||
//! Return univariate probability distribution specified in XML file
|
||||
//! \param[in] node XML node representing distribution
|
||||
//! \return Unique pointer to distribution
|
||||
UPtrDist distribution_from_xml(pugi::xml_node node);
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_DISTRIBUTION_H
|
||||
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
|
||||
|
|
@ -380,7 +380,11 @@ contains
|
|||
|
||||
! Determine overall generation and number of active generations
|
||||
i = overall_generation()
|
||||
n = i - n_inactive*gen_per_batch
|
||||
if (current_batch > n_inactive) then
|
||||
n = gen_per_batch*n_realizations + current_gen
|
||||
else
|
||||
n = 0
|
||||
end if
|
||||
|
||||
if (n <= 0) then
|
||||
! For inactive generations, use current generation k as estimate for next
|
||||
|
|
|
|||
178
src/endf.cpp
Normal file
178
src/endf.cpp
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
#include "endf.h"
|
||||
|
||||
#include <algorithm> // for copy
|
||||
#include <cmath> // for log, exp
|
||||
#include <iterator> // for back_inserter
|
||||
|
||||
#include "constants.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "search.h"
|
||||
#include "xtensor/xarray.hpp"
|
||||
#include "xtensor/xview.hpp"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Functions
|
||||
//==============================================================================
|
||||
|
||||
Interpolation int2interp(int i)
|
||||
{
|
||||
switch (i) {
|
||||
case 1:
|
||||
return Interpolation::histogram;
|
||||
case 2:
|
||||
return Interpolation::lin_lin;
|
||||
case 3:
|
||||
return Interpolation::lin_log;
|
||||
case 4:
|
||||
return Interpolation::log_lin;
|
||||
case 5:
|
||||
return Interpolation::log_log;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_fission(int mt)
|
||||
{
|
||||
return mt == 18 || mt == 19 || mt == 20 || mt == 21 || mt == 38;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Polynomial implementation
|
||||
//==============================================================================
|
||||
|
||||
Polynomial::Polynomial(hid_t dset)
|
||||
{
|
||||
// Read coefficients into a vector
|
||||
read_dataset(dset, coef_);
|
||||
}
|
||||
|
||||
double Polynomial::operator()(double x) const
|
||||
{
|
||||
// Use Horner's rule to evaluate polynomial. Note that coefficients are
|
||||
// ordered in increasing powers of x.
|
||||
double y = 0.0;
|
||||
for (auto c = coef_.crbegin(); c != coef_.crend(); ++c) {
|
||||
y = y*x + *c;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Tabulated1D implementation
|
||||
//==============================================================================
|
||||
|
||||
Tabulated1D::Tabulated1D(hid_t dset)
|
||||
{
|
||||
read_attribute(dset, "breakpoints", nbt_);
|
||||
n_regions_ = nbt_.size();
|
||||
|
||||
// Change 1-indexing to 0-indexing
|
||||
for (auto& b : nbt_) --b;
|
||||
|
||||
std::vector<int> int_temp;
|
||||
read_attribute(dset, "interpolation", int_temp);
|
||||
|
||||
// Convert vector of ints into Interpolation
|
||||
for (const auto i : int_temp)
|
||||
int_.push_back(int2interp(i));
|
||||
|
||||
xt::xarray<double> arr;
|
||||
read_dataset(dset, arr);
|
||||
|
||||
auto xs = xt::view(arr, 0);
|
||||
auto ys = xt::view(arr, 1);
|
||||
|
||||
std::copy(xs.begin(), xs.end(), std::back_inserter(x_));
|
||||
std::copy(ys.begin(), ys.end(), std::back_inserter(y_));
|
||||
n_pairs_ = x_.size();
|
||||
}
|
||||
|
||||
double Tabulated1D::operator()(double x) const
|
||||
{
|
||||
// find which bin the abscissa is in -- if the abscissa is outside the
|
||||
// tabulated range, the first or last point is chosen, i.e. no interpolation
|
||||
// is done outside the energy range
|
||||
int i;
|
||||
if (x < x_[0]) {
|
||||
return y_[0];
|
||||
} else if (x > x_[n_pairs_ - 1]) {
|
||||
return y_[n_pairs_ - 1];
|
||||
} else {
|
||||
i = lower_bound_index(x_.begin(), x_.end(), x);
|
||||
}
|
||||
|
||||
// determine interpolation scheme
|
||||
Interpolation interp;
|
||||
if (n_regions_ == 0) {
|
||||
interp = Interpolation::lin_lin;
|
||||
} else if (n_regions_ == 1) {
|
||||
interp = int_[0];
|
||||
} else if (n_regions_ > 1) {
|
||||
for (int j = 0; j < n_regions_; ++j) {
|
||||
if (i < nbt_[j]) {
|
||||
interp = int_[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle special case of histogram interpolation
|
||||
if (interp == Interpolation::histogram) return y_[i];
|
||||
|
||||
// determine bounding values
|
||||
double x0 = x_[i];
|
||||
double x1 = x_[i + 1];
|
||||
double y0 = y_[i];
|
||||
double y1 = y_[i + 1];
|
||||
|
||||
// determine interpolation factor and interpolated value
|
||||
double r;
|
||||
switch (interp) {
|
||||
case Interpolation::lin_lin:
|
||||
r = (x - x0)/(x1 - x0);
|
||||
return y0 + r*(y1 - y0);
|
||||
case Interpolation::lin_log:
|
||||
r = log(x/x0)/log(x1/x0);
|
||||
return y0 + r*(y1 - y0);
|
||||
case Interpolation::log_lin:
|
||||
r = (x - x0)/(x1 - x0);
|
||||
return y0*exp(r*log(y1/y0));
|
||||
case Interpolation::log_log:
|
||||
r = log(x/x0)/log(x1/x0);
|
||||
return y0*exp(r*log(y1/y0));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// CoherentElasticXS implementation
|
||||
//==============================================================================
|
||||
|
||||
CoherentElasticXS::CoherentElasticXS(hid_t dset)
|
||||
{
|
||||
// Read 2D array from dataset
|
||||
xt::xarray<double> arr;
|
||||
read_dataset(dset, arr);
|
||||
|
||||
// Get views for Bragg edges and structure factors
|
||||
auto E = xt::view(arr, 0);
|
||||
auto s = xt::view(arr, 1);
|
||||
|
||||
// Copy Bragg edges and partial sums of structure factors
|
||||
std::copy(E.begin(), E.end(), std::back_inserter(bragg_edges_));
|
||||
std::copy(s.begin(), s.end(), std::back_inserter(factors_));
|
||||
}
|
||||
|
||||
double CoherentElasticXS::operator()(double E) const
|
||||
{
|
||||
if (E < bragg_edges_[0]) {
|
||||
// If energy is below that of the lowest Bragg peak, the elastic cross
|
||||
// section will be zero
|
||||
return 0.0;
|
||||
} else {
|
||||
auto i_grid = lower_bound_index(bragg_edges_.begin(), bragg_edges_.end(), E);
|
||||
return factors_[i_grid] / E;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
91
src/endf.h
Normal file
91
src/endf.h
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
//! \file endf.h
|
||||
//! Classes and functions related to the ENDF-6 format
|
||||
|
||||
#ifndef OPENMC_ENDF_H
|
||||
#define OPENMC_ENDF_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "constants.h"
|
||||
#include "hdf5.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//! Convert integer representing interpolation law to enum
|
||||
//! \param[in] i Intereger (e.g. 1=histogram, 2=lin-lin)
|
||||
//! \return Corresponding enum value
|
||||
Interpolation int2interp(int i);
|
||||
|
||||
//! Determine whether MT number corresponds to a fission reaction
|
||||
//! \param[in] MT ENDF MT value
|
||||
//! \return Whether corresponding reaction is a fission reaction
|
||||
bool is_fission(int MT);
|
||||
|
||||
//==============================================================================
|
||||
//! Abstract one-dimensional function
|
||||
//==============================================================================
|
||||
|
||||
class Function1D {
|
||||
public:
|
||||
virtual double operator()(double x) const = 0;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! One-dimensional function expressed as a polynomial
|
||||
//==============================================================================
|
||||
|
||||
class Polynomial : public Function1D {
|
||||
public:
|
||||
//! Construct polynomial from HDF5 data
|
||||
//! \param[in] dset Dataset containing coefficients
|
||||
explicit Polynomial(hid_t dset);
|
||||
|
||||
//! Evaluate the polynomials
|
||||
//! \param[in] x independent variable
|
||||
//! \return Polynomial evaluated at x
|
||||
double operator()(double x) const;
|
||||
private:
|
||||
std::vector<double> coef_; //!< Polynomial coefficients
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! One-dimensional interpolable function
|
||||
//==============================================================================
|
||||
|
||||
class Tabulated1D : public Function1D {
|
||||
public:
|
||||
Tabulated1D() = default;
|
||||
|
||||
//! Construct function from HDF5 data
|
||||
//! \param[in] dset Dataset containing tabulated data
|
||||
explicit Tabulated1D(hid_t dset);
|
||||
|
||||
//! Evaluate the tabulated function
|
||||
//! \param[in] x independent variable
|
||||
//! \return Function evaluated at x
|
||||
double operator()(double x) const;
|
||||
private:
|
||||
std::size_t n_regions_ {0}; //!< number of interpolation regions
|
||||
std::vector<int> nbt_; //!< values separating interpolation regions
|
||||
std::vector<Interpolation> int_; //!< interpolation schemes
|
||||
std::size_t n_pairs_; //!< number of (x,y) pairs
|
||||
std::vector<double> x_; //!< values of abscissa
|
||||
std::vector<double> y_; //!< values of ordinate
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! Coherent elastic scattering data from a crystalline material
|
||||
//==============================================================================
|
||||
|
||||
class CoherentElasticXS : public Function1D {
|
||||
explicit CoherentElasticXS(hid_t dset);
|
||||
double operator()(double E) const;
|
||||
private:
|
||||
std::vector<double> bragg_edges_; //!< Bragg edges in [eV]
|
||||
std::vector<double> factors_; //!< Partial sums of structure factors [eV-b]
|
||||
};
|
||||
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_ENDF_H
|
||||
|
|
@ -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
|
||||
31
src/error.h
31
src/error.h
|
|
@ -5,6 +5,7 @@
|
|||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "openmc.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -14,20 +15,38 @@ extern "C" void warning_from_c(const char* message, int message_len);
|
|||
extern "C" void write_message_from_c(const char* message, int message_len,
|
||||
int level);
|
||||
|
||||
inline
|
||||
void fatal_error(const char *message)
|
||||
inline void
|
||||
set_errmsg(const char* message)
|
||||
{
|
||||
fatal_error_from_c(message, strlen(message));
|
||||
std::strcpy(openmc_err_msg, message);
|
||||
}
|
||||
|
||||
inline void
|
||||
set_errmsg(const std::string& message)
|
||||
{
|
||||
std::strcpy(openmc_err_msg, message.c_str());
|
||||
}
|
||||
|
||||
inline void
|
||||
set_errmsg(const std::stringstream& message)
|
||||
{
|
||||
std::strcpy(openmc_err_msg, message.str().c_str());
|
||||
}
|
||||
|
||||
inline
|
||||
void fatal_error(const std::string &message)
|
||||
void fatal_error(const char* message)
|
||||
{
|
||||
fatal_error_from_c(message, std::strlen(message));
|
||||
}
|
||||
|
||||
inline
|
||||
void fatal_error(const std::string& message)
|
||||
{
|
||||
fatal_error_from_c(message.c_str(), message.length());
|
||||
}
|
||||
|
||||
inline
|
||||
void fatal_error(const std::stringstream &message)
|
||||
void fatal_error(const std::stringstream& message)
|
||||
{
|
||||
fatal_error(message.str());
|
||||
}
|
||||
|
|
@ -47,7 +66,7 @@ void warning(const std::stringstream& message)
|
|||
inline
|
||||
void write_message(const char* message, int level)
|
||||
{
|
||||
write_message_from_c(message, strlen(message), level);
|
||||
write_message_from_c(message, std::strlen(message), level);
|
||||
}
|
||||
|
||||
inline
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ contains
|
|||
p % last_sqrtkT = p % sqrtkT
|
||||
|
||||
! Get distributed offset
|
||||
if (size(c % material) > 1 .or. size(c % sqrtkT) > 1) then
|
||||
if (c % material_size() > 1 .or. size(c % sqrtkT) > 1) then
|
||||
! Distributed instances of this cell have different
|
||||
! materials/temperatures. Determine which instance this is for
|
||||
! assigning the matching material/temperature.
|
||||
|
|
@ -204,7 +204,7 @@ contains
|
|||
end if
|
||||
|
||||
! Save the material
|
||||
if (size(c % material) > 1) then
|
||||
if (c % material_size() > 1) then
|
||||
p % material = c % material(offset + 1)
|
||||
else
|
||||
p % material = c % material(1)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
#ifndef GEOMETRY_H
|
||||
#define GEOMETRY_H
|
||||
#ifndef OPENMC_GEOMETRY_H
|
||||
#define OPENMC_GEOMETRY_H
|
||||
|
||||
namespace openmc {
|
||||
|
||||
extern "C" int openmc_root_universe;
|
||||
|
||||
#endif // GEOMETRY_H
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_GEOMETRY_H
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "lattice.h"
|
||||
#include "material.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
|
@ -15,11 +16,11 @@ namespace openmc {
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
adjust_indices_c()
|
||||
adjust_indices()
|
||||
{
|
||||
// Adjust material/fill idices.
|
||||
for (Cell *c : global_cells) {
|
||||
if (c->material[0] == C_NONE) {
|
||||
for (Cell* c : global_cells) {
|
||||
if (c->fill != C_NONE) {
|
||||
int32_t id = c->fill;
|
||||
auto search_univ = universe_map.find(id);
|
||||
auto search_lat = lattice_map.find(id);
|
||||
|
|
@ -36,13 +37,26 @@ adjust_indices_c()
|
|||
fatal_error(err_msg);
|
||||
}
|
||||
} else {
|
||||
//TODO: materials
|
||||
c->type = FILL_MATERIAL;
|
||||
for (auto it = c->material.begin(); it != c->material.end(); it++) {
|
||||
int32_t mid = *it;
|
||||
if (mid != MATERIAL_VOID) {
|
||||
auto search = material_map.find(mid);
|
||||
if (search != material_map.end()) {
|
||||
*it = search->second;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Could not find material " << mid
|
||||
<< " specified on cell " << c->id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change cell.universe values from IDs to indices.
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
auto search = universe_map.find(c->universe);
|
||||
if (search != universe_map.end()) {
|
||||
//TODO: Remove this off-by-one indexing.
|
||||
|
|
@ -56,7 +70,7 @@ adjust_indices_c()
|
|||
}
|
||||
|
||||
// Change all lattice universe values from IDs to indices.
|
||||
for (Lattice *l : lattices_c) {
|
||||
for (Lattice* l : lattices_c) {
|
||||
l->adjust_indices();
|
||||
}
|
||||
}
|
||||
|
|
@ -68,12 +82,12 @@ find_root_universe()
|
|||
{
|
||||
// Find all the universes listed as a cell fill.
|
||||
std::unordered_set<int32_t> fill_univ_ids;
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
fill_univ_ids.insert(c->fill);
|
||||
}
|
||||
|
||||
// Find all the universes contained in a lattice.
|
||||
for (Lattice *lat : lattices_c) {
|
||||
for (Lattice* lat : lattices_c) {
|
||||
for (auto it = lat->begin(); it != lat->end(); ++it) {
|
||||
fill_univ_ids.insert(*it);
|
||||
}
|
||||
|
|
@ -109,13 +123,13 @@ find_root_universe()
|
|||
void
|
||||
allocate_offset_tables(int n_maps)
|
||||
{
|
||||
for (Cell *c : global_cells) {
|
||||
for (Cell* c : global_cells) {
|
||||
if (c->type != FILL_MATERIAL) {
|
||||
c->offset.resize(n_maps, C_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
for (Lattice *lat : lattices_c) {
|
||||
for (Lattice* lat : lattices_c) {
|
||||
lat->allocate_offset_table(n_maps);
|
||||
}
|
||||
}
|
||||
|
|
@ -126,7 +140,7 @@ void
|
|||
count_cell_instances(int32_t univ_indx)
|
||||
{
|
||||
for (int32_t cell_indx : global_universes[univ_indx]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
++c.n_instances;
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
|
|
@ -135,7 +149,7 @@ count_cell_instances(int32_t univ_indx)
|
|||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
// This cell contains a lattice. Recurse into the lattice universes.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
count_cell_instances(*it);
|
||||
}
|
||||
|
|
@ -155,14 +169,14 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
|
|||
|
||||
int count {0};
|
||||
for (int32_t cell_indx : global_universes[search_univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
|
|
@ -178,10 +192,10 @@ count_universe_instances(int32_t search_univ, int32_t target_univ_id)
|
|||
void
|
||||
fill_offset_tables(int32_t target_univ_id, int map)
|
||||
{
|
||||
for (Universe *univ : global_universes) {
|
||||
for (Universe* univ : global_universes) {
|
||||
int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation.
|
||||
for (int32_t cell_indx : univ->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
c.offset[map] = offset;
|
||||
|
|
@ -189,7 +203,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
|
|||
offset += count_universe_instances(search_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
offset = lat.fill_offset_table(offset, target_univ_id, map);
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +214,7 @@ fill_offset_tables(int32_t target_univ_id, int map)
|
|||
|
||||
std::string
|
||||
distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
const Universe &search_univ, int32_t offset)
|
||||
const Universe& search_univ, int32_t offset)
|
||||
{
|
||||
std::stringstream path;
|
||||
|
||||
|
|
@ -210,7 +224,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
// write to the path and return.
|
||||
for (int32_t cell_indx : search_univ.cells) {
|
||||
if ((cell_indx == target_cell) && (offset == target_offset)) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
path << "c" << c.id;
|
||||
return path.str();
|
||||
}
|
||||
|
|
@ -222,7 +236,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
std::vector<std::int32_t>::const_reverse_iterator cell_it
|
||||
{search_univ.cells.crbegin()};
|
||||
for (; cell_it != search_univ.cells.crend(); ++cell_it) {
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
Cell& c = *global_cells[*cell_it];
|
||||
|
||||
// Material cells don't contain other cells so ignore them.
|
||||
if (c.type != FILL_MATERIAL) {
|
||||
|
|
@ -230,7 +244,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
if (c.type == FILL_UNIVERSE) {
|
||||
temp_offset = offset + c.offset[map];
|
||||
} else {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
int32_t indx = lat.universes.size()*map + lat.begin().indx;
|
||||
temp_offset = offset + lat.offsets[indx];
|
||||
}
|
||||
|
|
@ -242,7 +256,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
}
|
||||
|
||||
// Add the cell to the path string.
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
Cell& c = *global_cells[*cell_it];
|
||||
path << "c" << c.id << "->";
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
|
|
@ -253,7 +267,7 @@ distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
return path.str();
|
||||
} else {
|
||||
// Recurse into the lattice cell.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
path << "l" << lat.id;
|
||||
for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
|
||||
int32_t indx = lat.universes.size()*map + it.indx;
|
||||
|
|
@ -275,7 +289,7 @@ int
|
|||
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
Universe& root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
return path_.size() + 1;
|
||||
|
|
@ -285,9 +299,9 @@ distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
|||
|
||||
void
|
||||
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ, char *path)
|
||||
int32_t root_univ, char* path)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
Universe& root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
path_.copy(path, path_.size());
|
||||
|
|
@ -302,12 +316,12 @@ maximum_levels(int32_t univ)
|
|||
int levels_below {0};
|
||||
|
||||
for (int32_t cell_indx : global_universes[univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
Cell& c = *global_cells[cell_indx];
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
Lattice& lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
|
|
@ -324,16 +338,16 @@ maximum_levels(int32_t univ)
|
|||
void
|
||||
free_memory_geometry_c()
|
||||
{
|
||||
for (Cell *c : global_cells) {delete c;}
|
||||
for (Cell* c : global_cells) {delete c;}
|
||||
global_cells.clear();
|
||||
cell_map.clear();
|
||||
n_cells = 0;
|
||||
|
||||
for (Universe *u : global_universes) {delete u;}
|
||||
for (Universe* u : global_universes) {delete u;}
|
||||
global_universes.clear();
|
||||
universe_map.clear();
|
||||
|
||||
for (Lattice *lat : lattices_c) {delete lat;}
|
||||
for (Lattice* lat : lattices_c) {delete lat;}
|
||||
lattices_c.clear();
|
||||
lattice_map.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,47 @@ get_groups(hid_t group_id, char* name[])
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
member_names(hid_t group_id, H5O_type_t type)
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get names
|
||||
H5O_info_t oinfo;
|
||||
size_t size;
|
||||
std::vector<std::string> names;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type != type) continue;
|
||||
|
||||
// Get size of name
|
||||
size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC,
|
||||
i, nullptr, 0, H5P_DEFAULT);
|
||||
|
||||
// Read name
|
||||
char buffer[size];
|
||||
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
|
||||
buffer, size, H5P_DEFAULT);
|
||||
names.emplace_back(&buffer[0], size);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
group_names(hid_t group_id)
|
||||
{
|
||||
return member_names(group_id, H5O_TYPE_GROUP);
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
dataset_names(hid_t group_id)
|
||||
{
|
||||
return member_names(group_id, H5O_TYPE_DATASET);
|
||||
}
|
||||
|
||||
bool
|
||||
object_exists(hid_t object_id, const char* name)
|
||||
|
|
@ -299,14 +381,23 @@ object_exists(hid_t object_id, const char* name)
|
|||
}
|
||||
|
||||
|
||||
std::string
|
||||
object_name(hid_t obj_id)
|
||||
{
|
||||
// Determine size and create buffer
|
||||
size_t size = 1 + H5Iget_name(obj_id, nullptr, 0);
|
||||
char buffer[size];
|
||||
|
||||
// Read and return name
|
||||
H5Iget_name(obj_id, buffer, size);
|
||||
return {buffer, size};
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
open_dataset(hid_t group_id, const char* name)
|
||||
{
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
ensure_exists(group_id, name);
|
||||
return H5Dopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
|
|
@ -314,11 +405,7 @@ open_dataset(hid_t group_id, const char* name)
|
|||
hid_t
|
||||
open_group(hid_t group_id, const char* name)
|
||||
{
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
ensure_exists(group_id, name);
|
||||
return H5Gopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
|
|
@ -425,7 +512,7 @@ read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool inde
|
|||
|
||||
|
||||
void
|
||||
read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep)
|
||||
read_complex(hid_t obj_id, const char* name, std::complex<double>* buffer, bool indep)
|
||||
{
|
||||
// Create compound datatype for complex numbers
|
||||
struct complex_t {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
#ifndef HDF5_INTERFACE_H
|
||||
#define HDF5_INTERFACE_H
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
#ifndef OPENMC_HDF5_INTERFACE_H
|
||||
#define OPENMC_HDF5_INTERFACE_H
|
||||
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <complex.h>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
#include "xtensor/xadapt.hpp"
|
||||
#include "xtensor/xarray.hpp"
|
||||
|
||||
#include "position.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -19,7 +22,7 @@ namespace openmc {
|
|||
//==============================================================================
|
||||
|
||||
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
const void* buffer);
|
||||
void* buffer);
|
||||
void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer);
|
||||
void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
|
|
@ -70,6 +73,13 @@ read_nd_vector(hid_t obj_id, const char* name,
|
|||
std::vector<std::vector<std::vector<std::vector<std::vector<double> > > > >& result,
|
||||
bool must_have = false);
|
||||
|
||||
std::vector<hsize_t> attribute_shape(hid_t obj_id, const char* name);
|
||||
std::vector<std::string> dataset_names(hid_t group_id);
|
||||
void ensure_exists(hid_t group_id, const char* name);
|
||||
std::vector<std::string> group_names(hid_t group_id);
|
||||
std::vector<hsize_t> object_shape(hid_t obj_id);
|
||||
std::string object_name(hid_t obj_id);
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
|
@ -99,7 +109,7 @@ extern "C" {
|
|||
void read_attr_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer);
|
||||
void read_complex(hid_t obj_id, const char* name,
|
||||
double _Complex* buffer, bool indep);
|
||||
std::complex<double>* buffer, bool indep);
|
||||
void read_double(hid_t obj_id, const char* name, double* buffer,
|
||||
bool indep);
|
||||
void read_int(hid_t obj_id, const char* name, int* buffer,
|
||||
|
|
@ -140,7 +150,146 @@ template<typename T>
|
|||
struct H5TypeMap { static const hid_t type_id; };
|
||||
|
||||
//==============================================================================
|
||||
// Template functions used to provide simple interface to lower-level functions
|
||||
// Templates/overloads for read_attribute
|
||||
//==============================================================================
|
||||
|
||||
// Scalar version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, T& buffer)
|
||||
{
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
// vector version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, std::vector<T>& vec)
|
||||
{
|
||||
// Get shape of attribute array
|
||||
auto shape = attribute_shape(obj_id, name);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
vec.resize(size);
|
||||
|
||||
// Read data from attribute
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, vec.data());
|
||||
}
|
||||
|
||||
// Generic array version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, xt::xarray<T>& arr)
|
||||
{
|
||||
// Get shape of attribute array
|
||||
auto shape = attribute_shape(obj_id, name);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
T* buffer = new T[size];
|
||||
|
||||
// Read data from attribute
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, buffer);
|
||||
|
||||
// Adapt array into xarray
|
||||
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
|
||||
}
|
||||
|
||||
// overload for std::string
|
||||
inline void
|
||||
read_attribute(hid_t obj_id, const char* name, std::string& str)
|
||||
{
|
||||
// Create buffer to read data into
|
||||
auto n = attribute_typesize(obj_id, name);
|
||||
char buffer[n];
|
||||
|
||||
// Read attribute and set string
|
||||
read_attr_string(obj_id, name, n, buffer);
|
||||
str = std::string{buffer, n};
|
||||
}
|
||||
|
||||
// overload for std::vector<std::string>
|
||||
inline void
|
||||
read_attribute(hid_t obj_id, const char* name, std::vector<std::string>& vec)
|
||||
{
|
||||
auto dims = attribute_shape(obj_id, name);
|
||||
auto m = dims[0];
|
||||
|
||||
// Allocate a C char array to get strings
|
||||
auto n = attribute_typesize(obj_id, name);
|
||||
char buffer[m][n+1];
|
||||
|
||||
// Read char data in attribute
|
||||
read_attr_string(obj_id, name, n, buffer[0]);
|
||||
|
||||
for (int i = 0; i < m; ++i) {
|
||||
vec.emplace_back(&buffer[i][0]);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for read_dataset
|
||||
//==============================================================================
|
||||
|
||||
template<typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, T& buffer, bool indep=false)
|
||||
{
|
||||
read_dataset(obj_id, name, H5TypeMap<T>::type_id, &buffer, indep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t dset, std::vector<T>& vec, bool indep=false)
|
||||
{
|
||||
// Get shape of dataset
|
||||
std::vector<hsize_t> shape = object_shape(dset);
|
||||
|
||||
// Resize vector to appropriate size
|
||||
vec.resize(shape[0]);
|
||||
|
||||
// Read data into vector
|
||||
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, vec.data(), indep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, std::vector<T>& vec, bool indep=false)
|
||||
{
|
||||
hid_t dset = open_dataset(obj_id, name);
|
||||
read_dataset(dset, vec, indep);
|
||||
close_dataset(dset);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t dset, xt::xarray<T>& arr, bool indep=false)
|
||||
{
|
||||
// Get shape of dataset
|
||||
std::vector<hsize_t> shape = object_shape(dset);
|
||||
|
||||
// Allocate new array to read data into
|
||||
std::size_t size = 1;
|
||||
for (const auto x : shape)
|
||||
size *= x;
|
||||
T* buffer = new T[size];
|
||||
|
||||
// Read data from attribute
|
||||
read_dataset(dset, nullptr, H5TypeMap<T>::type_id, buffer, indep);
|
||||
|
||||
// Adapt into xarray
|
||||
arr = xt::adapt(buffer, size, xt::acquire_ownership(), shape);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void read_dataset(hid_t obj_id, const char* name, xt::xarray<T>& arr, bool indep=false)
|
||||
{
|
||||
// Open dataset and read array
|
||||
hid_t dset = open_dataset(obj_id, name);
|
||||
read_dataset(dset, arr, indep);
|
||||
close_dataset(dset);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for write_attribute
|
||||
//==============================================================================
|
||||
|
||||
template<typename T> inline void
|
||||
|
|
@ -149,8 +298,8 @@ write_attribute(hid_t obj_id, const char* name, T buffer)
|
|||
write_attr(obj_id, name, 0, nullptr, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
write_attribute<const char*>(hid_t obj_id, const char* name, const char* buffer)
|
||||
inline void
|
||||
write_attribute(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
write_attr_string(obj_id, name, buffer);
|
||||
}
|
||||
|
|
@ -162,14 +311,18 @@ write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
|||
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Templates/overloads for write_dataset
|
||||
//==============================================================================
|
||||
|
||||
template<typename T> inline void
|
||||
write_dataset(hid_t obj_id, const char* name, T buffer)
|
||||
{
|
||||
write_dataset(obj_id, 0, nullptr, name, H5TypeMap<T>::type_id, &buffer, false);
|
||||
}
|
||||
|
||||
template<> inline void
|
||||
write_dataset<const char*>(hid_t obj_id, const char* name, const char* buffer)
|
||||
inline void
|
||||
write_dataset(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
write_string(obj_id, name, buffer, false);
|
||||
}
|
||||
|
|
@ -181,5 +334,12 @@ write_dataset(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
|||
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
|
||||
}
|
||||
|
||||
inline void
|
||||
write_dataset(hid_t obj_id, const char* name, Position r)
|
||||
{
|
||||
std::array<double, 3> buffer {r.x, r.y, r.z};
|
||||
write_dataset(obj_id, name, buffer);
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
#endif //HDF5_INTERFACE_H
|
||||
#endif // OPENMC_HDF5_INTERFACE_H
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ module input_xml
|
|||
save
|
||||
|
||||
interface
|
||||
subroutine adjust_indices_c() bind(C)
|
||||
end subroutine adjust_indices_c
|
||||
subroutine adjust_indices() bind(C)
|
||||
end subroutine adjust_indices
|
||||
|
||||
subroutine allocate_offset_tables(n_maps) bind(C)
|
||||
import C_INT
|
||||
|
|
@ -87,6 +87,11 @@ module input_xml
|
|||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_settings
|
||||
|
||||
subroutine read_materials(node_ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_materials
|
||||
|
||||
function find_root_universe() bind(C) result(root)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T) :: root
|
||||
|
|
@ -1051,7 +1056,7 @@ contains
|
|||
allocate(surfaces(n_surfaces))
|
||||
|
||||
do i = 1, n_surfaces
|
||||
surfaces(i) % ptr = surface_pointer_c(i - 1);
|
||||
surfaces(i) % ptr = surface_pointer(i - 1);
|
||||
|
||||
if (surfaces(i) % bc() /= BC_TRANSMIT) boundary_exists = .true.
|
||||
|
||||
|
|
@ -1095,7 +1100,7 @@ contains
|
|||
do i = 1, n_cells
|
||||
c => cells(i)
|
||||
|
||||
c % ptr = cell_pointer_c(i - 1)
|
||||
c % ptr = cell_pointer(i - 1)
|
||||
|
||||
! Initialize distribcell instances and distribcell index
|
||||
c % distribcell_index = NONE
|
||||
|
|
@ -1109,42 +1114,6 @@ contains
|
|||
// to_str(c % id()))
|
||||
end if
|
||||
|
||||
! Read material
|
||||
if (check_for_node(node_cell, "material")) then
|
||||
n_mats = node_word_count(node_cell, "material")
|
||||
|
||||
if (n_mats > 0) then
|
||||
allocate(sarray(n_mats))
|
||||
call get_node_array(node_cell, "material", sarray)
|
||||
|
||||
allocate(c % material(n_mats))
|
||||
do j = 1, n_mats
|
||||
select case(trim(to_lower(sarray(j))))
|
||||
case ('void')
|
||||
c % material(j) = MATERIAL_VOID
|
||||
case default
|
||||
c % material(j) = int(str_to_int(sarray(j)), 4)
|
||||
|
||||
! Check for error
|
||||
if (c % material(j) == ERROR_INT) then
|
||||
call fatal_error("Invalid material specified on cell " &
|
||||
// to_str(c % id()))
|
||||
end if
|
||||
end select
|
||||
end do
|
||||
|
||||
deallocate(sarray)
|
||||
|
||||
else
|
||||
allocate(c % material(1))
|
||||
c % material(1) = NONE
|
||||
end if
|
||||
|
||||
else
|
||||
allocate(c % material(1))
|
||||
c % material(1) = NONE
|
||||
end if
|
||||
|
||||
! Check for region specification (also under deprecated name surfaces)
|
||||
if (check_for_node(node_cell, "surfaces")) then
|
||||
call warning("The use of 'surfaces' is deprecated and will be &
|
||||
|
|
@ -1246,7 +1215,7 @@ contains
|
|||
n = node_word_count(node_cell, "temperature")
|
||||
if (n > 0) then
|
||||
! Make sure this is a "normal" cell.
|
||||
if (c % material(1) == NONE) call fatal_error("Cell " &
|
||||
if (c % fill() /= C_NONE) call fatal_error("Cell " &
|
||||
// trim(to_str(c % id())) // " was specified with a temperature &
|
||||
&but no material. Temperature specification is only valid for &
|
||||
&cells filled with a material.")
|
||||
|
|
@ -1309,7 +1278,7 @@ contains
|
|||
RECT_LATTICES: do i = 1, n_rlats
|
||||
allocate(RectLattice::lattices(i) % obj)
|
||||
lat => lattices(i) % obj
|
||||
lat % ptr = lattice_pointer_c(i - 1)
|
||||
lat % ptr = lattice_pointer(i - 1)
|
||||
select type(lat)
|
||||
type is (RectLattice)
|
||||
|
||||
|
|
@ -1325,7 +1294,7 @@ contains
|
|||
HEX_LATTICES: do i = 1, n_hlats
|
||||
allocate(HexLattice::lattices(n_rlats + i) % obj)
|
||||
lat => lattices(n_rlats + i) % obj
|
||||
lat % ptr = lattice_pointer_c(n_rlats + i - 1)
|
||||
lat % ptr = lattice_pointer(n_rlats + i - 1)
|
||||
select type (lat)
|
||||
type is (HexLattice)
|
||||
|
||||
|
|
@ -1540,10 +1509,12 @@ contains
|
|||
call doc % load_file(filename)
|
||||
root = doc % document_element()
|
||||
|
||||
call read_materials(root % ptr)
|
||||
|
||||
! Get pointer to list of XML <material>
|
||||
call get_node_list(root, "material", node_mat_list)
|
||||
|
||||
! Allocate cells array
|
||||
! Allocate materials array
|
||||
n_materials = size(node_mat_list)
|
||||
allocate(materials(n_materials))
|
||||
allocate(material_temps(n_materials))
|
||||
|
|
@ -1556,27 +1527,14 @@ contains
|
|||
do i = 1, n_materials
|
||||
mat => materials(i)
|
||||
|
||||
mat % ptr = material_pointer(i - 1)
|
||||
|
||||
! Get pointer to i-th material node
|
||||
node_mat = node_mat_list(i)
|
||||
|
||||
! Copy material id
|
||||
if (check_for_node(node_mat, "id")) then
|
||||
call get_node_value(node_mat, "id", mat % id)
|
||||
else
|
||||
call fatal_error("Must specify id of material in materials XML file")
|
||||
end if
|
||||
|
||||
! Check if material is depletable
|
||||
if (check_for_node(node_mat, "depletable")) then
|
||||
call get_node_value(node_mat, "depletable", temp_str)
|
||||
if (to_lower(temp_str) == "true" .or. temp_str == "1") &
|
||||
mat % depletable = .true.
|
||||
end if
|
||||
|
||||
! Check to make sure 'id' hasn't been used
|
||||
if (material_dict % has(mat % id)) then
|
||||
call fatal_error("Two or more materials use the same unique ID: " &
|
||||
// to_str(mat % id))
|
||||
call get_node_value(node_mat, "depletable", mat % depletable)
|
||||
end if
|
||||
|
||||
! Copy material name
|
||||
|
|
@ -1596,7 +1554,7 @@ contains
|
|||
node_dens = node_mat % child("density")
|
||||
else
|
||||
call fatal_error("Must specify density element in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Copy units
|
||||
|
|
@ -1626,7 +1584,7 @@ contains
|
|||
sum_density = .false.
|
||||
if (val <= ZERO) then
|
||||
call fatal_error("Need to specify a positive density on material " &
|
||||
// trim(to_str(mat % id)) // ".")
|
||||
// trim(to_str(mat % id())) // ".")
|
||||
end if
|
||||
|
||||
! Adjust material density based on specified units
|
||||
|
|
@ -1641,7 +1599,7 @@ contains
|
|||
mat % density = 1.0e-24_8 * val
|
||||
case default
|
||||
call fatal_error("Unkwown units '" // trim(units) &
|
||||
// "' specified on material " // trim(to_str(mat % id)))
|
||||
// "' specified on material " // trim(to_str(mat % id())))
|
||||
end select
|
||||
end if
|
||||
|
||||
|
|
@ -1650,7 +1608,7 @@ contains
|
|||
|
||||
if (size(node_ele_list) > 0) then
|
||||
call fatal_error("Unable to add an element to material " &
|
||||
// trim(to_str(mat % id)) // " since the element option has &
|
||||
// trim(to_str(mat % id())) // " since the element option has &
|
||||
&been removed from the xml input. Elements can only be added via &
|
||||
&the Python API, which will expand elements into their natural &
|
||||
&nuclides.")
|
||||
|
|
@ -1663,7 +1621,7 @@ contains
|
|||
if (.not. check_for_node(node_mat, "nuclide") .and. &
|
||||
.not. check_for_node(node_mat, "macroscopic")) then
|
||||
call fatal_error("No macroscopic data or nuclides specified on &
|
||||
&material " // trim(to_str(mat % id)))
|
||||
&material " // trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Create list of macroscopic x/s based on those specified, just treat
|
||||
|
|
@ -1677,7 +1635,7 @@ contains
|
|||
& mode!")
|
||||
else if (size(node_macro_list) > 1) then
|
||||
call fatal_error("Only one macroscopic object permitted per material, " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
else if (size(node_macro_list) == 1) then
|
||||
|
||||
node_nuc = node_macro_list(1)
|
||||
|
|
@ -1685,7 +1643,7 @@ contains
|
|||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on macroscopic data in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
|
|
@ -1715,7 +1673,7 @@ contains
|
|||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on nuclide in material " &
|
||||
// trim(to_str(mat % id)))
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
|
|
@ -1854,7 +1812,7 @@ contains
|
|||
if (.not. (all(mat % atom_density >= ZERO) .or. &
|
||||
all(mat % atom_density <= ZERO))) then
|
||||
call fatal_error("Cannot mix atom and weight percents in material " &
|
||||
// to_str(mat % id))
|
||||
// to_str(mat % id()))
|
||||
end if
|
||||
|
||||
! Determine density if it is a sum value
|
||||
|
|
@ -1935,7 +1893,7 @@ contains
|
|||
end if
|
||||
|
||||
! Add material to dictionary
|
||||
call material_dict % set(mat % id, i)
|
||||
call material_dict % set(mat % id(), i)
|
||||
end do
|
||||
|
||||
! Set total number of nuclides and S(a,b) tables
|
||||
|
|
@ -2159,7 +2117,7 @@ contains
|
|||
|
||||
READ_TALLIES: do i = 1, n
|
||||
! Allocate tally
|
||||
err = openmc_tally_set_type(i_start + i - 1, &
|
||||
err = openmc_tally_allocate(i_start + i - 1, &
|
||||
C_CHAR_'generic' // C_NULL_CHAR)
|
||||
|
||||
! Get pointer to tally
|
||||
|
|
@ -2168,19 +2126,15 @@ contains
|
|||
! Get pointer to tally xml node
|
||||
node_tal = node_tal_list(i)
|
||||
|
||||
! Copy tally id
|
||||
! Copy and set tally id
|
||||
if (check_for_node(node_tal, "id")) then
|
||||
call get_node_value(node_tal, "id", tally_id)
|
||||
err = openmc_tally_set_id(i_start + i - 1, tally_id)
|
||||
if (err /= 0) call fatal_error(to_f_string(openmc_err_msg))
|
||||
else
|
||||
call fatal_error("Must specify id for tally in tally XML file.")
|
||||
end if
|
||||
|
||||
! Check to make sure 'id' hasn't been used
|
||||
if (tally_dict % has(tally_id)) then
|
||||
call fatal_error("Two or more tallies use the same unique ID: " &
|
||||
// to_str(tally_id))
|
||||
end if
|
||||
|
||||
! Copy tally name
|
||||
if (check_for_node(node_tal, "name")) &
|
||||
call get_node_value(node_tal, "name", t % name)
|
||||
|
|
@ -2880,9 +2834,6 @@ contains
|
|||
end select
|
||||
end if
|
||||
|
||||
! Set tally id
|
||||
err = openmc_tally_set_id(i_start + i - 1, tally_id)
|
||||
|
||||
end associate
|
||||
end do READ_TALLIES
|
||||
|
||||
|
|
@ -3255,7 +3206,7 @@ contains
|
|||
! Check if the specified tally mesh exists
|
||||
if (mesh_dict % has(meshid)) then
|
||||
pl % meshlines_mesh => meshes(mesh_dict % get(meshid))
|
||||
if (meshes(meshid) % type /= LATTICE_RECT) then
|
||||
if (meshes(meshid) % type /= MESH_REGULAR) then
|
||||
call fatal_error("Non-rectangular mesh specified in &
|
||||
&meshlines for plot " // trim(to_str(pl % id)))
|
||||
end if
|
||||
|
|
@ -3855,15 +3806,15 @@ contains
|
|||
|
||||
do i = 1, n_cells
|
||||
! Ignore non-normal cells and cells with defined temperature.
|
||||
if (cells(i) % material(1) == NONE) cycle
|
||||
if (cells(i) % fill() /= C_NONE) cycle
|
||||
if (cells(i) % sqrtkT(1) >= ZERO) cycle
|
||||
|
||||
! Set the number of temperatures equal to the number of materials.
|
||||
deallocate(cells(i) % sqrtkT)
|
||||
allocate(cells(i) % sqrtkT(size(cells(i) % material)))
|
||||
allocate(cells(i) % sqrtkT(cells(i) % material_size()))
|
||||
|
||||
! Check each of the cell materials for temperature data.
|
||||
do j = 1, size(cells(i) % material)
|
||||
do j = 1, cells(i) % material_size()
|
||||
! Arbitrarily set void regions to 0K.
|
||||
if (cells(i) % material(j) == MATERIAL_VOID) then
|
||||
cells(i) % sqrtkT(j) = ZERO
|
||||
|
|
@ -3931,45 +3882,6 @@ contains
|
|||
|
||||
end subroutine read_multipole_data
|
||||
|
||||
!===============================================================================
|
||||
! ADJUST_INDICES changes the values for 'surfaces' for each cell and the
|
||||
! material index assigned to each to the indices in the surfaces and material
|
||||
! array rather than the unique IDs assigned to each surface and material. Also
|
||||
! assigns boundary conditions to surfaces based on those read into the bc_dict
|
||||
! dictionary
|
||||
!===============================================================================
|
||||
|
||||
subroutine adjust_indices()
|
||||
|
||||
integer :: i ! index for various purposes
|
||||
integer :: j ! index for various purposes
|
||||
integer :: id ! user-specified id
|
||||
|
||||
call adjust_indices_c()
|
||||
|
||||
do i = 1, n_cells
|
||||
associate (c => cells(i))
|
||||
|
||||
! =======================================================================
|
||||
! ADJUST MATERIAL/FILL POINTERS FOR EACH CELL
|
||||
|
||||
if (c % material(1) /= NONE) then
|
||||
do j = 1, size(c % material)
|
||||
id = c % material(j)
|
||||
if (id == MATERIAL_VOID) then
|
||||
else if (material_dict % has(id)) then
|
||||
c % material(j) = material_dict % get(id)
|
||||
else
|
||||
call fatal_error("Could not find material " // trim(to_str(id)) &
|
||||
// " specified on cell " // trim(to_str(c % id())))
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
|
||||
end subroutine adjust_indices
|
||||
|
||||
!===============================================================================
|
||||
! PREPARE_DISTRIBCELL initializes any distribcell filters present and sets the
|
||||
! offsets for distribcells
|
||||
|
|
@ -3993,7 +3905,7 @@ contains
|
|||
|
||||
! Find all cells with multiple (distributed) materials or temperatures.
|
||||
do i = 1, n_cells
|
||||
if (size(cells(i) % material) > 1 .or. size(cells(i) % sqrtkT) > 1) then
|
||||
if (cells(i) % material_size() > 1 .or. size(cells(i) % sqrtkT) > 1) then
|
||||
call cell_list % add(i)
|
||||
end if
|
||||
end do
|
||||
|
|
@ -4002,10 +3914,10 @@ contains
|
|||
! number of respective cell instances.
|
||||
do i = 1, n_cells
|
||||
associate (c => cells(i))
|
||||
if (size(c % material) > 1) then
|
||||
if (size(c % material) /= c % n_instances()) then
|
||||
if (c % material_size() > 1) then
|
||||
if (c % material_size() /= c % n_instances()) then
|
||||
call fatal_error("Cell " // trim(to_str(c % id())) // " was &
|
||||
&specified with " // trim(to_str(size(c % material))) &
|
||||
&specified with " // trim(to_str(c % material_size())) &
|
||||
// " materials but has " // trim(to_str(c % n_instances())) &
|
||||
// " distributed instances. The number of materials must &
|
||||
&equal one or the number of instances.")
|
||||
|
|
|
|||
184
src/lattice.cpp
184
src/lattice.cpp
|
|
@ -29,7 +29,7 @@ std::unordered_map<int32_t, int32_t> lattice_map;
|
|||
Lattice::Lattice(pugi::xml_node lat_node)
|
||||
{
|
||||
if (check_for_node(lat_node, "id")) {
|
||||
id = stoi(get_node_value(lat_node, "id"));
|
||||
id = std::stoi(get_node_value(lat_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of lattice in geometry XML file.");
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ Lattice::Lattice(pugi::xml_node lat_node)
|
|||
}
|
||||
|
||||
if (check_for_node(lat_node, "outer")) {
|
||||
outer = stoi(get_node_value(lat_node, "outer"));
|
||||
outer = std::stoi(get_node_value(lat_node, "outer"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -141,14 +141,14 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
|
|||
std::string dimension_str {get_node_value(lat_node, "dimension")};
|
||||
std::vector<std::string> dimension_words {split(dimension_str)};
|
||||
if (dimension_words.size() == 2) {
|
||||
n_cells[0] = stoi(dimension_words[0]);
|
||||
n_cells[1] = stoi(dimension_words[1]);
|
||||
n_cells[0] = std::stoi(dimension_words[0]);
|
||||
n_cells[1] = std::stoi(dimension_words[1]);
|
||||
n_cells[2] = 1;
|
||||
is_3d = false;
|
||||
} else if (dimension_words.size() == 3) {
|
||||
n_cells[0] = stoi(dimension_words[0]);
|
||||
n_cells[1] = stoi(dimension_words[1]);
|
||||
n_cells[2] = stoi(dimension_words[2]);
|
||||
n_cells[0] = std::stoi(dimension_words[0]);
|
||||
n_cells[1] = std::stoi(dimension_words[1]);
|
||||
n_cells[2] = std::stoi(dimension_words[2]);
|
||||
is_3d = true;
|
||||
} else {
|
||||
fatal_error("Rectangular lattice must be two or three dimensions.");
|
||||
|
|
@ -195,7 +195,7 @@ RectLattice::RectLattice(pugi::xml_node lat_node)
|
|||
for (int ix = 0; ix < nx; ix++) {
|
||||
int indx1 = nx*ny*iz + nx*(ny-iy-1) + ix;
|
||||
int indx2 = nx*ny*iz + nx*iy + ix;
|
||||
universes[indx1] = stoi(univ_words[indx2]);
|
||||
universes[indx1] = std::stoi(univ_words[indx2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,26 +223,23 @@ RectLattice::are_valid_indices(const int i_xyz[3]) const
|
|||
//==============================================================================
|
||||
|
||||
std::pair<double, std::array<int, 3>>
|
||||
RectLattice::distance(const double xyz[3], const double uvw[3],
|
||||
const int i_xyz[3]) const
|
||||
RectLattice::distance(Position r, Direction u, const int i_xyz[3]) const
|
||||
{
|
||||
// Get short aliases to the coordinates.
|
||||
double x {xyz[0]};
|
||||
double y {xyz[1]};
|
||||
double z {xyz[2]};
|
||||
double u {uvw[0]};
|
||||
double v {uvw[1]};
|
||||
double x = r.x;
|
||||
double y = r.y;
|
||||
double z = r.z;
|
||||
|
||||
// Determine the oncoming edge.
|
||||
double x0 {copysign(0.5 * pitch[0], u)};
|
||||
double y0 {copysign(0.5 * pitch[1], v)};
|
||||
double x0 {copysign(0.5 * pitch[0], u.x)};
|
||||
double y0 {copysign(0.5 * pitch[1], u.y)};
|
||||
|
||||
// Left and right sides
|
||||
double d {INFTY};
|
||||
std::array<int, 3> lattice_trans;
|
||||
if ((std::abs(x - x0) > FP_PRECISION) && u != 0) {
|
||||
d = (x0 - x) / u;
|
||||
if (u > 0) {
|
||||
if ((std::abs(x - x0) > FP_PRECISION) && u.x != 0) {
|
||||
d = (x0 - x) / u.x;
|
||||
if (u.x > 0) {
|
||||
lattice_trans = {1, 0, 0};
|
||||
} else {
|
||||
lattice_trans = {-1, 0, 0};
|
||||
|
|
@ -250,11 +247,11 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
|
|||
}
|
||||
|
||||
// Front and back sides
|
||||
if ((std::abs(y - y0) > FP_PRECISION) && v != 0) {
|
||||
double this_d = (y0 - y) / v;
|
||||
if ((std::abs(y - y0) > FP_PRECISION) && u.y != 0) {
|
||||
double this_d = (y0 - y) / u.y;
|
||||
if (this_d < d) {
|
||||
d = this_d;
|
||||
if (v > 0) {
|
||||
if (u.y > 0) {
|
||||
lattice_trans = {0, 1, 0};
|
||||
} else {
|
||||
lattice_trans = {0, -1, 0};
|
||||
|
|
@ -264,13 +261,12 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
|
|||
|
||||
// Top and bottom sides
|
||||
if (is_3d) {
|
||||
double w {uvw[2]};
|
||||
double z0 {copysign(0.5 * pitch[2], w)};
|
||||
if ((std::abs(z - z0) > FP_PRECISION) && w != 0) {
|
||||
double this_d = (z0 - z) / w;
|
||||
double z0 {copysign(0.5 * pitch[2], u.z)};
|
||||
if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
|
||||
double this_d = (z0 - z) / u.z;
|
||||
if (this_d < d) {
|
||||
d = this_d;
|
||||
if (w > 0) {
|
||||
if (u.z > 0) {
|
||||
lattice_trans = {0, 0, 1};
|
||||
} else {
|
||||
lattice_trans = {0, 0, -1};
|
||||
|
|
@ -285,13 +281,13 @@ RectLattice::distance(const double xyz[3], const double uvw[3],
|
|||
//==============================================================================
|
||||
|
||||
std::array<int, 3>
|
||||
RectLattice::get_indices(const double xyz[3]) const
|
||||
RectLattice::get_indices(Position r) const
|
||||
{
|
||||
int ix {static_cast<int>(std::ceil((xyz[0] - lower_left[0]) / pitch[0]))-1};
|
||||
int iy {static_cast<int>(std::ceil((xyz[1] - lower_left[1]) / pitch[1]))-1};
|
||||
int ix {static_cast<int>(std::ceil((r.x - lower_left.x) / pitch.x))-1};
|
||||
int iy {static_cast<int>(std::ceil((r.y - lower_left.y) / pitch.y))-1};
|
||||
int iz;
|
||||
if (is_3d) {
|
||||
iz = static_cast<int>(std::ceil((xyz[2] - lower_left[2]) / pitch[2]))-1;
|
||||
iz = static_cast<int>(std::ceil((r.z - lower_left.z) / pitch.z))-1;
|
||||
} else {
|
||||
iz = 0;
|
||||
}
|
||||
|
|
@ -300,18 +296,15 @@ RectLattice::get_indices(const double xyz[3]) const
|
|||
|
||||
//==============================================================================
|
||||
|
||||
std::array<double, 3>
|
||||
RectLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const
|
||||
Position
|
||||
RectLattice::get_local_position(Position r, const int i_xyz[3]) const
|
||||
{
|
||||
std::array<double, 3> local_xyz;
|
||||
local_xyz[0] = global_xyz[0] - (lower_left[0] + (i_xyz[0] + 0.5)*pitch[0]);
|
||||
local_xyz[1] = global_xyz[1] - (lower_left[1] + (i_xyz[1] + 0.5)*pitch[1]);
|
||||
r.x -= (lower_left.x + (i_xyz[0] + 0.5)*pitch.x);
|
||||
r.y -= (lower_left.y + (i_xyz[1] + 0.5)*pitch.y);
|
||||
if (is_3d) {
|
||||
local_xyz[2] = global_xyz[2] - (lower_left[2] + (i_xyz[2] + 0.5)*pitch[2]);
|
||||
} else {
|
||||
local_xyz[2] = global_xyz[2];
|
||||
r.z -= (lower_left.z + (i_xyz[2] + 0.5)*pitch.z);
|
||||
}
|
||||
return local_xyz;
|
||||
return r;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -407,9 +400,9 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
: Lattice {lat_node}
|
||||
{
|
||||
// Read the number of lattice cells in each dimension.
|
||||
n_rings = stoi(get_node_value(lat_node, "n_rings"));
|
||||
n_rings = std::stoi(get_node_value(lat_node, "n_rings"));
|
||||
if (check_for_node(lat_node, "n_axial")) {
|
||||
n_axial = stoi(get_node_value(lat_node, "n_axial"));
|
||||
n_axial = std::stoi(get_node_value(lat_node, "n_axial"));
|
||||
is_3d = true;
|
||||
} else {
|
||||
n_axial = 1;
|
||||
|
|
@ -483,7 +476,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
@ -512,7 +505,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
@ -535,7 +528,7 @@ HexLattice::HexLattice(pugi::xml_node lat_node)
|
|||
int indx = (2*n_rings-1)*(2*n_rings-1) * m
|
||||
+ (2*n_rings-1) * (i_a+n_rings-1)
|
||||
+ (i_x+n_rings-1);
|
||||
universes[indx] = stoi(univ_words[input_index]);
|
||||
universes[indx] = std::stoi(univ_words[input_index]);
|
||||
input_index++;
|
||||
// Walk the index to the right neighbor (which is not adjacent).
|
||||
i_x += 2;
|
||||
|
|
@ -583,12 +576,11 @@ HexLattice::are_valid_indices(const int i_xyz[3]) const
|
|||
//==============================================================================
|
||||
|
||||
std::pair<double, std::array<int, 3>>
|
||||
HexLattice::distance(const double xyz[3], const double uvw[3],
|
||||
const int i_xyz[3]) const
|
||||
HexLattice::distance(Position r, Direction u, const int i_xyz[3]) const
|
||||
{
|
||||
// Compute the direction on the hexagonal basis.
|
||||
double beta_dir = uvw[0] * std::sqrt(3.0) / 2.0 + uvw[1] / 2.0;
|
||||
double gamma_dir = uvw[0] * std::sqrt(3.0) / 2.0 - uvw[1] / 2.0;
|
||||
double beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0;
|
||||
double gamma_dir = u.x * std::sqrt(3.0) / 2.0 - u.y / 2.0;
|
||||
|
||||
// Note that hexagonal lattice distance calculations are performed
|
||||
// using the particle's coordinates relative to the neighbor lattice
|
||||
|
|
@ -600,15 +592,15 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
|
|||
double d {INFTY};
|
||||
std::array<int, 3> lattice_trans;
|
||||
double edge = -copysign(0.5*pitch[0], beta_dir); // Oncoming edge
|
||||
std::array<double, 3> xyz_t;
|
||||
Position r_t;
|
||||
if (beta_dir > 0) {
|
||||
const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1], i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
} else {
|
||||
const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1], i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
}
|
||||
double beta = xyz_t[0] * std::sqrt(3.0) / 2.0 + xyz_t[1] / 2.0;
|
||||
double beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0;
|
||||
if ((std::abs(beta - edge) > FP_PRECISION) && beta_dir != 0) {
|
||||
d = (edge - beta) / beta_dir;
|
||||
if (beta_dir > 0) {
|
||||
|
|
@ -622,12 +614,12 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
|
|||
edge = -copysign(0.5*pitch[0], gamma_dir);
|
||||
if (gamma_dir > 0) {
|
||||
const int i_xyz_t[3] {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
} else {
|
||||
const int i_xyz_t[3] {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
}
|
||||
double gamma = xyz_t[0] * std::sqrt(3.0) / 2.0 - xyz_t[1] / 2.0;
|
||||
double gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0;
|
||||
if ((std::abs(gamma - edge) > FP_PRECISION) && gamma_dir != 0) {
|
||||
double this_d = (edge - gamma) / gamma_dir;
|
||||
if (this_d < d) {
|
||||
|
|
@ -641,18 +633,18 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
|
|||
}
|
||||
|
||||
// Upper and lower sides.
|
||||
edge = -copysign(0.5*pitch[0], uvw[1]);
|
||||
if (uvw[1] > 0) {
|
||||
edge = -copysign(0.5*pitch[0], u.y);
|
||||
if (u.y > 0) {
|
||||
const int i_xyz_t[3] {i_xyz[0], i_xyz[1]+1, i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
} else {
|
||||
const int i_xyz_t[3] {i_xyz[0], i_xyz[1]-1, i_xyz[2]};
|
||||
xyz_t = get_local_xyz(xyz, i_xyz_t);
|
||||
r_t = get_local_position(r, i_xyz_t);
|
||||
}
|
||||
if ((std::abs(xyz_t[1] - edge) > FP_PRECISION) && uvw[1] != 0) {
|
||||
double this_d = (edge - xyz_t[1]) / uvw[1];
|
||||
if ((std::abs(r_t.y - edge) > FP_PRECISION) && u.y != 0) {
|
||||
double this_d = (edge - r_t.y) / u.y;
|
||||
if (this_d < d) {
|
||||
if (uvw[1] > 0) {
|
||||
if (u.y > 0) {
|
||||
lattice_trans = {0, 1, 0};
|
||||
} else {
|
||||
lattice_trans = {0, -1, 0};
|
||||
|
|
@ -663,14 +655,13 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
|
|||
|
||||
// Top and bottom sides
|
||||
if (is_3d) {
|
||||
double z {xyz[2]};
|
||||
double w {uvw[2]};
|
||||
double z0 {copysign(0.5 * pitch[1], w)};
|
||||
if ((std::abs(z - z0) > FP_PRECISION) && w != 0) {
|
||||
double this_d = (z0 - z) / w;
|
||||
double z = r.z;
|
||||
double z0 {copysign(0.5 * pitch[1], u.z)};
|
||||
if ((std::abs(z - z0) > FP_PRECISION) && u.z != 0) {
|
||||
double this_d = (z0 - z) / u.z;
|
||||
if (this_d < d) {
|
||||
d = this_d;
|
||||
if (w > 0) {
|
||||
if (u.z > 0) {
|
||||
lattice_trans = {0, 0, 1};
|
||||
} else {
|
||||
lattice_trans = {0, 0, -1};
|
||||
|
|
@ -686,24 +677,24 @@ HexLattice::distance(const double xyz[3], const double uvw[3],
|
|||
//==============================================================================
|
||||
|
||||
std::array<int, 3>
|
||||
HexLattice::get_indices(const double xyz[3]) const
|
||||
HexLattice::get_indices(Position r) const
|
||||
{
|
||||
// Offset the xyz by the lattice center.
|
||||
double xyz_o[3] {xyz[0] - center[0], xyz[1] - center[1], xyz[2]};
|
||||
if (is_3d) {xyz_o[2] -= center[2];}
|
||||
Position r_o {r.x - center.x, r.y - center.y, r.z};
|
||||
if (is_3d) {r_o.z -= center.z;}
|
||||
|
||||
// Index the z direction.
|
||||
std::array<int, 3> out;
|
||||
if (is_3d) {
|
||||
out[2] = static_cast<int>(std::ceil(xyz_o[2] / pitch[1] + 0.5 * n_axial))-1;
|
||||
out[2] = static_cast<int>(std::ceil(r_o.z / pitch[1] + 0.5 * n_axial))-1;
|
||||
} else {
|
||||
out[2] = 0;
|
||||
}
|
||||
|
||||
// Convert coordinates into skewed bases. The (x, alpha) basis is used to
|
||||
// find the index of the global coordinates to within 4 cells.
|
||||
double alpha = xyz_o[1] - xyz_o[0] / std::sqrt(3.0);
|
||||
out[0] = static_cast<int>(std::floor(xyz_o[0]
|
||||
double alpha = r_o.y - r_o.x / std::sqrt(3.0);
|
||||
out[0] = static_cast<int>(std::floor(r_o.x
|
||||
/ (0.5*std::sqrt(3.0) * pitch[0])));
|
||||
out[1] = static_cast<int>(std::floor(alpha / pitch[0]));
|
||||
|
||||
|
|
@ -725,8 +716,8 @@ HexLattice::get_indices(const double xyz[3]) const
|
|||
for (int i = 0; i < 2; i++) {
|
||||
for (int j = 0; j < 2; j++) {
|
||||
int i_xyz[3] {out[0] + j, out[1] + i, 0};
|
||||
std::array<double, 3> xyz_t = get_local_xyz(xyz, i_xyz);
|
||||
double d = xyz_t[0]*xyz_t[0] + xyz_t[1]*xyz_t[1];
|
||||
Position r_t = get_local_position(r, i_xyz);
|
||||
double d = r_t.x*r_t.x + r_t.y*r_t.y;
|
||||
if (d < d_min) {
|
||||
d_min = d;
|
||||
k_min = k;
|
||||
|
|
@ -751,26 +742,19 @@ HexLattice::get_indices(const double xyz[3]) const
|
|||
|
||||
//==============================================================================
|
||||
|
||||
std::array<double, 3>
|
||||
HexLattice::get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const
|
||||
Position
|
||||
HexLattice::get_local_position(Position r, const int i_xyz[3]) const
|
||||
{
|
||||
std::array<double, 3> local_xyz;
|
||||
|
||||
// x_l = x_g - (center + pitch_x*cos(30)*index_x)
|
||||
local_xyz[0] = global_xyz[0] - (center[0]
|
||||
+ std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]);
|
||||
r.x -= (center.x + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings + 1) * pitch[0]);
|
||||
// y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y)
|
||||
local_xyz[1] = global_xyz[1] - (center[1]
|
||||
+ (i_xyz[1] - n_rings + 1) * pitch[0]
|
||||
+ (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0);
|
||||
r.y -= (center.y + (i_xyz[1] - n_rings + 1) * pitch[0]
|
||||
+ (i_xyz[0] - n_rings + 1) * pitch[0] / 2.0);
|
||||
if (is_3d) {
|
||||
local_xyz[2] = global_xyz[2] - center[2]
|
||||
+ (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1];
|
||||
} else {
|
||||
local_xyz[2] = global_xyz[2];
|
||||
r.z -= center.z - (0.5 * n_axial - i_xyz[2] - 0.5) * pitch[1];
|
||||
}
|
||||
|
||||
return local_xyz;
|
||||
return r;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -908,7 +892,9 @@ extern "C" {
|
|||
void lattice_distance(Lattice *lat, const double xyz[3], const double uvw[3],
|
||||
const int i_xyz[3], double *d, int lattice_trans[3])
|
||||
{
|
||||
std::pair<double, std::array<int, 3>> ld {lat->distance(xyz, uvw, i_xyz)};
|
||||
Position r {xyz};
|
||||
Direction u {uvw};
|
||||
std::pair<double, std::array<int, 3>> ld {lat->distance(r, u, i_xyz)};
|
||||
*d = ld.first;
|
||||
lattice_trans[0] = ld.second[0];
|
||||
lattice_trans[1] = ld.second[1];
|
||||
|
|
@ -917,7 +903,8 @@ extern "C" {
|
|||
|
||||
void lattice_get_indices(Lattice *lat, const double xyz[3], int i_xyz[3])
|
||||
{
|
||||
std::array<int, 3> inds = lat->get_indices(xyz);
|
||||
Position r {xyz};
|
||||
std::array<int, 3> inds = lat->get_indices(r);
|
||||
i_xyz[0] = inds[0];
|
||||
i_xyz[1] = inds[1];
|
||||
i_xyz[2] = inds[2];
|
||||
|
|
@ -926,10 +913,11 @@ extern "C" {
|
|||
void lattice_get_local_xyz(Lattice *lat, const double global_xyz[3],
|
||||
const int i_xyz[3], double local_xyz[3])
|
||||
{
|
||||
std::array<double, 3> xyz = lat->get_local_xyz(global_xyz, i_xyz);
|
||||
local_xyz[0] = xyz[0];
|
||||
local_xyz[1] = xyz[1];
|
||||
local_xyz[2] = xyz[2];
|
||||
Position global {global_xyz};
|
||||
Position local = lat->get_local_position(global, i_xyz);
|
||||
local_xyz[0] = local.x;
|
||||
local_xyz[1] = local.y;
|
||||
local_xyz[2] = local.z;
|
||||
}
|
||||
|
||||
int32_t lattice_offset(Lattice *lat, int map, const int i_xyz[3])
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef LATTICE_H
|
||||
#define LATTICE_H
|
||||
#ifndef OPENMC_LATTICE_H
|
||||
#define OPENMC_LATTICE_H
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
|
@ -7,10 +7,12 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "constants.h"
|
||||
#include "hdf5.h"
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include "constants.h"
|
||||
#include "position.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -69,54 +71,54 @@ public:
|
|||
int32_t fill_offset_table(int32_t offset, int32_t target_univ_id, int map);
|
||||
|
||||
//! \brief Check lattice indices.
|
||||
//! @param i_xyz[3] The indices for a lattice tile.
|
||||
//! @return true if the given indices fit within the lattice bounds. False
|
||||
//! \param i_xyz[3] The indices for a lattice tile.
|
||||
//! \return true if the given indices fit within the lattice bounds. False
|
||||
//! otherwise.
|
||||
virtual bool are_valid_indices(const int i_xyz[3]) const = 0;
|
||||
|
||||
//! \brief Find the next lattice surface crossing
|
||||
//! @param xyz[3] A 3D Cartesian coordinate.
|
||||
//! @param uvw[3] A 3D Cartesian direction.
|
||||
//! @param i_xyz[3] The indices for a lattice tile.
|
||||
//! @return The distance to the next crossing and an array indicating how the
|
||||
//! \param r A 3D Cartesian coordinate.
|
||||
//! \param u A 3D Cartesian direction.
|
||||
//! \param i_xyz[3] The indices for a lattice tile.
|
||||
//! \return The distance to the next crossing and an array indicating how the
|
||||
//! lattice indices would change after crossing that boundary.
|
||||
virtual std::pair<double, std::array<int, 3>>
|
||||
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const
|
||||
distance(Position r, Direction u, const int i_xyz[3]) const
|
||||
= 0;
|
||||
|
||||
//! \brief Find the lattice tile indices for a given point.
|
||||
//! @param xyz[3] A 3D Cartesian coordinate.
|
||||
//! @return An array containing the indices of a lattice tile.
|
||||
virtual std::array<int, 3> get_indices(const double xyz[3]) const = 0;
|
||||
//! \param r A 3D Cartesian coordinate.
|
||||
//! \return An array containing the indices of a lattice tile.
|
||||
virtual std::array<int, 3> get_indices(Position r) const = 0;
|
||||
|
||||
//! \brief Get coordinates local to a lattice tile.
|
||||
//! @param global_xyz[3] A 3D Cartesian coordinate.
|
||||
//! @param i_xyz[3] The indices for a lattice tile.
|
||||
//! @return Local 3D Cartesian coordinates.
|
||||
virtual std::array<double, 3>
|
||||
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const = 0;
|
||||
//! \param r A 3D Cartesian coordinate.
|
||||
//! \param i_xyz[3] The indices for a lattice tile.
|
||||
//! \return Local 3D Cartesian coordinates.
|
||||
virtual Position
|
||||
get_local_position(Position r, const int i_xyz[3]) const = 0;
|
||||
|
||||
//! \brief Check flattened lattice index.
|
||||
//! @param indx The index for a lattice tile.
|
||||
//! @return true if the given index fit within the lattice bounds. False
|
||||
//! \param indx The index for a lattice tile.
|
||||
//! \return true if the given index fit within the lattice bounds. False
|
||||
//! otherwise.
|
||||
virtual bool is_valid_index(int indx) const
|
||||
{return (indx >= 0) && (indx < universes.size());}
|
||||
|
||||
//! \brief Get the distribcell offset for a lattice tile.
|
||||
//! @param The map index for the target cell.
|
||||
//! @param i_xyz[3] The indices for a lattice tile.
|
||||
//! @return Distribcell offset i.e. the largest instance number for the target
|
||||
//! \param The map index for the target cell.
|
||||
//! \param i_xyz[3] The indices for a lattice tile.
|
||||
//! \return Distribcell offset i.e. the largest instance number for the target
|
||||
//! cell found in the geometry tree under this lattice tile.
|
||||
virtual int32_t& offset(int map, const int i_xyz[3]) = 0;
|
||||
|
||||
//! \brief Convert an array index to a useful human-readable string.
|
||||
//! @param indx The index for a lattice tile.
|
||||
//! @return A string representing the lattice tile.
|
||||
//! \param indx The index for a lattice tile.
|
||||
//! \return A string representing the lattice tile.
|
||||
virtual std::string index_to_string(int indx) const = 0;
|
||||
|
||||
//! \brief Write lattice information to an HDF5 group.
|
||||
//! @param group_id An HDF5 group id.
|
||||
//! \param group_id An HDF5 group id.
|
||||
void to_hdf5(hid_t group_id) const;
|
||||
|
||||
protected:
|
||||
|
|
@ -193,12 +195,12 @@ public:
|
|||
bool are_valid_indices(const int i_xyz[3]) const;
|
||||
|
||||
std::pair<double, std::array<int, 3>>
|
||||
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const;
|
||||
distance(Position r, Direction u, const int i_xyz[3]) const;
|
||||
|
||||
std::array<int, 3> get_indices(const double xyz[3]) const;
|
||||
std::array<int, 3> get_indices(Position r) const;
|
||||
|
||||
std::array<double, 3>
|
||||
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const;
|
||||
Position
|
||||
get_local_position(Position r, const int i_xyz[3]) const;
|
||||
|
||||
int32_t& offset(int map, const int i_xyz[3]);
|
||||
|
||||
|
|
@ -207,9 +209,9 @@ public:
|
|||
void to_hdf5_inner(hid_t group_id) const;
|
||||
|
||||
private:
|
||||
std::array<int, 3> n_cells; //!< Number of cells along each axis
|
||||
std::array<double, 3> lower_left; //!< Global lower-left corner of the lattice
|
||||
std::array<double, 3> pitch; //!< Lattice tile width along each axis
|
||||
std::array<int, 3> n_cells; //!< Number of cells along each axis
|
||||
Position lower_left; //!< Global lower-left corner of the lattice
|
||||
Position pitch; //!< Lattice tile width along each axis
|
||||
|
||||
// Convenience aliases
|
||||
int &nx {n_cells[0]};
|
||||
|
|
@ -233,12 +235,12 @@ public:
|
|||
bool are_valid_indices(const int i_xyz[3]) const;
|
||||
|
||||
std::pair<double, std::array<int, 3>>
|
||||
distance(const double xyz[3], const double uvw[3], const int i_xyz[3]) const;
|
||||
distance(Position r, Direction u, const int i_xyz[3]) const;
|
||||
|
||||
std::array<int, 3> get_indices(const double xyz[3]) const;
|
||||
std::array<int, 3> get_indices(Position r) const;
|
||||
|
||||
std::array<double, 3>
|
||||
get_local_xyz(const double global_xyz[3], const int i_xyz[3]) const;
|
||||
Position
|
||||
get_local_position(Position r, const int i_xyz[3]) const;
|
||||
|
||||
bool is_valid_index(int indx) const;
|
||||
|
||||
|
|
@ -251,9 +253,9 @@ public:
|
|||
private:
|
||||
int n_rings; //!< Number of radial tile positions
|
||||
int n_axial; //!< Number of axial tile positions
|
||||
std::array<double, 3> center; //!< Global center of lattice
|
||||
Position center; //!< Global center of lattice
|
||||
std::array<double, 2> pitch; //!< Lattice tile width and height
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
#endif // LATTICE_H
|
||||
#endif // OPENMC_LATTICE_H
|
||||
|
|
|
|||
137
src/material.cpp
Normal file
137
src/material.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "material.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "error.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
std::vector<Material*> global_materials;
|
||||
std::unordered_map<int32_t, int32_t> material_map;
|
||||
|
||||
//==============================================================================
|
||||
// Material implementation
|
||||
//==============================================================================
|
||||
|
||||
Material::Material(pugi::xml_node node)
|
||||
{
|
||||
if (check_for_node(node, "id")) {
|
||||
id = std::stoi(get_node_value(node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of material in materials XML file.");
|
||||
}
|
||||
|
||||
if (check_for_node(node, "volume")) {
|
||||
volume_ = std::stod(get_node_value(node, "volume"));
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-method functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_materials(pugi::xml_node* node)
|
||||
{
|
||||
// Loop over XML material elements and populate the array.
|
||||
for (pugi::xml_node material_node : node->children("material")) {
|
||||
global_materials.push_back(new Material(material_node));
|
||||
}
|
||||
global_materials.shrink_to_fit();
|
||||
|
||||
// Populate the material map.
|
||||
for (int i = 0; i < global_materials.size(); i++) {
|
||||
int32_t mid = global_materials[i]->id;
|
||||
auto search = material_map.find(mid);
|
||||
if (search == material_map.end()) {
|
||||
material_map[mid] = i;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Two or more materials use the same unique ID: " << mid;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// C API
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
openmc_material_get_volume(int32_t index, double* volume)
|
||||
{
|
||||
if (index >= 1 && index <= global_materials.size()) {
|
||||
Material* m = global_materials[index - 1];
|
||||
if (m->volume_ >= 0.0) {
|
||||
*volume = m->volume_;
|
||||
return 0;
|
||||
} else {
|
||||
std::stringstream msg;
|
||||
msg << "Volume for material with ID=" << m->id << " not set.";
|
||||
set_errmsg(msg);
|
||||
return OPENMC_E_UNASSIGNED;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_set_volume(int32_t index, double volume)
|
||||
{
|
||||
if (index >= 1 && index <= global_materials.size()) {
|
||||
Material* m = global_materials[index - 1];
|
||||
if (volume >= 0.0) {
|
||||
m->volume_ = volume;
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Volume must be non-negative");
|
||||
return OPENMC_E_INVALID_ARGUMENT;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
Material* material_pointer(int32_t indx) {return global_materials[indx];}
|
||||
|
||||
int32_t material_id(Material* mat) {return mat->id;}
|
||||
|
||||
void material_set_id(Material* mat, int32_t id, int32_t index)
|
||||
{
|
||||
mat->id = id;
|
||||
//TODO: off-by-one
|
||||
material_map[id] = index - 1;
|
||||
}
|
||||
|
||||
void extend_materials_c(int32_t n)
|
||||
{
|
||||
global_materials.reserve(global_materials.size() + n);
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
global_materials.push_back(new Material());
|
||||
}
|
||||
}
|
||||
|
||||
void free_memory_material_c()
|
||||
{
|
||||
for (Material *mat : global_materials) {delete mat;}
|
||||
global_materials.clear();
|
||||
material_map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
36
src/material.h
Normal file
36
src/material.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef OPENMC_MATERIAL_H
|
||||
#define OPENMC_MATERIAL_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
class Material;
|
||||
extern std::vector<Material*> global_materials;
|
||||
extern std::unordered_map<int32_t, int32_t> material_map;
|
||||
|
||||
//==============================================================================
|
||||
//! A substance with constituent nuclides and thermal scattering data
|
||||
//==============================================================================
|
||||
|
||||
class Material
|
||||
{
|
||||
public:
|
||||
int32_t id; //!< Unique ID
|
||||
double volume_ {-1.0}; //!< Volume in [cm^3]
|
||||
|
||||
Material() {};
|
||||
|
||||
explicit Material(pugi::xml_node material_node);
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
#endif // OPENMC_MATERIAL_H
|
||||
|
|
@ -24,16 +24,52 @@ module material_header
|
|||
public :: openmc_material_add_nuclide
|
||||
public :: openmc_material_get_id
|
||||
public :: openmc_material_get_densities
|
||||
public :: openmc_material_get_volume
|
||||
public :: openmc_material_set_density
|
||||
public :: openmc_material_set_densities
|
||||
public :: openmc_material_set_id
|
||||
public :: material_pointer
|
||||
|
||||
interface
|
||||
function material_pointer(mat_ind) bind(C) result(ptr)
|
||||
import C_PTR, C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: mat_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function material_pointer
|
||||
|
||||
function material_id_c(mat_ptr) bind(C, name='material_id') result(id)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T) :: id
|
||||
end function material_id_c
|
||||
|
||||
subroutine material_set_id_c(mat_ptr, id, index) &
|
||||
bind(C, name='material_set_id')
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T), intent(in), value :: id
|
||||
integer(C_INT32_T), intent(in), value :: index
|
||||
end subroutine material_set_id_c
|
||||
|
||||
subroutine extend_materials_c(n) bind(C)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: n
|
||||
end subroutine extend_materials_c
|
||||
|
||||
function openmc_material_get_volume(index, volume) result(err) bind(C)
|
||||
import C_INT32_T, C_DOUBLE, C_INT
|
||||
integer(C_INT32_T), value :: index
|
||||
real(C_DOUBLE), intent(out) :: volume
|
||||
integer(C_INT) :: err
|
||||
end function openmc_material_get_volume
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL describes a material by its constituent nuclides
|
||||
!===============================================================================
|
||||
|
||||
type, public :: Material
|
||||
integer :: id ! unique identifier
|
||||
type(C_PTR) :: ptr
|
||||
character(len=104) :: name = "" ! User-defined name
|
||||
integer :: n_nuclides = 0 ! number of nuclides
|
||||
integer, allocatable :: nuclide(:) ! index in nuclides array
|
||||
|
|
@ -67,6 +103,8 @@ module material_header
|
|||
logical, allocatable :: p0(:)
|
||||
|
||||
contains
|
||||
procedure :: id => material_id
|
||||
procedure :: set_id => material_set_id
|
||||
procedure :: set_density => material_set_density
|
||||
procedure :: init_nuclide_index => material_init_nuclide_index
|
||||
procedure :: assign_sab_tables => material_assign_sab_tables
|
||||
|
|
@ -88,6 +126,19 @@ contains
|
|||
! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm.
|
||||
!===============================================================================
|
||||
|
||||
function material_id(this) result(id)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T) :: id
|
||||
id = material_id_c(this % ptr)
|
||||
end function material_id
|
||||
|
||||
subroutine material_set_id(this, id, index)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T), intent(in) :: id
|
||||
integer(C_INT32_T), intent(in) :: index
|
||||
call material_set_id_c(this % ptr, id, index)
|
||||
end subroutine material_set_id
|
||||
|
||||
function material_set_density(this, density) result(err)
|
||||
class(Material), intent(inout) :: this
|
||||
real(8), intent(in) :: density
|
||||
|
|
@ -172,7 +223,7 @@ contains
|
|||
found = .false.
|
||||
associate (sab => sab_tables(this % i_sab_tables(k)))
|
||||
FIND_NUCLIDE: do j = 1, size(this % nuclide)
|
||||
if (any(sab % nuclides == nuclides(this % nuclide(j)) % name)) then
|
||||
if (sab % has_nuclide(nuclides(this % nuclide(j)) % name)) then
|
||||
call i_sab_tables % push_back(this % i_sab_tables(k))
|
||||
call i_sab_nuclides % push_back(j)
|
||||
call sab_fracs % push_back(this % sab_fracs(k))
|
||||
|
|
@ -185,7 +236,7 @@ contains
|
|||
if (.not. found) then
|
||||
call fatal_error("S(a,b) table " // trim(this % &
|
||||
sab_names(k)) // " did not match any nuclide on material " &
|
||||
// trim(to_str(this % id)))
|
||||
// trim(to_str(this % id())))
|
||||
end if
|
||||
end do ASSIGN_SAB
|
||||
|
||||
|
|
@ -195,7 +246,7 @@ contains
|
|||
if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then
|
||||
call fatal_error(trim( &
|
||||
nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) &
|
||||
// " in material " // trim(to_str(this % id)) // " was found &
|
||||
// " in material " // trim(to_str(this % id())) // " was found &
|
||||
&in multiple S(a,b) tables. Each nuclide can only appear in &
|
||||
&one S(a,b) table per material.")
|
||||
end if
|
||||
|
|
@ -326,7 +377,7 @@ contains
|
|||
|
||||
! If particle energy is greater than the highest energy for the
|
||||
! S(a,b) table, then don't use the S(a,b) table
|
||||
if (p % E > sab_tables(i_sab) % data(1) % threshold_inelastic) then
|
||||
if (p % E > sab_tables(i_sab) % threshold()) then
|
||||
i_sab = 0
|
||||
end if
|
||||
|
||||
|
|
@ -444,6 +495,11 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine free_memory_material()
|
||||
interface
|
||||
subroutine free_memory_material_c() bind(C)
|
||||
end subroutine free_memory_material_c
|
||||
end interface
|
||||
call free_memory_material_c()
|
||||
n_materials = 0
|
||||
if (allocated(materials)) deallocate(materials)
|
||||
call material_dict % clear()
|
||||
|
|
@ -460,6 +516,7 @@ contains
|
|||
integer(C_INT32_T), optional, intent(out) :: index_end
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: i
|
||||
type(Material), allocatable :: temp(:) ! temporary materials array
|
||||
|
||||
if (n_materials == 0) then
|
||||
|
|
@ -481,6 +538,12 @@ contains
|
|||
if (present(index_end)) index_end = n_materials + n
|
||||
n_materials = n_materials + n
|
||||
|
||||
! Extend the C++ materials array and get pointers to the C++ objects
|
||||
call extend_materials_c(n)
|
||||
do i = n_materials - n, n_materials
|
||||
materials(i) % ptr = material_pointer(i - 1)
|
||||
end do
|
||||
|
||||
err = 0
|
||||
end function openmc_extend_materials
|
||||
|
||||
|
|
@ -606,7 +669,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
id = materials(index) % id
|
||||
id = materials(index) % id()
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
|
|
@ -622,7 +685,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= n_materials) then
|
||||
materials(index) % id = id
|
||||
call materials(index) % set_id(id, index)
|
||||
call material_dict % set(id, index)
|
||||
err = 0
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
67
src/nuclide.h
Normal file
67
src/nuclide.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef OPENMC_NUCLIDE_H
|
||||
#define OPENMC_NUCLIDE_H
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//===============================================================================
|
||||
//! Cached microscopic cross sections for a particular nuclide at the current
|
||||
//! energy
|
||||
//===============================================================================
|
||||
|
||||
struct NuclideMicroXS {
|
||||
// Microscopic cross sections in barns
|
||||
double total; //!< total cross section
|
||||
double absorption; //!< absorption (disappearance)
|
||||
double fission; //!< fission
|
||||
double nu_fission; //!< neutron production from fission
|
||||
|
||||
double elastic; //!< If sab_frac is not 1 or 0, then this value is
|
||||
//!< averaged over bound and non-bound nuclei
|
||||
double thermal; //!< Bound thermal elastic & inelastic scattering
|
||||
double thermal_elastic; //!< Bound thermal elastic scattering
|
||||
double photon_prod; //!< microscopic photon production xs
|
||||
|
||||
// Cross sections for depletion reactions (note that these are not stored in
|
||||
// macroscopic cache)
|
||||
double reaction[DEPLETION_RX.size()];
|
||||
|
||||
// Indicies and factors needed to compute cross sections from the data tables
|
||||
int index_grid; //!< Index on nuclide energy grid
|
||||
int index_temp; //!< Temperature index for nuclide
|
||||
double interp_factor; //!< Interpolation factor on nuc. energy grid
|
||||
int index_sab {-1}; //!< Index in sab_tables
|
||||
int index_temp_sab; //!< Temperature index for sab_tables
|
||||
double sab_frac; //!< Fraction of atoms affected by S(a,b)
|
||||
bool use_ptable; //!< In URR range with probability tables?
|
||||
|
||||
// Energy and temperature last used to evaluate these cross sections. If
|
||||
// these values have changed, then the cross sections must be re-evaluated.
|
||||
double last_E {0.0}; //!< Last evaluated energy
|
||||
double last_sqrtkT {0.0}; //!< Last temperature in sqrt(Boltzmann constant
|
||||
//!< * temperature (eV))
|
||||
};
|
||||
|
||||
//===============================================================================
|
||||
// MATERIALMACROXS contains cached macroscopic cross sections for the material a
|
||||
// particle is traveling through
|
||||
//===============================================================================
|
||||
|
||||
struct MaterialMacroXS {
|
||||
double total; //!< macroscopic total xs
|
||||
double absorption; //!< macroscopic absorption xs
|
||||
double fission; //!< macroscopic fission xs
|
||||
double nu_fission; //!< macroscopic production xs
|
||||
double photon_prod; //!< macroscopic photon production xs
|
||||
|
||||
// Photon cross sections
|
||||
double coherent; //!< macroscopic coherent xs
|
||||
double incoherent; //!< macroscopic incoherent xs
|
||||
double photoelectric; //!< macroscopic photoelectric xs
|
||||
double pair_production; //!< macroscopic pair production xs
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_NUCLIDE_H
|
||||
|
|
@ -17,11 +17,9 @@ module nuclide_header
|
|||
FIT_T, FIT_A, FIT_F, MultipoleArray
|
||||
use message_passing
|
||||
use multipole_header, only: MultipoleArray
|
||||
use product_header, only: AngleEnergyContainer
|
||||
use random_lcg, only: prn, future_prn, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use sab_header, only: SAlphaBeta, sab_tables
|
||||
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
|
||||
use settings
|
||||
use stl_vector, only: VectorInt, VectorReal
|
||||
use string
|
||||
|
|
@ -128,36 +126,36 @@ module nuclide_header
|
|||
! (NuclideMicroXS % elastic)
|
||||
real(8), parameter :: CACHE_INVALID = dble(Z"FFE0000000000000")
|
||||
|
||||
type NuclideMicroXS
|
||||
type, bind(C) :: NuclideMicroXS
|
||||
! Microscopic cross sections in barns
|
||||
real(8) :: total
|
||||
real(8) :: absorption ! absorption (disappearance)
|
||||
real(8) :: fission ! fission
|
||||
real(8) :: nu_fission ! neutron production from fission
|
||||
real(C_DOUBLE) :: total
|
||||
real(C_DOUBLE) :: absorption ! absorption (disappearance)
|
||||
real(C_DOUBLE) :: fission ! fission
|
||||
real(C_DOUBLE) :: nu_fission ! neutron production from fission
|
||||
|
||||
real(8) :: elastic ! If sab_frac is not 1 or 0, then this value is
|
||||
real(C_DOUBLE) :: elastic ! If sab_frac is not 1 or 0, then this value is
|
||||
! averaged over bound and non-bound nuclei
|
||||
real(8) :: thermal ! Bound thermal elastic & inelastic scattering
|
||||
real(8) :: thermal_elastic ! Bound thermal elastic scattering
|
||||
real(8) :: photon_prod ! microscopic photon production xs
|
||||
real(C_DOUBLE) :: thermal ! Bound thermal elastic & inelastic scattering
|
||||
real(C_DOUBLE) :: thermal_elastic ! Bound thermal elastic scattering
|
||||
real(C_DOUBLE) :: photon_prod ! microscopic photon production xs
|
||||
|
||||
! Cross sections for depletion reactions (note that these are not stored in
|
||||
! macroscopic cache)
|
||||
real(8) :: reaction(size(DEPLETION_RX))
|
||||
real(C_DOUBLE) :: reaction(size(DEPLETION_RX))
|
||||
|
||||
! Indicies and factors needed to compute cross sections from the data tables
|
||||
integer :: index_grid ! Index on nuclide energy grid
|
||||
integer :: index_temp ! Temperature index for nuclide
|
||||
real(8) :: interp_factor ! Interpolation factor on nuc. energy grid
|
||||
integer :: index_sab = NONE ! Index in sab_tables
|
||||
integer :: index_temp_sab ! Temperature index for sab_tables
|
||||
real(8) :: sab_frac ! Fraction of atoms affected by S(a,b)
|
||||
logical :: use_ptable ! In URR range with probability tables?
|
||||
integer(C_INT) :: index_grid ! Index on nuclide energy grid
|
||||
integer(C_INT) :: index_temp ! Temperature index for nuclide
|
||||
real(C_DOUBLE) :: interp_factor ! Interpolation factor on nuc. energy grid
|
||||
integer(C_INT) :: index_sab = NONE ! Index in sab_tables
|
||||
integer(C_INT) :: index_temp_sab ! Temperature index for sab_tables
|
||||
real(C_DOUBLE) :: sab_frac ! Fraction of atoms affected by S(a,b)
|
||||
logical(C_BOOL) :: use_ptable ! In URR range with probability tables?
|
||||
|
||||
! Energy and temperature last used to evaluate these cross sections. If
|
||||
! these values have changed, then the cross sections must be re-evaluated.
|
||||
real(8) :: last_E = ZERO ! Last evaluated energy
|
||||
real(8) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann
|
||||
real(C_DOUBLE) :: last_E = ZERO ! Last evaluated energy
|
||||
real(C_DOUBLE) :: last_sqrtkT = ZERO ! Last temperature in sqrt(Boltzmann
|
||||
! constant * temperature (eV))
|
||||
end type NuclideMicroXS
|
||||
|
||||
|
|
@ -166,7 +164,7 @@ module nuclide_header
|
|||
! particle is traveling through
|
||||
!===============================================================================
|
||||
|
||||
type MaterialMacroXS
|
||||
type, bind(C) :: MaterialMacroXS
|
||||
real(C_DOUBLE) :: total ! macroscopic total xs
|
||||
real(C_DOUBLE) :: absorption ! macroscopic absorption xs
|
||||
real(C_DOUBLE) :: fission ! macroscopic fission xs
|
||||
|
|
@ -200,7 +198,7 @@ module nuclide_header
|
|||
type(DictCharInt) :: nuclide_dict
|
||||
|
||||
! Cross section caches
|
||||
type(NuclideMicroXS), allocatable :: micro_xs(:) ! Cache for each nuclide
|
||||
type(NuclideMicroXS), allocatable, target :: micro_xs(:) ! Cache for each nuclide
|
||||
type(MaterialMacroXS) :: material_xs ! Cache for current material
|
||||
!$omp threadprivate(micro_xs, material_xs)
|
||||
|
||||
|
|
@ -642,29 +640,33 @@ contains
|
|||
if (rx % MT >= N_2N0 .and. rx % MT <= N_2NC .and. find(MTs, N_2N) /= -1) cycle
|
||||
|
||||
do t = 1, n_temperature
|
||||
j = rx % xs(t) % threshold
|
||||
n = size(rx % xs(t) % value)
|
||||
j = rx % xs_threshold(t)
|
||||
n = rx % xs_size(t)
|
||||
|
||||
! Add contribution to total cross section
|
||||
this % xs(t) % value(XS_TOTAL,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_TOTAL,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_TOTAL,k) = this % xs(t) % &
|
||||
value(XS_TOTAL,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
|
||||
! Calculate photon production cross section
|
||||
do k = 1, size(rx % products)
|
||||
if (rx % products(k) % particle == PHOTON) then
|
||||
do k = 1, rx % products_size()
|
||||
if (rx % product_particle(k) == PHOTON) then
|
||||
do l = 1, n
|
||||
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) = &
|
||||
this % xs(t) % value(XS_PHOTON_PROD,l+j-1) + &
|
||||
rx % xs(t) % value(l) * rx % products(k) % &
|
||||
yield % evaluate(this % grid(t) % energy(l+j-1))
|
||||
rx % xs(t, l) * rx % product_yield(k, &
|
||||
this % grid(t) % energy(l+j-1))
|
||||
end do
|
||||
end if
|
||||
end do
|
||||
|
||||
! Add contribution to absorption cross section
|
||||
if (is_disappearance(rx % MT)) then
|
||||
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
end if
|
||||
|
||||
! Information about fission reactions
|
||||
|
|
@ -680,39 +682,20 @@ contains
|
|||
! Add contribution to fission cross section
|
||||
if (is_fission(rx % MT)) then
|
||||
this % fissionable = .true.
|
||||
this % xs(t) % value(XS_FISSION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_FISSION,j:j+n-1) + rx % xs(t) % value
|
||||
do k = j, j + n - 1
|
||||
this % xs(t) % value(XS_FISSION,k) = this % xs(t) % &
|
||||
value(XS_FISSION,k) + rx % xs(t, k - j + 1)
|
||||
|
||||
! Also need to add fission cross sections to absorption
|
||||
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
|
||||
! Also need to add fission cross sections to absorption
|
||||
this % xs(t) % value(XS_ABSORPTION,k) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,k) + rx % xs(t, k - j + 1)
|
||||
end do
|
||||
|
||||
! Keep track of this reaction for easy searching later
|
||||
if (t == 1) then
|
||||
i_fission = i_fission + 1
|
||||
this % index_fission(i_fission) = i
|
||||
this % n_fission = this % n_fission + 1
|
||||
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
! Before the secondary distribution refactor, when the angle/energy
|
||||
! distribution was uncorrelated, no angle was actually sampled. With
|
||||
! the refactor, an angle is always sampled for an uncorrelated
|
||||
! distribution even when no angle distribution exists in the ACE file
|
||||
! (isotropic is assumed). To preserve the RNG stream, we explicitly
|
||||
! mark fission reactions so that we avoid the angle sampling.
|
||||
do k = 1, size(rx % products)
|
||||
if (rx % products(k) % particle == NEUTRON) then
|
||||
do m = 1, size(rx % products(k) % distribution)
|
||||
associate (aedist => rx % products(k) % distribution(m) % obj)
|
||||
select type (aedist)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
aedist % fission = .true.
|
||||
end select
|
||||
end associate
|
||||
end do
|
||||
end if
|
||||
end do
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
end if
|
||||
end if ! fission
|
||||
end do ! temperature
|
||||
|
|
@ -721,12 +704,13 @@ contains
|
|||
|
||||
! Determine number of delayed neutron precursors
|
||||
if (this % fissionable) then
|
||||
do i = 1, size(this % reactions(this % index_fission(1)) % products)
|
||||
if (this % reactions(this % index_fission(1)) % products(i) % &
|
||||
emission_mode == EMISSION_DELAYED) then
|
||||
this % n_precursor = this % n_precursor + 1
|
||||
end if
|
||||
end do
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
do i = 1, rx % products_size()
|
||||
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
|
||||
this % n_precursor = this % n_precursor + 1
|
||||
end if
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
|
||||
! Calculate nu-fission cross section
|
||||
|
|
@ -761,36 +745,30 @@ contains
|
|||
|
||||
select case (emission_mode)
|
||||
case (EMISSION_PROMPT)
|
||||
associate (product => this % reactions(this % index_fission(1)) % products(1))
|
||||
nu = product % yield % evaluate(E)
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
nu = rx % product_yield(1, E)
|
||||
end associate
|
||||
|
||||
case (EMISSION_DELAYED)
|
||||
if (this % n_precursor > 0) then
|
||||
if (present(group) .and. group < &
|
||||
size(this % reactions(this % index_fission(1)) % products)) then
|
||||
! If delayed group specified, determine yield immediately
|
||||
associate(p => this % reactions(this % index_fission(1)) % products(1 + group))
|
||||
nu = p % yield % evaluate(E)
|
||||
end associate
|
||||
associate(rx => this % reactions(this % index_fission(1)))
|
||||
if (present(group) .and. group < rx % products_size()) then
|
||||
! If delayed group specified, determine yield immediately
|
||||
nu = rx % product_yield(1 + group, E)
|
||||
else
|
||||
nu = ZERO
|
||||
|
||||
else
|
||||
nu = ZERO
|
||||
do i = 2, rx % products_size()
|
||||
! Skip any non-neutron products
|
||||
if (rx % product_particle(i) /= NEUTRON) exit
|
||||
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
do i = 2, size(rx % products)
|
||||
associate (product => rx % products(i))
|
||||
! Skip any non-neutron products
|
||||
if (product % particle /= NEUTRON) exit
|
||||
|
||||
! Evaluate yield
|
||||
if (product % emission_mode == EMISSION_DELAYED) then
|
||||
nu = nu + product % yield % evaluate(E)
|
||||
end if
|
||||
end associate
|
||||
! Evaluate yield
|
||||
if (rx % product_emission_mode(i) == EMISSION_DELAYED) then
|
||||
nu = nu + rx % product_yield(i, E)
|
||||
end if
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
nu = ZERO
|
||||
end if
|
||||
|
|
@ -799,8 +777,8 @@ contains
|
|||
if (allocated(this % total_nu)) then
|
||||
nu = this % total_nu % evaluate(E)
|
||||
else
|
||||
associate (product => this % reactions(this % index_fission(1)) % products(1))
|
||||
nu = product % yield % evaluate(E)
|
||||
associate (rx => this % reactions(this % index_fission(1)))
|
||||
nu = rx % product_yield(1, E)
|
||||
end associate
|
||||
end if
|
||||
end select
|
||||
|
|
@ -868,6 +846,7 @@ contains
|
|||
integer :: i_high ! upper logarithmic mapping index
|
||||
integer :: i_rxn ! reaction index
|
||||
integer :: j ! index in DEPLETION_RX
|
||||
integer :: threshold ! threshold energy index
|
||||
real(8) :: f ! interp factor on nuclide energy grid
|
||||
real(8) :: kT ! temperature in eV
|
||||
real(8) :: sig_t, sig_a, sig_f ! Intermediate multipole variables
|
||||
|
|
@ -1009,10 +988,11 @@ contains
|
|||
! need to specifically check its threshold index
|
||||
i_rxn = this % reaction_index(DEPLETION_RX(1))
|
||||
if (i_rxn > 0) then
|
||||
associate (xs => this % reactions(i_rxn) % xs(i_temp))
|
||||
associate (rx => this % reactions(i_rxn))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
micro_xs % reaction(1) = (ONE - f) * &
|
||||
xs % value(i_grid - xs % threshold + 1) + &
|
||||
f * xs % value(i_grid - xs % threshold + 2)
|
||||
rx % xs(i_temp, i_grid - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_grid - threshold + 2)
|
||||
end associate
|
||||
end if
|
||||
|
||||
|
|
@ -1022,11 +1002,12 @@ contains
|
|||
! reaction xs appropriately
|
||||
i_rxn = this % reaction_index(DEPLETION_RX(j))
|
||||
if (i_rxn > 0) then
|
||||
associate (xs => this % reactions(i_rxn) % xs(i_temp))
|
||||
if (i_grid >= xs % threshold) then
|
||||
associate (rx => this % reactions(i_rxn))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid >= threshold) then
|
||||
micro_xs % reaction(j) = (ONE - f) * &
|
||||
xs % value(i_grid - xs % threshold + 1) + &
|
||||
f * xs % value(i_grid - xs % threshold + 2)
|
||||
rx % xs(i_temp, i_grid - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_grid - threshold + 2)
|
||||
elseif (j >= 4) then
|
||||
! One can show that the the threshold for (n,(x+1)n) is always
|
||||
! higher than the threshold for (n,xn). Thus, if we are below
|
||||
|
|
@ -1091,8 +1072,9 @@ contains
|
|||
f = micro_xs % interp_factor
|
||||
|
||||
if (i_temp > 0) then
|
||||
associate (xs => this % reactions(1) % xs(i_temp) % value)
|
||||
micro_xs % elastic = (ONE - f) * xs(i_grid) + f * xs(i_grid + 1)
|
||||
associate (rx => this % reactions(1))
|
||||
micro_xs % elastic = (ONE - f) * rx % xs(i_temp, i_grid) + &
|
||||
f * rx % xs(i_temp, i_grid + 1)
|
||||
end associate
|
||||
else
|
||||
! For multipole, elastic is total - absorption
|
||||
|
|
@ -1114,9 +1096,9 @@ contains
|
|||
real(8), intent(in) :: sab_frac ! fraction of atoms affected by S(a,b)
|
||||
type(NuclideMicroXS), intent(inout) :: micro_xs ! Cross section cache
|
||||
|
||||
integer :: i_temp ! temperature index
|
||||
real(8) :: inelastic ! S(a,b) inelastic cross section
|
||||
real(8) :: elastic ! S(a,b) elastic cross section
|
||||
integer(C_INT) :: i_temp ! temperature index
|
||||
real(C_DOUBLE) :: inelastic ! S(a,b) inelastic cross section
|
||||
real(C_DOUBLE) :: elastic ! S(a,b) elastic cross section
|
||||
|
||||
! Set flag that S(a,b) treatment should be used for scattering
|
||||
micro_xs % index_sab = i_sab
|
||||
|
|
@ -1459,6 +1441,7 @@ contains
|
|||
integer :: i_energy ! index for energy
|
||||
integer :: i_low ! band index at lower bounding energy
|
||||
integer :: i_up ! band index at upper bounding energy
|
||||
integer :: threshold ! threshold energy index
|
||||
real(8) :: f ! interpolation factor
|
||||
real(8) :: r ! pseudo-random number
|
||||
real(8) :: elastic ! elastic cross section
|
||||
|
|
@ -1551,10 +1534,11 @@ contains
|
|||
f = micro_xs % interp_factor
|
||||
|
||||
! Determine inelastic scattering cross section
|
||||
associate (xs => this % reactions(this % urr_inelastic) % xs(i_temp))
|
||||
if (i_energy >= xs % threshold) then
|
||||
inelastic = (ONE - f) * xs % value(i_energy - xs % threshold + 1) + &
|
||||
f * xs % value(i_energy - xs % threshold + 2)
|
||||
associate (rx => this % reactions(this % urr_inelastic))
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_energy >= threshold) then
|
||||
inelastic = (ONE - f) * rx % xs(i_temp, i_energy - threshold + 1) + &
|
||||
f * rx % xs(i_temp, i_energy - threshold + 2)
|
||||
end if
|
||||
end associate
|
||||
end if
|
||||
|
|
|
|||
|
|
@ -324,7 +324,11 @@ contains
|
|||
|
||||
! Determine overall generation and number of active generations
|
||||
i = overall_generation()
|
||||
n = i - n_inactive*gen_per_batch
|
||||
if (current_batch > n_inactive) then
|
||||
n = gen_per_batch*n_realizations + current_gen
|
||||
else
|
||||
n = 0
|
||||
end if
|
||||
|
||||
! write out information about batch and generation
|
||||
write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') &
|
||||
|
|
@ -357,7 +361,7 @@ contains
|
|||
|
||||
! Determine overall generation and number of active generations
|
||||
i = current_batch*gen_per_batch
|
||||
n = i - n_inactive*gen_per_batch
|
||||
n = n_realizations*gen_per_batch
|
||||
|
||||
! write out information batch and option independent output
|
||||
write(UNIT=OUTPUT_UNIT, FMT='(2X,A9)', ADVANCE='NO') &
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
318
src/physics.F90
318
src/physics.F90
|
|
@ -18,7 +18,6 @@ module physics
|
|||
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use sab_header, only: sab_tables
|
||||
use secondary_uncorrelated, only: UncorrelatedAngleEnergy
|
||||
use settings
|
||||
use simulation_header
|
||||
use string, only: to_str
|
||||
|
|
@ -506,6 +505,7 @@ contains
|
|||
integer :: i
|
||||
integer :: i_grid
|
||||
integer :: i_temp
|
||||
integer :: threshold
|
||||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
|
|
@ -545,13 +545,14 @@ contains
|
|||
FISSION_REACTION_LOOP: do i = 1, nuc % n_fission
|
||||
i_reaction = nuc % index_fission(i)
|
||||
|
||||
associate (xs => nuc % reactions(i_reaction) % xs(i_temp))
|
||||
associate (rx => nuc % reactions(i_reaction))
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < xs % threshold) cycle
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
! add to cumulative probability
|
||||
prob = prob + ((ONE - f) * xs % value(i_grid - xs % threshold + 1) &
|
||||
+ f*(xs % value(i_grid - xs % threshold + 2)))
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
|
||||
end associate
|
||||
|
||||
! Create fission bank sites if fission occurs
|
||||
|
|
@ -593,17 +594,17 @@ contains
|
|||
! Loop through each reaction type
|
||||
REACTION_LOOP: do i_reaction = 1, size(nuc % reactions)
|
||||
associate (rx => nuc % reactions(i_reaction))
|
||||
threshold = rx % xs(i_temp) % threshold
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
do i_product = 1, size(rx % products)
|
||||
if (rx % products(i_product) % particle == PHOTON) then
|
||||
do i_product = 1, rx % products_size()
|
||||
if (rx % product_particle(i_product) == PHOTON) then
|
||||
! add to cumulative probability
|
||||
yield = rx % products(i_product) % yield % evaluate(E)
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp) % value(i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp) % value(i_grid - threshold + 2))) * yield
|
||||
yield = rx % product_yield(i_product, E)
|
||||
prob = prob + ((ONE - f) * rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2))) * yield
|
||||
|
||||
if (prob > cutoff) return
|
||||
last_valid_reaction = i_reaction
|
||||
|
|
@ -672,6 +673,7 @@ contains
|
|||
integer :: j
|
||||
integer :: i_temp
|
||||
integer :: i_grid
|
||||
integer :: threshold
|
||||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
|
|
@ -750,14 +752,14 @@ contains
|
|||
&// trim(nuc % name))
|
||||
end if
|
||||
|
||||
associate (rx => nuc % reactions(i), &
|
||||
xs => nuc % reactions(i) % xs(i_temp))
|
||||
associate (rx => nuc % reactions(i))
|
||||
! if energy is below threshold for this reaction, skip it
|
||||
if (i_grid < xs % threshold) cycle
|
||||
threshold = rx % xs_threshold(i_temp)
|
||||
if (i_grid < threshold) cycle
|
||||
|
||||
! add to cumulative probability
|
||||
prob = prob + ((ONE - f)*xs % value(i_grid - xs % threshold + 1) &
|
||||
+ f*(xs % value(i_grid - xs % threshold + 2)))
|
||||
prob = prob + ((ONE - f)*rx % xs(i_temp, i_grid - threshold + 1) &
|
||||
+ f*(rx % xs(i_temp, i_grid - threshold + 2)))
|
||||
end associate
|
||||
end do
|
||||
|
||||
|
|
@ -839,14 +841,7 @@ contains
|
|||
vel = sqrt(dot_product(v_n, v_n))
|
||||
|
||||
! Sample scattering angle
|
||||
select type (dist => rxn % products(1) % distribution(1) % obj)
|
||||
type is (UncorrelatedAngleEnergy)
|
||||
if (allocated(dist % angle % energy)) then
|
||||
mu_cm = dist % angle % sample(E)
|
||||
else
|
||||
mu_cm = TWO*prn() - ONE
|
||||
end if
|
||||
end select
|
||||
mu_cm = rxn % sample_elastic_mu(E)
|
||||
|
||||
! Determine direction cosines in CM
|
||||
uvw_cm = v_n/vel
|
||||
|
|
@ -889,263 +884,16 @@ contains
|
|||
real(8), intent(inout) :: uvw(3) ! directional cosines
|
||||
real(8), intent(out) :: mu ! scattering cosine
|
||||
|
||||
integer :: i ! incoming energy bin
|
||||
integer :: j ! outgoing energy bin
|
||||
integer :: k ! outgoing cosine bin
|
||||
integer :: i_temp ! temperature index
|
||||
integer :: n_energy_out ! number of outgoing energy bins
|
||||
real(8) :: f ! interpolation factor
|
||||
real(8) :: r ! used for skewed sampling & continuous
|
||||
real(8) :: E_ij ! outgoing energy j for E_in(i)
|
||||
real(8) :: E_i1j ! outgoing energy j for E_in(i+1)
|
||||
real(8) :: mu_ijk ! outgoing cosine k for E_in(i) and E_out(j)
|
||||
real(8) :: mu_i1jk ! outgoing cosine k for E_in(i+1) and E_out(j)
|
||||
real(8) :: prob ! probability for sampling Bragg edge
|
||||
! Following are needed only for SAB_SECONDARY_CONT scattering
|
||||
integer :: l ! sampled incoming E bin (is i or i + 1)
|
||||
real(8) :: E_i_1, E_i_J ! endpoints on outgoing grid i
|
||||
real(8) :: E_i1_1, E_i1_J ! endpoints on outgoing grid i+1
|
||||
real(8) :: E_1, E_J ! endpoints interpolated between i and i+1
|
||||
real(8) :: E_l_j, E_l_j1 ! adjacent E on outgoing grid l
|
||||
real(8) :: p_l_j, p_l_j1 ! adjacent p on outgoing grid l
|
||||
real(8) :: c_j, c_j1 ! cumulative probability
|
||||
real(8) :: frac ! interpolation factor on outgoing energy
|
||||
real(8) :: r1 ! RNG for outgoing energy
|
||||
real(8) :: mu_left, mu_right ! adjacent mu values
|
||||
real(C_DOUBLE) :: E_out
|
||||
type(C_PTR) :: ptr
|
||||
|
||||
i_temp = micro_xs(i_nuclide) % index_temp_sab
|
||||
! Sample from C++ side
|
||||
ptr = C_LOC(micro_xs(i_nuclide))
|
||||
call sab_tables(i_sab) % sample(ptr, E, E_out, mu)
|
||||
|
||||
! Get pointer to S(a,b) table
|
||||
associate (sab => sab_tables(i_sab) % data(i_temp))
|
||||
|
||||
! Determine whether inelastic or elastic scattering will occur
|
||||
if (prn() < micro_xs(i_nuclide) % thermal_elastic / &
|
||||
micro_xs(i_nuclide) % thermal) then
|
||||
! elastic scattering
|
||||
|
||||
! Get index and interpolation factor for elastic grid
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
i = 1
|
||||
f = ZERO
|
||||
else
|
||||
i = binary_search(sab % elastic_e_in, sab % n_elastic_e_in, E)
|
||||
f = (E - sab%elastic_e_in(i)) / &
|
||||
(sab%elastic_e_in(i+1) - sab%elastic_e_in(i))
|
||||
end if
|
||||
|
||||
! Select treatment based on elastic mode
|
||||
if (sab % elastic_mode == SAB_ELASTIC_DISCRETE) then
|
||||
! With this treatment, we interpolate between two discrete cosines
|
||||
! corresponding to neighboring incoming energies. This is used for
|
||||
! data derived in the incoherent approximation
|
||||
|
||||
! Sample outgoing cosine bin
|
||||
k = 1 + int(prn() * sab % n_elastic_mu)
|
||||
|
||||
! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1)
|
||||
mu_ijk = sab % elastic_mu(k,i)
|
||||
mu_i1jk = sab % elastic_mu(k,i+1)
|
||||
|
||||
! Cosine of angle between incoming and outgoing neutron
|
||||
mu = (1 - f)*mu_ijk + f*mu_i1jk
|
||||
|
||||
elseif (sab % elastic_mode == SAB_ELASTIC_EXACT) then
|
||||
! This treatment is used for data derived in the coherent
|
||||
! approximation, i.e. for crystalline structures that have Bragg
|
||||
! edges.
|
||||
|
||||
! Sample a Bragg edge between 1 and i
|
||||
prob = prn() * sab % elastic_P(i+1)
|
||||
if (prob < sab % elastic_P(1)) then
|
||||
k = 1
|
||||
else
|
||||
k = binary_search(sab % elastic_P(1:i+1), i+1, prob)
|
||||
end if
|
||||
|
||||
! Characteristic scattering cosine for this Bragg edge
|
||||
mu = ONE - TWO*sab % elastic_e_in(k) / E
|
||||
|
||||
end if
|
||||
|
||||
! Outgoing energy is same as incoming energy -- no need to do anything
|
||||
|
||||
else
|
||||
! Perform inelastic calculations
|
||||
|
||||
! Get index and interpolation factor for inelastic grid
|
||||
if (E < sab % inelastic_e_in(1)) then
|
||||
i = 1
|
||||
f = ZERO
|
||||
else
|
||||
i = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
|
||||
f = (E - sab%inelastic_e_in(i)) / &
|
||||
(sab%inelastic_e_in(i+1) - sab%inelastic_e_in(i))
|
||||
end if
|
||||
|
||||
! Now that we have an incoming energy bin, we need to determine the
|
||||
! outgoing energy bin. This will depend on the "secondary energy
|
||||
! mode". If the mode is 0, then the outgoing energy bin is chosen from a
|
||||
! set of equally-likely bins. If the mode is 1, then the first
|
||||
! two and last two bins are skewed to have lower probabilities than the
|
||||
! other bins (0.1 for the first and last bins and 0.4 for the second and
|
||||
! second to last bins, relative to a normal bin probability of 1).
|
||||
! Finally, if the mode is 2, then a continuous distribution (with
|
||||
! accompanying PDF and CDF is utilized)
|
||||
|
||||
if ((sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) .or. &
|
||||
(sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED)) then
|
||||
if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_EQUAL) then
|
||||
! All bins equally likely
|
||||
|
||||
j = 1 + int(prn() * sab % n_inelastic_e_out)
|
||||
elseif (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_SKEWED) then
|
||||
! Distribution skewed away from edge points
|
||||
|
||||
! Determine number of outgoing energy and angle bins
|
||||
n_energy_out = sab % n_inelastic_e_out
|
||||
|
||||
r = prn() * (n_energy_out - 3)
|
||||
if (r > ONE) then
|
||||
! equally likely N-4 middle bins
|
||||
j = int(r) + 2
|
||||
elseif (r > 0.6_8) then
|
||||
! second to last bin has relative probability of 0.4
|
||||
j = n_energy_out - 1
|
||||
elseif (r > HALF) then
|
||||
! last bin has relative probability of 0.1
|
||||
j = n_energy_out
|
||||
elseif (r > 0.1_8) then
|
||||
! second bin has relative probability of 0.4
|
||||
j = 2
|
||||
else
|
||||
! first bin has relative probability of 0.1
|
||||
j = 1
|
||||
end if
|
||||
end if
|
||||
|
||||
! Determine outgoing energy corresponding to E_in(i) and E_in(i+1)
|
||||
E_ij = sab % inelastic_e_out(j,i)
|
||||
E_i1j = sab % inelastic_e_out(j,i+1)
|
||||
|
||||
! Outgoing energy
|
||||
E = (1 - f)*E_ij + f*E_i1j
|
||||
|
||||
! Sample outgoing cosine bin
|
||||
k = 1 + int(prn() * sab % n_inelastic_mu)
|
||||
|
||||
! Determine outgoing cosine corresponding to E_in(i) and E_in(i+1)
|
||||
mu_ijk = sab % inelastic_mu(k,j,i)
|
||||
mu_i1jk = sab % inelastic_mu(k,j,i+1)
|
||||
|
||||
! Cosine of angle between incoming and outgoing neutron
|
||||
mu = (1 - f)*mu_ijk + f*mu_i1jk
|
||||
|
||||
else if (sab_tables(i_sab) % secondary_mode == SAB_SECONDARY_CONT) then
|
||||
! Continuous secondary energy - this is to be similar to
|
||||
! Law 61 interpolation on outgoing energy
|
||||
|
||||
! Sample between ith and (i+1)th bin
|
||||
r = prn()
|
||||
if (f > r) then
|
||||
l = i + 1
|
||||
else
|
||||
l = i
|
||||
end if
|
||||
|
||||
! Determine endpoints on grid i
|
||||
n_energy_out = sab % inelastic_data(i) % n_e_out
|
||||
E_i_1 = sab % inelastic_data(i) % e_out(1)
|
||||
E_i_J = sab % inelastic_data(i) % e_out(n_energy_out)
|
||||
|
||||
! Determine endpoints on grid i + 1
|
||||
n_energy_out = sab % inelastic_data(i + 1) % n_e_out
|
||||
E_i1_1 = sab % inelastic_data(i + 1) % e_out(1)
|
||||
E_i1_J = sab % inelastic_data(i + 1) % e_out(n_energy_out)
|
||||
|
||||
E_1 = E_i_1 + f * (E_i1_1 - E_i_1)
|
||||
E_J = E_i_J + f * (E_i1_J - E_i_J)
|
||||
|
||||
! Determine outgoing energy bin
|
||||
! (First reset n_energy_out to the right value)
|
||||
n_energy_out = sab % inelastic_data(l) % n_e_out
|
||||
r1 = prn()
|
||||
c_j = sab % inelastic_data(l) % e_out_cdf(1)
|
||||
do j = 1, n_energy_out - 1
|
||||
c_j1 = sab % inelastic_data(l) % e_out_cdf(j + 1)
|
||||
if (r1 < c_j1) exit
|
||||
c_j = c_j1
|
||||
end do
|
||||
|
||||
! check to make sure k is <= n_energy_out - 1
|
||||
j = min(j, n_energy_out - 1)
|
||||
|
||||
! Get the data to interpolate between
|
||||
E_l_j = sab % inelastic_data(l) % e_out(j)
|
||||
p_l_j = sab % inelastic_data(l) % e_out_pdf(j)
|
||||
|
||||
! Next part assumes linear-linear interpolation in standard
|
||||
E_l_j1 = sab % inelastic_data(l) % e_out(j + 1)
|
||||
p_l_j1 = sab % inelastic_data(l) % e_out_pdf(j + 1)
|
||||
|
||||
! Find secondary energy (variable E)
|
||||
frac = (p_l_j1 - p_l_j) / (E_l_j1 - E_l_j)
|
||||
if (frac == ZERO) then
|
||||
E = E_l_j + (r1 - c_j) / p_l_j
|
||||
else
|
||||
E = E_l_j + (sqrt(max(ZERO, p_l_j * p_l_j + &
|
||||
TWO * frac * (r1 - c_j))) - p_l_j) / frac
|
||||
end if
|
||||
|
||||
! Now interpolate between incident energy bins i and i + 1
|
||||
if (l == i) then
|
||||
E = E_1 + (E - E_i_1) * (E_J - E_1) / (E_i_J - E_i_1)
|
||||
else
|
||||
E = E_1 + (E - E_i1_1) * (E_J - E_1) / (E_i1_J - E_i1_1)
|
||||
end if
|
||||
|
||||
! Sample outgoing cosine bin
|
||||
k = 1 + int(prn() * sab % n_inelastic_mu)
|
||||
|
||||
! Rather than use the sampled discrete mu directly, it is smeared over
|
||||
! a bin of width min(mu[k] - mu[k-1], mu[k+1] - mu[k]) centered on the
|
||||
! discrete mu value itself.
|
||||
associate (mu_l => sab % inelastic_data(l) % mu)
|
||||
f = (r1 - c_j)/(c_j1 - c_j)
|
||||
|
||||
! Determine (k-1)th mu value
|
||||
if (k == 1) then
|
||||
mu_left = -ONE
|
||||
else
|
||||
mu_left = mu_l(k-1, j) + f*(mu_l(k-1, j+1) - mu_l(k-1,j))
|
||||
end if
|
||||
|
||||
! Determine kth mu value
|
||||
mu = mu_l(k, j) + f*(mu_l(k, j+1) - mu_l(k, j))
|
||||
|
||||
! Determine (k+1)th mu value
|
||||
if (k == sab % n_inelastic_mu) then
|
||||
mu_right = ONE - mu
|
||||
else
|
||||
mu_right = mu_l(k+1, j) + f*(mu_l(k+1, j+1) - mu_l(k+1,j)) - mu
|
||||
end if
|
||||
end associate
|
||||
|
||||
! Smear angle
|
||||
mu = mu + min(mu - mu_left, mu_right - mu)*(prn() - HALF)
|
||||
|
||||
end if ! (inelastic secondary energy treatment)
|
||||
end if ! (elastic or inelastic)
|
||||
end associate
|
||||
|
||||
! Because of floating-point roundoff, it may be possible for mu to be
|
||||
! outside of the range [-1,1). In these cases, we just set mu to exactly
|
||||
! -1 or 1
|
||||
|
||||
if (abs(mu) > ONE) mu = sign(ONE,mu)
|
||||
|
||||
! change direction of particle
|
||||
! Set energy to outgoing, change direction of particle
|
||||
E = E_out
|
||||
uvw = rotate_angle(uvw, mu)
|
||||
|
||||
end subroutine sab_scatter
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -1581,7 +1329,7 @@ contains
|
|||
do group = 1, nuc % n_precursor
|
||||
|
||||
! determine delayed neutron precursor yield for group j
|
||||
yield = rxn % products(1 + group) % yield % evaluate(E_in)
|
||||
yield = rxn % product_yield(1 + group, E_in)
|
||||
|
||||
! Check if this group is sampled
|
||||
prob = prob + yield
|
||||
|
|
@ -1600,7 +1348,7 @@ contains
|
|||
do
|
||||
! sample from energy/angle distribution -- note that mu has already been
|
||||
! sampled above and doesn't need to be resampled
|
||||
call rxn % products(1 + group) % sample(E_in, site % E, mu)
|
||||
call rxn % product_sample(1 + group, E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
|
@ -1624,7 +1372,7 @@ contains
|
|||
! sample from prompt neutron energy distribution
|
||||
n_sample = 0
|
||||
do
|
||||
call rxn % products(1) % sample(E_in, site % E, mu)
|
||||
call rxn % product_sample(1, E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
|
@ -1663,7 +1411,7 @@ contains
|
|||
E_in = p % E
|
||||
|
||||
! sample outgoing energy and scattering cosine
|
||||
call rxn % products(1) % sample(E_in, E, mu)
|
||||
call rxn % product_sample(1, E_in, E, mu)
|
||||
|
||||
! if scattering system is in center-of-mass, transfer cosine of scattering
|
||||
! angle and outgoing energy from CM to LAB
|
||||
|
|
@ -1692,7 +1440,7 @@ contains
|
|||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
||||
! evaluate yield
|
||||
yield = rxn % products(1) % yield % evaluate(E_in)
|
||||
yield = rxn % product_yield(1, E_in)
|
||||
if (mod(yield, ONE) == ZERO) then
|
||||
! If yield is integral, create exactly that many secondary particles
|
||||
do i = 1, nint(yield) - 1
|
||||
|
|
@ -1740,8 +1488,8 @@ contains
|
|||
call sample_photon_product(i_nuclide, p % E, i_reaction, i_product)
|
||||
|
||||
! Sample the outgoing energy and angle
|
||||
call nuclides(i_nuclide) % reactions(i_reaction) % products(i_product) &
|
||||
% sample(p % E, E, mu)
|
||||
call nuclides(i_nuclide) % reactions(i_reaction) % &
|
||||
product_sample(i_product, p % E, E, mu)
|
||||
|
||||
! Sample the new direction
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ contains
|
|||
id = -1
|
||||
else
|
||||
rgb = pl % colors(p % material) % rgb
|
||||
id = materials(p % material) % id
|
||||
id = materials(p % material) % id()
|
||||
end if
|
||||
end associate
|
||||
else if (pl % color_by == PLOT_COLOR_CELLS) then
|
||||
|
|
|
|||
63
src/position.cpp
Normal file
63
src/position.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#include "position.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Position implementation
|
||||
//==============================================================================
|
||||
|
||||
Position&
|
||||
Position::operator+=(Position other)
|
||||
{
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position&
|
||||
Position::operator+=(double v)
|
||||
{
|
||||
x += v;
|
||||
y += v;
|
||||
z += v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position&
|
||||
Position::operator-=(Position other)
|
||||
{
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position&
|
||||
Position::operator-=(double v)
|
||||
{
|
||||
x -= v;
|
||||
y -= v;
|
||||
z -= v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position&
|
||||
Position::operator*=(Position other)
|
||||
{
|
||||
x *= other.x;
|
||||
y *= other.y;
|
||||
z *= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Position&
|
||||
Position::operator*=(double v)
|
||||
{
|
||||
x *= v;
|
||||
y *= v;
|
||||
z *= v;
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
74
src/position.h
Normal file
74
src/position.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef OPENMC_POSITION_H
|
||||
#define OPENMC_POSITION_H
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Type representing a position in Cartesian coordinates
|
||||
//==============================================================================
|
||||
|
||||
struct Position {
|
||||
// Constructors
|
||||
Position() = default;
|
||||
Position(double x_, double y_, double z_) : x{x_}, y{y_}, z{z_} { };
|
||||
Position(const double xyz[]) : x{xyz[0]}, y{xyz[1]}, z{xyz[2]} { };
|
||||
|
||||
// Unary operators
|
||||
Position& operator+=(Position);
|
||||
Position& operator+=(double);
|
||||
Position& operator-=(Position);
|
||||
Position& operator-=(double);
|
||||
Position& operator*=(Position);
|
||||
Position& operator*=(double);
|
||||
const double& operator[](int i) const {
|
||||
switch (i) {
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
}
|
||||
}
|
||||
double& operator[](int i) {
|
||||
switch (i) {
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
}
|
||||
}
|
||||
|
||||
// Other member functions
|
||||
|
||||
//! Dot product of two vectors
|
||||
//! \param[in] other Vector to take dot product with
|
||||
//! \result Resulting dot product
|
||||
inline double dot(Position other) {
|
||||
return x*other.x + y*other.y + z*other.z;
|
||||
}
|
||||
|
||||
// Data members
|
||||
double x = 0.;
|
||||
double y = 0.;
|
||||
double z = 0.;
|
||||
};
|
||||
|
||||
// Binary operators
|
||||
inline Position operator+(Position a, Position b) { return a += b; }
|
||||
inline Position operator+(Position a, double b) { return a += b; }
|
||||
inline Position operator+(double a, Position b) { return b += a; }
|
||||
|
||||
inline Position operator-(Position a, Position b) { return a -= b; }
|
||||
inline Position operator-(Position a, double b) { return a -= b; }
|
||||
inline Position operator-(double a, Position b) { return b -= a; }
|
||||
|
||||
inline Position operator*(Position a, Position b) { return a *= b; }
|
||||
inline Position operator*(Position a, double b) { return a *= b; }
|
||||
inline Position operator*(double a, Position b) { return b *= a; }
|
||||
|
||||
//==============================================================================
|
||||
//! Type representing a vector direction in Cartesian coordinates
|
||||
//==============================================================================
|
||||
|
||||
using Direction = Position;
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_POSITION_H
|
||||
|
|
@ -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
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
element materials {
|
||||
element material {
|
||||
(element id { xsd:int } | attribute id { xsd:int }) &
|
||||
(element name { xsd:string { maxLength="52" } } |
|
||||
attribute name { xsd:string { maxLength="52" } })? &
|
||||
|
||||
element temperature { xsd:double }? &
|
||||
(element name { xsd:string } | attribute name { xsd:string })? &
|
||||
|
||||
(element depletable { xsd:boolean } | attribute depletable { xsd:boolean })? &
|
||||
|
||||
(element volume { xsd:double } | attribute volume { xsd:double })? &
|
||||
|
||||
(element temperature { xsd:double } | attribute temperature { xsd:double })? &
|
||||
|
||||
element density {
|
||||
(element value { xsd:double } | attribute value { xsd:double })? &
|
||||
|
|
|
|||
|
|
@ -15,21 +15,42 @@
|
|||
<optional>
|
||||
<choice>
|
||||
<element name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="name">
|
||||
<data type="string">
|
||||
<param name="maxLength">52</param>
|
||||
</data>
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name="temperature">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<choice>
|
||||
<element name="depletable">
|
||||
<data type="boolean"/>
|
||||
</element>
|
||||
<attribute name="depletable">
|
||||
<data type="boolean"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="volume">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="volume">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="temperature">
|
||||
<data type="double"/>
|
||||
</element>
|
||||
<attribute name="temperature">
|
||||
<data type="double"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<element name="density">
|
||||
<interleave>
|
||||
|
|
|
|||
|
|
@ -1,468 +1,172 @@
|
|||
module sab_header
|
||||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
use, intrinsic :: ISO_FORTRAN_ENV
|
||||
|
||||
use algorithm, only: find, sort, binary_search
|
||||
use constants
|
||||
use dict_header, only: DictIntInt, DictCharInt
|
||||
use distribution_univariate, only: Tabular
|
||||
use error, only: warning, fatal_error
|
||||
use dict_header, only: DictCharInt
|
||||
use hdf5_interface
|
||||
use random_lcg, only: prn
|
||||
use secondary_correlated, only: CorrelatedAngleEnergy
|
||||
use settings
|
||||
use stl_vector, only: VectorInt, VectorReal
|
||||
use string, only: to_str, str_to_int
|
||||
use stl_vector, only: VectorReal
|
||||
use string, only: to_c_string
|
||||
|
||||
implicit none
|
||||
private
|
||||
|
||||
!===============================================================================
|
||||
! DISTENERGYSAB contains the secondary energy/angle distributions for inelastic
|
||||
! thermal scattering collisions which utilize a continuous secondary energy
|
||||
! representation.
|
||||
!===============================================================================
|
||||
|
||||
type DistEnergySab
|
||||
integer :: n_e_out
|
||||
real(8), allocatable :: e_out(:)
|
||||
real(8), allocatable :: e_out_pdf(:)
|
||||
real(8), allocatable :: e_out_cdf(:)
|
||||
real(8), allocatable :: mu(:,:)
|
||||
end type DistEnergySab
|
||||
public :: free_memory_sab
|
||||
|
||||
!===============================================================================
|
||||
! SALPHABETA contains S(a,b) data for thermal neutron scattering, typically off
|
||||
! of light isotopes such as water, graphite, Be, etc
|
||||
!===============================================================================
|
||||
|
||||
type SabData
|
||||
! threshold for S(a,b) treatment (usually ~4 eV)
|
||||
real(8) :: threshold_inelastic
|
||||
real(8) :: threshold_elastic = ZERO
|
||||
|
||||
! Inelastic scattering data
|
||||
integer :: n_inelastic_e_in ! # of incoming E for inelastic
|
||||
integer :: n_inelastic_e_out ! # of outgoing E for inelastic
|
||||
integer :: n_inelastic_mu ! # of outgoing angles for inelastic
|
||||
real(8), allocatable :: inelastic_e_in(:)
|
||||
real(8), allocatable :: inelastic_sigma(:)
|
||||
! The following are used only if secondary_mode is 0 or 1
|
||||
real(8), allocatable :: inelastic_e_out(:,:)
|
||||
real(8), allocatable :: inelastic_mu(:,:,:)
|
||||
! The following is used only if secondary_mode is 3
|
||||
! The different implementation is necessary because the continuous
|
||||
! representation has a variable number of outgoing energy points for each
|
||||
! incoming energy
|
||||
type(DistEnergySab), allocatable :: inelastic_data(:) ! One for each Ein
|
||||
|
||||
! Elastic scattering data
|
||||
integer :: elastic_mode ! elastic mode (discrete/exact)
|
||||
integer :: n_elastic_e_in ! # of incoming E for elastic
|
||||
integer :: n_elastic_mu ! # of outgoing angles for elastic
|
||||
real(8), allocatable :: elastic_e_in(:)
|
||||
real(8), allocatable :: elastic_P(:)
|
||||
real(8), allocatable :: elastic_mu(:,:)
|
||||
end type SabData
|
||||
|
||||
type SAlphaBeta
|
||||
character(150) :: name ! name of table, e.g. lwtr.10t
|
||||
real(8) :: awr ! weight of nucleus in neutron masses
|
||||
real(8), allocatable :: kTs(:) ! temperatures in eV (k*T)
|
||||
character(10), allocatable :: nuclides(:) ! List of valid nuclides
|
||||
integer :: secondary_mode ! secondary mode (equal/skewed/continuous)
|
||||
|
||||
! cross sections and distributions at each temperature
|
||||
type(SabData), allocatable :: data(:)
|
||||
type, public :: SAlphaBeta
|
||||
type(C_PTR) :: ptr
|
||||
contains
|
||||
procedure :: from_hdf5 => salphabeta_from_hdf5
|
||||
procedure :: calculate_xs => sab_calculate_xs
|
||||
procedure :: from_hdf5
|
||||
procedure :: calculate_xs
|
||||
procedure :: free
|
||||
procedure :: has_nuclide
|
||||
procedure :: sample
|
||||
procedure :: threshold
|
||||
end type SAlphaBeta
|
||||
|
||||
! S(a,b) tables
|
||||
type(SAlphaBeta), allocatable, target :: sab_tables(:)
|
||||
integer(C_INT), bind(C) :: n_sab_tables
|
||||
type(DictCharInt) :: sab_dict
|
||||
type(SAlphaBeta), public, allocatable, target :: sab_tables(:)
|
||||
integer(C_INT), public, bind(C) :: n_sab_tables
|
||||
type(DictCharInt), public :: sab_dict
|
||||
|
||||
interface
|
||||
function sab_from_hdf5(group_id, temperature, n, method, &
|
||||
tolerance, minmax) result(ptr) bind(C)
|
||||
import HID_T, C_DOUBLE, C_INT, C_PTR
|
||||
integer(HID_T), value :: group_id
|
||||
real(C_DOUBLE), intent(in) :: temperature
|
||||
integer(C_INT), value :: n
|
||||
integer(C_INT), value :: method
|
||||
real(C_DOUBLE), value :: tolerance
|
||||
real(C_DOUBLE), intent(in) :: minmax(2)
|
||||
type(C_PTR) :: ptr
|
||||
end function
|
||||
|
||||
subroutine sab_calculate_xs(ptr, E, sqrtkT, i_temp, elastic, &
|
||||
inelastic) bind(C)
|
||||
import C_PTR, C_DOUBLE, C_INT
|
||||
type(C_PTR), value :: ptr
|
||||
real(C_DOUBLE), value :: E
|
||||
real(C_DOUBLE), value :: sqrtkT
|
||||
integer(C_INT), intent(out) :: i_temp
|
||||
real(C_DOUBLE), intent(out) :: elastic
|
||||
real(C_DOUBLE), intent(out) :: inelastic
|
||||
end subroutine
|
||||
|
||||
subroutine sab_free(ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR), value :: ptr
|
||||
end subroutine
|
||||
|
||||
function sab_has_nuclide(ptr, name) result(val) bind(C)
|
||||
import C_PTR, C_CHAR, C_BOOL
|
||||
type(C_PTR), value :: ptr
|
||||
character(kind=C_CHAR), intent(in) :: name(*)
|
||||
logical(C_BOOL) :: val
|
||||
end function
|
||||
|
||||
subroutine sab_sample(ptr, micro_xs, E_in, E_out, mu) bind(C)
|
||||
import C_PTR, C_INT, C_DOUBLE
|
||||
type(C_PTR), value :: ptr
|
||||
type(C_PTR), value :: micro_xs
|
||||
real(C_DOUBLE), value :: E_in
|
||||
real(C_DOUBLE), intent(out) :: E_out
|
||||
real(C_DOUBLE), intent(out) :: mu
|
||||
end subroutine
|
||||
|
||||
function sab_threshold(ptr) result(threshold) bind(C)
|
||||
import C_PTR, C_double
|
||||
type(C_PTR), value :: ptr
|
||||
real(C_DOUBLE) :: threshold
|
||||
end function
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
||||
subroutine salphabeta_from_hdf5(this, group_id, temperature, method, &
|
||||
subroutine from_hdf5(this, group_id, temperature, method, &
|
||||
tolerance, minmax)
|
||||
class(SAlphaBeta), intent(inout) :: this
|
||||
integer(HID_T), intent(in) :: group_id
|
||||
type(VectorReal), intent(in) :: temperature ! list of temperatures
|
||||
integer, intent(in) :: method
|
||||
real(8), intent(in) :: tolerance
|
||||
real(8), intent(in) :: minmax(2)
|
||||
integer(C_INT), intent(in) :: method
|
||||
real(C_DOUBLE), intent(in) :: tolerance
|
||||
real(C_DOUBLE), intent(in) :: minmax(2)
|
||||
|
||||
integer :: i, j
|
||||
integer :: t
|
||||
integer :: n_energy, n_energy_out, n_mu
|
||||
integer :: i_closest
|
||||
integer :: n_temperature
|
||||
integer(HID_T) :: T_group
|
||||
integer(HID_T) :: elastic_group
|
||||
integer(HID_T) :: inelastic_group
|
||||
integer(HID_T) :: dset_id
|
||||
integer(HID_T) :: kT_group
|
||||
integer(HSIZE_T) :: dims2(2)
|
||||
integer(HSIZE_T) :: dims3(3)
|
||||
real(8), allocatable :: temp(:,:)
|
||||
character(20) :: type
|
||||
type(CorrelatedAngleEnergy) :: correlated_dist
|
||||
real(C_DOUBLE) :: dummy
|
||||
integer(C_INT) :: n
|
||||
|
||||
character(MAX_WORD_LEN) :: temp_str
|
||||
character(MAX_WORD_LEN), allocatable :: dset_names(:)
|
||||
real(8), allocatable :: temps_available(:) ! temperatures available
|
||||
real(8) :: temp_desired
|
||||
real(8) :: temp_actual
|
||||
type(VectorInt) :: temps_to_read
|
||||
|
||||
! Get name of table from group
|
||||
this % name = get_name(group_id)
|
||||
|
||||
! Get rid of leading '/'
|
||||
this % name = trim(this % name(2:))
|
||||
|
||||
call read_attribute(this % awr, group_id, 'atomic_weight_ratio')
|
||||
call read_attribute(this % nuclides, group_id, 'nuclides')
|
||||
call read_attribute(type, group_id, 'secondary_mode')
|
||||
select case (type)
|
||||
case ('equal')
|
||||
this % secondary_mode = SAB_SECONDARY_EQUAL
|
||||
case ('skewed')
|
||||
this % secondary_mode = SAB_SECONDARY_SKEWED
|
||||
case ('continuous')
|
||||
this % secondary_mode = SAB_SECONDARY_CONT
|
||||
end select
|
||||
|
||||
! Read temperatures
|
||||
kT_group = open_group(group_id, 'kTs')
|
||||
|
||||
! Determine temperatures available
|
||||
call get_datasets(kT_group, dset_names)
|
||||
allocate(temps_available(size(dset_names)))
|
||||
do i = 1, size(dset_names)
|
||||
! Read temperature value
|
||||
call read_dataset(temps_available(i), kT_group, trim(dset_names(i)))
|
||||
temps_available(i) = temps_available(i) / K_BOLTZMANN
|
||||
end do
|
||||
call sort(temps_available)
|
||||
|
||||
! Determine actual temperatures to read -- start by checking whether a
|
||||
! temperature range was given, in which case all temperatures in the range
|
||||
! are loaded irrespective of what temperatures actually appear in the model
|
||||
if (minmax(2) > ZERO) then
|
||||
do i = 1, size(temps_available)
|
||||
temp_actual = temps_available(i)
|
||||
if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then
|
||||
call temps_to_read % push_back(nint(temp_actual))
|
||||
end if
|
||||
end do
|
||||
n = temperature % size()
|
||||
if (n > 0) then
|
||||
this % ptr = sab_from_hdf5(group_id, temperature % data(1), n, method, tolerance, minmax)
|
||||
else
|
||||
! In this case, temperatures % data(1) doesn't exist, so we just pass a
|
||||
! dummy value
|
||||
this % ptr = sab_from_hdf5(group_id, dummy, n, method, tolerance, minmax)
|
||||
end if
|
||||
|
||||
select case (method)
|
||||
case (TEMPERATURE_NEAREST)
|
||||
! Determine actual temperatures to read
|
||||
do i = 1, temperature % size()
|
||||
temp_desired = temperature % data(i)
|
||||
i_closest = minloc(abs(temps_available - temp_desired), dim=1)
|
||||
temp_actual = temps_available(i_closest)
|
||||
if (abs(temp_actual - temp_desired) < tolerance) then
|
||||
if (find(temps_to_read, nint(temp_actual)) == -1) then
|
||||
call temps_to_read % push_back(nint(temp_actual))
|
||||
end if
|
||||
else
|
||||
call fatal_error("Nuclear data library does not contain cross sections &
|
||||
&for " // trim(this % name) // " at or near " // &
|
||||
trim(to_str(nint(temp_desired))) // " K.")
|
||||
end if
|
||||
end do
|
||||
|
||||
case (TEMPERATURE_INTERPOLATION)
|
||||
! If temperature interpolation or multipole is selected, get a list of
|
||||
! bounding temperatures for each actual temperature present in the model
|
||||
TEMP_LOOP: do i = 1, temperature % size()
|
||||
temp_desired = temperature % data(i)
|
||||
|
||||
do j = 1, size(temps_available) - 1
|
||||
if (temps_available(j) <= temp_desired .and. &
|
||||
temp_desired < temps_available(j + 1)) then
|
||||
if (find(temps_to_read, nint(temps_available(j))) == -1) then
|
||||
call temps_to_read % push_back(nint(temps_available(j)))
|
||||
end if
|
||||
if (find(temps_to_read, nint(temps_available(j + 1))) == -1) then
|
||||
call temps_to_read % push_back(nint(temps_available(j + 1)))
|
||||
end if
|
||||
cycle TEMP_LOOP
|
||||
end if
|
||||
end do
|
||||
|
||||
call fatal_error("Nuclear data library does not contain cross sections &
|
||||
&for " // trim(this % name) // " at temperatures that bound " // &
|
||||
trim(to_str(nint(temp_desired))) // " K.")
|
||||
end do TEMP_LOOP
|
||||
|
||||
end select
|
||||
|
||||
! Sort temperatures to read
|
||||
call sort(temps_to_read)
|
||||
|
||||
n_temperature = temps_to_read % size()
|
||||
allocate(this % kTs(n_temperature))
|
||||
allocate(this % data(n_temperature))
|
||||
|
||||
do t = 1, n_temperature
|
||||
! Get temperature as a string
|
||||
temp_str = trim(to_str(temps_to_read % data(t))) // "K"
|
||||
|
||||
! Read exact temperature value
|
||||
call read_dataset(this % kTs(t), kT_group, temp_str)
|
||||
|
||||
! Open group for temperature i
|
||||
T_group = open_group(group_id, temp_str)
|
||||
|
||||
! Coherent elastic data
|
||||
if (object_exists(T_group, 'elastic')) then
|
||||
! Read cross section data
|
||||
elastic_group = open_group(T_group, 'elastic')
|
||||
dset_id = open_dataset(elastic_group, 'xs')
|
||||
call read_attribute(type, dset_id, 'type')
|
||||
call get_shape(dset_id, dims2)
|
||||
allocate(temp(dims2(1), dims2(2)))
|
||||
call read_dataset(temp, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Set cross section data and type
|
||||
this % data(t) % n_elastic_e_in = int(dims2(1), 4)
|
||||
allocate(this % data(t) % elastic_e_in(this % data(t) % n_elastic_e_in))
|
||||
allocate(this % data(t) % elastic_P(this % data(t) % n_elastic_e_in))
|
||||
this % data(t) % elastic_e_in(:) = temp(:, 1)
|
||||
this % data(t) % elastic_P(:) = temp(:, 2)
|
||||
select case (type)
|
||||
case ('tab1')
|
||||
this % data(t) % elastic_mode = SAB_ELASTIC_DISCRETE
|
||||
case ('bragg')
|
||||
this % data(t) % elastic_mode = SAB_ELASTIC_EXACT
|
||||
end select
|
||||
deallocate(temp)
|
||||
|
||||
! Set elastic threshold
|
||||
this % data(t) % threshold_elastic = this % data(t) % elastic_e_in(&
|
||||
this % data(t) % n_elastic_e_in)
|
||||
|
||||
! Read angle distribution
|
||||
if (this % data(t) % elastic_mode /= SAB_ELASTIC_EXACT) then
|
||||
dset_id = open_dataset(elastic_group, 'mu_out')
|
||||
call get_shape(dset_id, dims2)
|
||||
this % data(t) % n_elastic_mu = int(dims2(1), 4)
|
||||
allocate(this % data(t) % elastic_mu(dims2(1), dims2(2)))
|
||||
call read_dataset(this % data(t) % elastic_mu, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
end if
|
||||
|
||||
call close_group(elastic_group)
|
||||
end if
|
||||
|
||||
! Inelastic data
|
||||
if (object_exists(T_group, 'inelastic')) then
|
||||
! Read type of inelastic data
|
||||
inelastic_group = open_group(T_group, 'inelastic')
|
||||
|
||||
! Read cross section data
|
||||
dset_id = open_dataset(inelastic_group, 'xs')
|
||||
call get_shape(dset_id, dims2)
|
||||
allocate(temp(dims2(1), dims2(2)))
|
||||
call read_dataset(temp, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Set cross section data
|
||||
this % data(t) % n_inelastic_e_in = int(dims2(1), 4)
|
||||
allocate(this % data(t) % inelastic_e_in(this % data(t) % n_inelastic_e_in))
|
||||
allocate(this % data(t) % inelastic_sigma(this % data(t) % n_inelastic_e_in))
|
||||
this % data(t) % inelastic_e_in(:) = temp(:, 1)
|
||||
this % data(t) % inelastic_sigma(:) = temp(:, 2)
|
||||
deallocate(temp)
|
||||
|
||||
! Set inelastic threshold
|
||||
this % data(t) % threshold_inelastic = this % data(t) % inelastic_e_in(&
|
||||
this % data(t) % n_inelastic_e_in)
|
||||
|
||||
if (this % secondary_mode /= SAB_SECONDARY_CONT) then
|
||||
! Read energy distribution
|
||||
dset_id = open_dataset(inelastic_group, 'energy_out')
|
||||
call get_shape(dset_id, dims2)
|
||||
this % data(t) % n_inelastic_e_out = int(dims2(1), 4)
|
||||
allocate(this % data(t) % inelastic_e_out(dims2(1), dims2(2)))
|
||||
call read_dataset(this % data(t) % inelastic_e_out, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Read angle distribution
|
||||
dset_id = open_dataset(inelastic_group, 'mu_out')
|
||||
call get_shape(dset_id, dims3)
|
||||
this % data(t) % n_inelastic_mu = int(dims3(1), 4)
|
||||
allocate(this % data(t) % inelastic_mu(dims3(1), dims3(2), dims3(3)))
|
||||
call read_dataset(this % data(t) % inelastic_mu, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
else
|
||||
! Read correlated angle-energy distribution
|
||||
call correlated_dist % from_hdf5(inelastic_group)
|
||||
|
||||
! Convert to S(a,b) native format
|
||||
n_energy = size(correlated_dist % energy)
|
||||
allocate(this % data(t) % inelastic_data(n_energy))
|
||||
do i = 1, n_energy
|
||||
associate (edist => correlated_dist % distribution(i))
|
||||
! Get number of outgoing energies for incoming energy i
|
||||
n_energy_out = size(edist % e_out)
|
||||
this % data(t) % inelastic_data(i) % n_e_out = n_energy_out
|
||||
allocate(this % data(t) % inelastic_data(i) % e_out(n_energy_out))
|
||||
allocate(this % data(t) % inelastic_data(i) % e_out_pdf(n_energy_out))
|
||||
allocate(this % data(t) % inelastic_data(i) % e_out_cdf(n_energy_out))
|
||||
|
||||
! Copy outgoing energy distribution
|
||||
this % data(t) % inelastic_data(i) % e_out(:) = edist % e_out
|
||||
this % data(t) % inelastic_data(i) % e_out_pdf(:) = edist % p
|
||||
this % data(t) % inelastic_data(i) % e_out_cdf(:) = edist % c
|
||||
|
||||
do j = 1, n_energy_out
|
||||
select type (adist => edist % angle(j) % obj)
|
||||
type is (Tabular)
|
||||
! On first pass, allocate space for angles
|
||||
if (j == 1) then
|
||||
n_mu = size(adist % x)
|
||||
this % data(t) % n_inelastic_mu = n_mu
|
||||
allocate(this % data(t) % inelastic_data(i) % mu(&
|
||||
n_mu, n_energy_out))
|
||||
end if
|
||||
|
||||
! Copy outgoing angles
|
||||
this % data(t) % inelastic_data(i) % mu(:, j) = adist % x
|
||||
end select
|
||||
end do
|
||||
end associate
|
||||
end do
|
||||
|
||||
! Clear data on correlated angle-energy object
|
||||
deallocate(correlated_dist % breakpoints)
|
||||
deallocate(correlated_dist % interpolation)
|
||||
deallocate(correlated_dist % energy)
|
||||
deallocate(correlated_dist % distribution)
|
||||
end if
|
||||
|
||||
call close_group(inelastic_group)
|
||||
end if
|
||||
call close_group(T_group)
|
||||
end do
|
||||
|
||||
call close_group(kT_group)
|
||||
end subroutine salphabeta_from_hdf5
|
||||
end subroutine from_hdf5
|
||||
|
||||
!===============================================================================
|
||||
! SAB_CALCULATE_XS determines the elastic and inelastic scattering
|
||||
! cross-sections in the thermal energy range.
|
||||
!===============================================================================
|
||||
|
||||
subroutine sab_calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic)
|
||||
subroutine calculate_xs(this, E, sqrtkT, i_temp, elastic, inelastic)
|
||||
class(SAlphaBeta), intent(in) :: this ! S(a,b) object
|
||||
real(8), intent(in) :: E ! energy
|
||||
real(8), intent(in) :: sqrtkT ! temperature
|
||||
real(C_DOUBLE), intent(in) :: E ! energy
|
||||
real(C_DOUBLE), intent(in) :: sqrtkT ! temperature
|
||||
integer, intent(out) :: i_temp ! index in the S(a,b)'s temperature
|
||||
real(8), intent(out) :: elastic ! thermal elastic cross section
|
||||
real(8), intent(out) :: inelastic ! thermal inelastic cross section
|
||||
real(C_DOUBLE), intent(out) :: elastic ! thermal elastic cross section
|
||||
real(C_DOUBLE), intent(out) :: inelastic ! thermal inelastic cross section
|
||||
|
||||
integer :: i_grid ! index on S(a,b) energy grid
|
||||
real(8) :: f ! interp factor on S(a,b) energy grid
|
||||
real(8) :: kT
|
||||
call sab_calculate_xs(this % ptr, E, sqrtkT, i_temp, elastic, inelastic)
|
||||
end subroutine
|
||||
|
||||
! Determine temperature for S(a,b) table
|
||||
kT = sqrtkT**2
|
||||
if (temperature_method == TEMPERATURE_NEAREST) then
|
||||
! If using nearest temperature, do linear search on temperature
|
||||
do i_temp = 1, size(this % kTs)
|
||||
if (abs(this % kTs(i_temp) - kT) < &
|
||||
K_BOLTZMANN*temperature_tolerance) exit
|
||||
end do
|
||||
else
|
||||
! Find temperatures that bound the actual temperature
|
||||
do i_temp = 1, size(this % kTs) - 1
|
||||
if (this % kTs(i_temp) <= kT .and. &
|
||||
kT < this % kTs(i_temp + 1)) exit
|
||||
end do
|
||||
subroutine free(this)
|
||||
class(SAlphaBeta), intent(inout) :: this
|
||||
call sab_free(this % ptr)
|
||||
end subroutine
|
||||
|
||||
! Randomly sample between temperature i and i+1
|
||||
f = (kT - this % kTs(i_temp)) / &
|
||||
(this % kTs(i_temp + 1) - this % kTs(i_temp))
|
||||
if (f > prn()) i_temp = i_temp + 1
|
||||
end if
|
||||
function has_nuclide(this, name) result(val)
|
||||
class(SAlphaBeta), intent(in) :: this
|
||||
character(len=*), intent(in) :: name
|
||||
logical(C_BOOL) :: val
|
||||
|
||||
val = sab_has_nuclide(this % ptr, to_c_string(name))
|
||||
end function
|
||||
|
||||
! Get pointer to S(a,b) table
|
||||
associate (sab => this % data(i_temp))
|
||||
subroutine sample(this, micro_xs, E_in, E_out, mu)
|
||||
class(SAlphaBeta), intent(in) :: this
|
||||
type(C_PTR), value :: micro_xs
|
||||
real(C_DOUBLE), value :: E_in
|
||||
real(C_DOUBLE), intent(out) :: E_out
|
||||
real(C_DOUBLE), intent(out) :: mu
|
||||
|
||||
! Get index and interpolation factor for inelastic grid
|
||||
if (E < sab % inelastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
f = ZERO
|
||||
else
|
||||
i_grid = binary_search(sab % inelastic_e_in, sab % n_inelastic_e_in, E)
|
||||
f = (E - sab%inelastic_e_in(i_grid)) / &
|
||||
(sab%inelastic_e_in(i_grid+1) - sab%inelastic_e_in(i_grid))
|
||||
end if
|
||||
|
||||
! Calculate S(a,b) inelastic scattering cross section
|
||||
inelastic = (ONE - f) * sab % inelastic_sigma(i_grid) + &
|
||||
f * sab % inelastic_sigma(i_grid + 1)
|
||||
|
||||
! Check for elastic data
|
||||
if (E < sab % threshold_elastic) then
|
||||
! Determine whether elastic scattering is given in the coherent or
|
||||
! incoherent approximation. For coherent, the cross section is
|
||||
! represented as P/E whereas for incoherent, it is simply P
|
||||
|
||||
if (sab % elastic_mode == SAB_ELASTIC_EXACT) then
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
! If energy is below that of the lowest Bragg peak, the elastic
|
||||
! cross section will be zero
|
||||
elastic = ZERO
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
elastic = sab % elastic_P(i_grid) / E
|
||||
end if
|
||||
else
|
||||
! Determine index on elastic energy grid
|
||||
if (E < sab % elastic_e_in(1)) then
|
||||
i_grid = 1
|
||||
else
|
||||
i_grid = binary_search(sab % elastic_e_in, &
|
||||
sab % n_elastic_e_in, E)
|
||||
end if
|
||||
|
||||
! Get interpolation factor for elastic grid
|
||||
f = (E - sab%elastic_e_in(i_grid))/(sab%elastic_e_in(i_grid+1) - &
|
||||
sab%elastic_e_in(i_grid))
|
||||
|
||||
! Calculate S(a,b) elastic scattering cross section
|
||||
elastic = (ONE - f) * sab % elastic_P(i_grid) + &
|
||||
f * sab % elastic_P(i_grid + 1)
|
||||
end if
|
||||
else
|
||||
! No elastic data
|
||||
elastic = ZERO
|
||||
end if
|
||||
end associate
|
||||
|
||||
end subroutine sab_calculate_xs
|
||||
call sab_sample(this % ptr, micro_xs, E_in, E_out, mu)
|
||||
end subroutine
|
||||
|
||||
function threshold(this)
|
||||
class(SAlphaBeta), intent(in) :: this
|
||||
real(C_DOUBLE) :: threshold
|
||||
threshold = sab_threshold(this % ptr)
|
||||
end function
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_SAB deallocates global arrays defined in this module
|
||||
!===============================================================================
|
||||
|
||||
subroutine free_memory_sab()
|
||||
integer :: i
|
||||
n_sab_tables = 0
|
||||
if (allocated(sab_tables)) deallocate(sab_tables)
|
||||
if (allocated(sab_tables)) then
|
||||
do i = 1, size(sab_tables)
|
||||
call sab_tables(i) % free()
|
||||
end do
|
||||
deallocate(sab_tables)
|
||||
end if
|
||||
call sab_dict % clear()
|
||||
end subroutine free_memory_sab
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
module secondary_correlated
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use angleenergy_header, only: AngleEnergy
|
||||
use constants, only: ZERO, ONE, HALF, TWO, HISTOGRAM, LINEAR_LINEAR
|
||||
use distribution_univariate, only: DistributionContainer, Tabular
|
||||
use hdf5_interface
|
||||
use random_lcg, only: prn
|
||||
|
||||
!===============================================================================
|
||||
! CORRELATEDANGLEENERGY represents a correlated angle-energy distribution. This
|
||||
! corresponds to ACE law 61 and ENDF File 6, LAW=1, LANG/=2.
|
||||
!===============================================================================
|
||||
|
||||
type AngleEnergyTable
|
||||
integer :: interpolation
|
||||
integer :: n_discrete
|
||||
real(8), allocatable :: e_out(:)
|
||||
real(8), allocatable :: p(:)
|
||||
real(8), allocatable :: c(:)
|
||||
type(DistributionContainer), allocatable :: angle(:)
|
||||
end type AngleEnergyTable
|
||||
|
||||
type, extends(AngleEnergy) :: CorrelatedAngleEnergy
|
||||
integer :: n_region ! number of interpolation regions
|
||||
integer, allocatable :: breakpoints(:) ! breakpoints of interpolation regions
|
||||
integer, allocatable :: interpolation(:) ! interpolation region codes
|
||||
real(8), allocatable :: energy(:) ! incoming energies
|
||||
type(AngleEnergyTable), allocatable :: distribution(:) ! outgoing E/mu distributions
|
||||
contains
|
||||
procedure :: sample => correlated_sample
|
||||
procedure :: from_hdf5 => correlated_from_hdf5
|
||||
end type CorrelatedAngleEnergy
|
||||
|
||||
contains
|
||||
|
||||
subroutine correlated_sample(this, E_in, E_out, mu)
|
||||
class(CorrelatedAngleEnergy), intent(in) :: this
|
||||
real(8), intent(in) :: E_in ! incoming energy
|
||||
real(8), intent(out) :: E_out ! sampled outgoing energy
|
||||
real(8), intent(out) :: mu ! sapmled scattering cosine
|
||||
|
||||
integer :: i, k, l ! indices
|
||||
integer :: n_energy_in ! number of incoming energies
|
||||
integer :: n_energy_out ! number of outgoing energies
|
||||
real(8) :: r ! interpolation factor on incoming energy
|
||||
real(8) :: r1 ! random number on [0,1)
|
||||
real(8) :: frac ! interpolation factor on outgoing energy
|
||||
real(8) :: E_i_1, E_i_K ! endpoints on outgoing grid i
|
||||
real(8) :: E_i1_1, E_i1_K ! endpoints on outgoing grid i+1
|
||||
real(8) :: E_1, E_K ! endpoints interpolated between i and i+1
|
||||
real(8) :: E_l_k, E_l_k1 ! adjacent E on outgoing grid l
|
||||
real(8) :: p_l_k, p_l_k1 ! adjacent p on outgoing grid l
|
||||
real(8) :: c_k, c_k1 ! cumulative probability
|
||||
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
! Before the secondary distribution refactor, an isotropic polar cosine was
|
||||
! always sampled but then overwritten with the polar cosine sampled from the
|
||||
! correlated distribution. To preserve the random number stream, we keep
|
||||
! this dummy sampling here but can remove it later (will change answers)
|
||||
mu = TWO*prn() - ONE
|
||||
! <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< REMOVE THIS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
! find energy bin and calculate interpolation factor -- if the energy is
|
||||
! outside the range of the tabulated energies, choose the first or last bins
|
||||
n_energy_in = size(this%energy)
|
||||
if (E_in < this%energy(1)) then
|
||||
i = 1
|
||||
r = ZERO
|
||||
elseif (E_in > this%energy(n_energy_in)) then
|
||||
i = n_energy_in - 1
|
||||
r = ONE
|
||||
else
|
||||
i = binary_search(this%energy, n_energy_in, E_in)
|
||||
r = (E_in - this%energy(i)) / &
|
||||
(this%energy(i+1) - this%energy(i))
|
||||
end if
|
||||
|
||||
! Sample between the ith and (i+1)th bin
|
||||
if (r > prn()) then
|
||||
l = i + 1
|
||||
else
|
||||
l = i
|
||||
end if
|
||||
|
||||
! interpolation for energy E1 and EK
|
||||
n_energy_out = size(this%distribution(i)%e_out)
|
||||
E_i_1 = this%distribution(i)%e_out(1)
|
||||
E_i_K = this%distribution(i)%e_out(n_energy_out)
|
||||
|
||||
n_energy_out = size(this%distribution(i+1)%e_out)
|
||||
E_i1_1 = this%distribution(i+1)%e_out(1)
|
||||
E_i1_K = this%distribution(i+1)%e_out(n_energy_out)
|
||||
|
||||
E_1 = E_i_1 + r*(E_i1_1 - E_i_1)
|
||||
E_K = E_i_K + r*(E_i1_K - E_i_K)
|
||||
|
||||
! determine outgoing energy bin
|
||||
n_energy_out = size(this%distribution(l)%e_out)
|
||||
r1 = prn()
|
||||
c_k = this%distribution(l)%c(1)
|
||||
do k = 1, n_energy_out - 1
|
||||
c_k1 = this%distribution(l)%c(k+1)
|
||||
if (r1 < c_k1) exit
|
||||
c_k = c_k1
|
||||
end do
|
||||
|
||||
! check to make sure k is <= NP - 1
|
||||
k = min(k, n_energy_out - 1)
|
||||
|
||||
E_l_k = this%distribution(l)%e_out(k)
|
||||
p_l_k = this%distribution(l)%p(k)
|
||||
if (this%distribution(l)%interpolation == HISTOGRAM) then
|
||||
! Histogram interpolation
|
||||
if (p_l_k > ZERO) then
|
||||
E_out = E_l_k + (r1 - c_k)/p_l_k
|
||||
else
|
||||
E_out = E_l_k
|
||||
end if
|
||||
|
||||
elseif (this%distribution(l)%interpolation == LINEAR_LINEAR) then
|
||||
! Linear-linear interpolation
|
||||
E_l_k1 = this%distribution(l)%e_out(k+1)
|
||||
p_l_k1 = this%distribution(l)%p(k+1)
|
||||
|
||||
frac = (p_l_k1 - p_l_k)/(E_l_k1 - E_l_k)
|
||||
if (frac == ZERO) then
|
||||
E_out = E_l_k + (r1 - c_k)/p_l_k
|
||||
else
|
||||
E_out = E_l_k + (sqrt(max(ZERO, p_l_k*p_l_k + &
|
||||
TWO*frac*(r1 - c_k))) - p_l_k)/frac
|
||||
end if
|
||||
end if
|
||||
|
||||
! Now interpolate between incident energy bins i and i + 1
|
||||
if (l == i) then
|
||||
E_out = E_1 + (E_out - E_i_1)*(E_K - E_1)/(E_i_K - E_i_1)
|
||||
else
|
||||
E_out = E_1 + (E_out - E_i1_1)*(E_K - E_1)/(E_i1_K - E_i1_1)
|
||||
end if
|
||||
|
||||
! Find correlated angular distribution for closest outgoing energy bin
|
||||
if (r1 - c_k < c_k1 - r1) then
|
||||
mu = this%distribution(l)%angle(k)%obj%sample()
|
||||
else
|
||||
mu = this%distribution(l)%angle(k + 1)%obj%sample()
|
||||
end if
|
||||
end subroutine correlated_sample
|
||||
|
||||
subroutine correlated_from_hdf5(this, group_id)
|
||||
class(CorrelatedAngleEnergy), intent(inout) :: this
|
||||
integer(HID_T), intent(in) :: group_id
|
||||
|
||||
integer :: i, j, k
|
||||
integer :: n_energy
|
||||
integer :: m, n
|
||||
integer :: offset_mu
|
||||
integer :: interp_mu
|
||||
integer(HID_T) :: dset_id
|
||||
integer(HSIZE_T) :: dims(1), dims2(2)
|
||||
integer, allocatable :: temp(:,:)
|
||||
integer, allocatable :: offsets(:)
|
||||
integer, allocatable :: interp(:)
|
||||
integer, allocatable :: n_discrete(:)
|
||||
real(8), allocatable :: eout(:,:)
|
||||
real(8), allocatable :: mu(:,:)
|
||||
|
||||
! Open incoming energy dataset
|
||||
dset_id = open_dataset(group_id, 'energy')
|
||||
|
||||
! Get interpolation parameters
|
||||
call read_attribute(temp, dset_id, 'interpolation')
|
||||
allocate(this%breakpoints(size(temp, 1)))
|
||||
allocate(this%interpolation(size(temp, 1)))
|
||||
this%breakpoints(:) = temp(:, 1)
|
||||
this%interpolation(:) = temp(:, 2)
|
||||
this%n_region = size(this%breakpoints)
|
||||
deallocate(temp)
|
||||
|
||||
! Get incoming energies
|
||||
call get_shape(dset_id, dims)
|
||||
n_energy = int(dims(1), 4)
|
||||
allocate(this%energy(n_energy))
|
||||
allocate(this%distribution(n_energy))
|
||||
call read_dataset(this%energy, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Get outgoing energy distribution data
|
||||
dset_id = open_dataset(group_id, 'energy_out')
|
||||
call read_attribute(offsets, dset_id, 'offsets')
|
||||
call read_attribute(interp, dset_id, 'interpolation')
|
||||
call read_attribute(n_discrete, dset_id, 'n_discrete_lines')
|
||||
call get_shape(dset_id, dims2)
|
||||
allocate(eout(dims2(1), dims2(2)))
|
||||
call read_dataset(eout, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Get outgoing angle data
|
||||
dset_id = open_dataset(group_id, 'mu')
|
||||
call get_shape(dset_id, dims2)
|
||||
allocate(mu(dims2(1), dims2(2)))
|
||||
call read_dataset(mu, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
do i = 1, n_energy
|
||||
! Determine number of outgoing energies
|
||||
j = offsets(i)
|
||||
if (i < n_energy) then
|
||||
n = offsets(i+1) - j
|
||||
else
|
||||
n = size(eout, 1) - j
|
||||
end if
|
||||
|
||||
associate (d => this % distribution(i))
|
||||
! Assign interpolation scheme and number of discrete lines
|
||||
d % interpolation = interp(i)
|
||||
d % n_discrete = n_discrete(i)
|
||||
|
||||
! Allocate arrays for energies and PDF/CDF
|
||||
allocate(d % e_out(n))
|
||||
allocate(d % p(n))
|
||||
allocate(d % c(n))
|
||||
allocate(d % angle(n))
|
||||
|
||||
! Copy data
|
||||
d % e_out(:) = eout(j+1:j+n, 1)
|
||||
d % p(:) = eout(j+1:j+n, 2)
|
||||
d % c(:) = eout(j+1:j+n, 3)
|
||||
|
||||
! To get answers that match ACE data, for now we still use the tabulated
|
||||
! CDF values that were passed through to the HDF5 library. At a later
|
||||
! time, we can remove the CDF values from the HDF5 library and
|
||||
! reconstruct them using the PDF
|
||||
if (.false.) then
|
||||
! Calculate cumulative distribution function -- discrete portion
|
||||
do k = 1, d % n_discrete
|
||||
if (k == 1) then
|
||||
d % c(k) = d % p(k)
|
||||
else
|
||||
d % c(k) = d % c(k-1) + d % p(k)
|
||||
end if
|
||||
end do
|
||||
|
||||
! Continuous portion
|
||||
do k = d % n_discrete + 1, n
|
||||
if (k == d % n_discrete + 1) then
|
||||
d % c(k) = sum(d % p(1:d % n_discrete))
|
||||
else
|
||||
if (d % interpolation == HISTOGRAM) then
|
||||
d % c(k) = d % c(k-1) + d % p(k-1) * &
|
||||
(d % e_out(k) - d % e_out(k-1))
|
||||
elseif (d % interpolation == LINEAR_LINEAR) then
|
||||
d % c(k) = d % c(k-1) + HALF*(d % p(k-1) + d % p(k)) * &
|
||||
(d % e_out(k) - d % e_out(k-1))
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
|
||||
! Normalize density and distribution functions
|
||||
d % p(:) = d % p(:)/d % c(n)
|
||||
d % c(:) = d % c(:)/d % c(n)
|
||||
end if
|
||||
end associate
|
||||
|
||||
do j = 1, n
|
||||
allocate(Tabular :: this%distribution(i)%angle(j)%obj)
|
||||
select type(mudist => this%distribution(i)%angle(j)%obj)
|
||||
type is (Tabular)
|
||||
! Get interpolation scheme
|
||||
interp_mu = nint(eout(offsets(i)+j, 4))
|
||||
|
||||
! Determine offset and size of distribution
|
||||
offset_mu = nint(eout(offsets(i)+j, 5))
|
||||
if (offsets(i) + j < size(eout, 1)) then
|
||||
m = nint(eout(offsets(i)+j+1, 5)) - offset_mu
|
||||
else
|
||||
m = size(mu, 1) - offset_mu
|
||||
end if
|
||||
|
||||
! To get answers that match ACE data, for now we still use the tabulated
|
||||
! CDF values that were passed through to the HDF5 library. At a later
|
||||
! time, we can remove the CDF values from the HDF5 library and
|
||||
! reconstruct them using the PDF
|
||||
if (.true.) then
|
||||
mudist % interpolation = interp_mu
|
||||
allocate(mudist % x(m))
|
||||
allocate(mudist % p(m))
|
||||
allocate(mudist % c(m))
|
||||
mudist % x(:) = mu(offset_mu+1:offset_mu+m, 1)
|
||||
mudist % p(:) = mu(offset_mu+1:offset_mu+m, 2)
|
||||
mudist % c(:) = mu(offset_mu+1:offset_mu+m, 3)
|
||||
else
|
||||
! Initialize tabular distribution
|
||||
call mudist % initialize(mu(offset_mu+1:offset_mu+m, 1), &
|
||||
mu(offset_mu+1:offset_mu+m, 2), interp_mu)
|
||||
end if
|
||||
end select
|
||||
end do
|
||||
end do
|
||||
end subroutine correlated_from_hdf5
|
||||
|
||||
end module secondary_correlated
|
||||
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
|
||||
60
src/secondary_correlated.h
Normal file
60
src/secondary_correlated.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
//! \file secondary_correlated.h
|
||||
//! Correlated angle-energy distribution
|
||||
|
||||
#ifndef OPENMC_SECONDARY_CORRELATED_H
|
||||
#define OPENMC_SECONDARY_CORRELATED_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "xtensor/xtensor.hpp"
|
||||
#include "angle_energy.h"
|
||||
#include "endf.h"
|
||||
#include "distribution.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Correlated angle-energy distribution corresponding to ACE law 61 and ENDF
|
||||
//! File 6, LAW=1, LANG!=2.
|
||||
//==============================================================================
|
||||
|
||||
class CorrelatedAngleEnergy : public AngleEnergy {
|
||||
public:
|
||||
//! Outgoing energy/angle at a single incoming energy
|
||||
struct CorrTable {
|
||||
int n_discrete; //!< Number of discrete lines
|
||||
Interpolation interpolation; //!< Interpolation law
|
||||
xt::xtensor<double, 1> e_out; //!< Outgoing energies [eV]
|
||||
xt::xtensor<double, 1> p; //!< Probability density
|
||||
xt::xtensor<double, 1> c; //!< Cumulative distribution
|
||||
std::vector<UPtrDist> angle; //!< Angle distribution
|
||||
};
|
||||
|
||||
explicit CorrelatedAngleEnergy(hid_t group);
|
||||
|
||||
//! Sample distribution for an angle and energy
|
||||
//! \param[in] E_in Incoming energy in [eV]
|
||||
//! \param[out] E_out Outgoing energy in [eV]
|
||||
//! \param[out] mu Outgoing cosine with respect to current direction
|
||||
void sample(double E_in, double& E_out, double& mu) const;
|
||||
|
||||
// energy property
|
||||
std::vector<double>& energy() { return energy_; }
|
||||
const std::vector<double>& energy() const { return energy_; }
|
||||
|
||||
// distribution property
|
||||
std::vector<CorrTable>& distribution() { return distribution_; }
|
||||
const std::vector<CorrTable>& distribution() const { return distribution_; }
|
||||
private:
|
||||
int n_region_; //!< Number of interpolation regions
|
||||
std::vector<int> breakpoints_; //!< Breakpoints between regions
|
||||
std::vector<Interpolation> interpolation_; //!< Interpolation laws
|
||||
std::vector<double> energy_; //!< Energies [eV] at which distributions
|
||||
//!< are tabulated
|
||||
std::vector<CorrTable> distribution_; //!< Distribution at each energy
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_SECONDARY_CORRELATED_H
|
||||
|
|
@ -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
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#include "settings.h"
|
||||
|
||||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "openmc.h"
|
||||
#include "string_utils.h"
|
||||
|
|
@ -20,6 +21,12 @@ std::string path_multipole;
|
|||
std::string path_output;
|
||||
std::string path_source;
|
||||
|
||||
int temperature_method {TEMPERATURE_NEAREST};
|
||||
bool temperature_multipole {false};
|
||||
double temperature_tolerance {10.0};
|
||||
double temperature_default {293.6};
|
||||
std::array<double, 2> temperature_range {0.0, 0.0};
|
||||
|
||||
//==============================================================================
|
||||
// Functions
|
||||
//==============================================================================
|
||||
|
|
@ -65,6 +72,32 @@ void read_settings(pugi::xml_node* root)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get temperature settings
|
||||
if (check_for_node(*root, "temperature_default")) {
|
||||
temperature_default = std::stod(get_node_value(*root, "temperature_default"));
|
||||
}
|
||||
if (check_for_node(*root, "temperature_method")) {
|
||||
auto temp_str = get_node_value(*root, "temperature_method", true, true);
|
||||
if (temp_str == "nearest") {
|
||||
temperature_method = TEMPERATURE_NEAREST;
|
||||
} else if (temp_str == "interpolation") {
|
||||
temperature_method = TEMPERATURE_INTERPOLATION;
|
||||
} else {
|
||||
fatal_error("Unknown temperature method: " + temp_str);
|
||||
}
|
||||
}
|
||||
if (check_for_node(*root, "temperature_tolerance")) {
|
||||
temperature_tolerance = std::stod(get_node_value(*root, "temperature_tolerance"));
|
||||
}
|
||||
if (check_for_node(*root, "temperature_multipole")) {
|
||||
temperature_multipole = get_node_value_bool(*root, "temperature_multipole");
|
||||
}
|
||||
if (check_for_node(*root, "temperature_range")) {
|
||||
auto range = get_node_array<double>(*root, "temperature_range");
|
||||
temperature_range[0] = range[0];
|
||||
temperature_range[1] = range[1];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
//! \file settings.h
|
||||
//! \brief Settings for OpenMC
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
|
@ -31,6 +32,12 @@ extern std::string path_multipole;
|
|||
extern std::string path_output;
|
||||
extern std::string path_source;
|
||||
|
||||
extern int temperature_method;
|
||||
extern bool temperature_multipole;
|
||||
extern double temperature_tolerance;
|
||||
extern double temperature_default;
|
||||
extern std::array<double, 2> temperature_range;
|
||||
|
||||
//==============================================================================
|
||||
//! Read settings from XML file
|
||||
//! \param[in] root XML node for <settings>
|
||||
|
|
@ -40,4 +47,4 @@ extern "C" void read_settings(pugi::xml_node* root);
|
|||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_SETTINGS_H
|
||||
#endif // OPENMC_SETTINGS_H
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue