mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-29 06:35:48 -04:00
Merge branch 'tally-capi' into cmfd-capi
This commit is contained in:
commit
96e4b7c40b
123 changed files with 36878 additions and 1342 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -59,14 +59,12 @@ src/install_manifest.txt
|
|||
scripts/nndc
|
||||
scripts/nndc_hdf5
|
||||
scripts/wmp
|
||||
scripts/multipole_lib.tar.gz
|
||||
scripts/ENDF-B-VII.1-*.tar.gz
|
||||
scripts/JEFF32-ACE-*.tar.gz
|
||||
scripts/JEFF32-ACE-*.zip
|
||||
scripts/TSLs.tar.gz
|
||||
scripts/jeff-3.2
|
||||
scripts/jeff-3.2-hdf5
|
||||
scripts/*.tar.xz
|
||||
scripts/*.tar.*
|
||||
scripts/G4EMLOW*/
|
||||
|
||||
# Images
|
||||
*.ppm
|
||||
|
|
|
|||
|
|
@ -319,6 +319,8 @@ add_library(libopenmc SHARED
|
|||
src/output.F90
|
||||
src/particle_header.F90
|
||||
src/particle_restart.F90
|
||||
src/photon_header.F90
|
||||
src/photon_physics.F90
|
||||
src/physics_common.F90
|
||||
src/physics.F90
|
||||
src/physics_mg.F90
|
||||
|
|
@ -371,6 +373,7 @@ add_library(libopenmc SHARED
|
|||
src/tallies/tally_filter_mesh.F90
|
||||
src/tallies/tally_filter_meshsurface.F90
|
||||
src/tallies/tally_filter_mu.F90
|
||||
src/tallies/tally_filter_particle.F90
|
||||
src/tallies/tally_filter_polar.F90
|
||||
src/tallies/tally_filter_sph_harm.F90
|
||||
src/tallies/tally_filter_sptl_legendre.F90
|
||||
|
|
@ -390,10 +393,12 @@ add_library(libopenmc SHARED
|
|||
src/message_passing.cpp
|
||||
src/mgxs.cpp
|
||||
src/mgxs_interface.cpp
|
||||
src/particle.cpp
|
||||
src/plot.cpp
|
||||
src/pugixml/pugixml_c.cpp
|
||||
src/random_lcg.cpp
|
||||
src/scattdata.cpp
|
||||
src/settings.cpp
|
||||
src/simulation.cpp
|
||||
src/state_point.cpp
|
||||
src/string_functions.cpp
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ openmc@anl.gov.
|
|||
|
||||
## Resources
|
||||
|
||||
- [GitHub Repository](https://github.com/mit-crpg/openmc)
|
||||
- [GitHub Repository](https://github.com/openmc-dev/openmc)
|
||||
- [Documentation](http://openmc.readthedocs.io/en/latest)
|
||||
- [User's Mailing List](openmc-users@googlegroups.com)
|
||||
- [Developer's Mailing List](openmc-dev@googlegroups.com)
|
||||
|
|
@ -22,7 +22,7 @@ openmc@anl.gov.
|
|||
## How to Report Bugs
|
||||
|
||||
OpenMC is hosted on GitHub and all bugs are reported and tracked through the
|
||||
[Issues](https://github.com/mit-crpg/openmc/issues) listed on GitHub.
|
||||
[Issues](https://github.com/openmc-dev/openmc/issues) listed on GitHub.
|
||||
|
||||
## How to Suggest Enhancements
|
||||
|
||||
|
|
|
|||
2
LICENSE
2
LICENSE
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2011-2018 Massachusetts Institute of Technology
|
||||
Copyright (c) 2011-2018 Massachusetts Institute of Technology and OpenMC contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# OpenMC Monte Carlo Particle Transport Code
|
||||
|
||||
[](http://openmc.readthedocs.io/en/latest/license.html)
|
||||
[](https://travis-ci.org/mit-crpg/openmc)
|
||||
[](https://coveralls.io/github/mit-crpg/openmc?branch=develop)
|
||||
[](http://openmc.readthedocs.io/en/latest/license.html)
|
||||
[](https://travis-ci.org/openmc-dev/openmc)
|
||||
[](https://coveralls.io/github/openmc-dev/openmc?branch=develop)
|
||||
|
||||
The OpenMC project aims to provide a fully-featured Monte Carlo particle
|
||||
transport code based on modern methods. It is a constructive solid geometry,
|
||||
|
|
@ -44,7 +44,7 @@ list](https://groups.google.com/forum/?fromgroups=#!forum/openmc-users).
|
|||
## Reporting Bugs
|
||||
|
||||
OpenMC is hosted on GitHub and all bugs are reported and tracked through the
|
||||
[Issues](https://github.com/mit-crpg/openmc/issues) feature on GitHub. However,
|
||||
[Issues](https://github.com/openmc-dev/openmc/issues) feature on GitHub. However,
|
||||
GitHub Issues should not be used for common troubleshooting purposes. If you are
|
||||
having trouble installing the code or getting your model to run properly, you
|
||||
should first send a message to the User's Group mailing list. If it turns out
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'OpenMC'
|
||||
copyright = u'2011-2018, Massachusetts Institute of Technology'
|
||||
copyright = u'2011-2018, Massachusetts Institute of Technology and OpenMC contributors'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
@ -208,7 +208,7 @@ htmlhelp_basename = 'openmcdoc'
|
|||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'openmc.tex', u'OpenMC Documentation',
|
||||
u'Massachusetts Institute of Technology', 'manual'),
|
||||
u'OpenMC contributors', 'manual'),
|
||||
]
|
||||
|
||||
latex_elements = {
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ developer or send a message to the `developers mailing list`_.
|
|||
.. _property attribute: https://docs.python.org/3.6/library/functions.html#property
|
||||
.. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/
|
||||
.. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean
|
||||
.. _xml_interface module: https://github.com/mit-crpg/openmc/blob/develop/src/xml_interface.F90
|
||||
.. _input_xml module: https://github.com/mit-crpg/openmc/blob/develop/src/input_xml.F90
|
||||
.. _xml_interface module: https://github.com/openmc-dev/openmc/blob/develop/src/xml_interface.F90
|
||||
.. _input_xml module: https://github.com/openmc-dev/openmc/blob/develop/src/input_xml.F90
|
||||
.. _RELAX NG: http://relaxng.org/
|
||||
.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html
|
||||
.. _trang: http://www.thaiopensource.com/relaxng/trang.html
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Now that you understand the basic development workflow, let's discuss how an
|
|||
individual to contribute to development. Note that this would apply to both new
|
||||
features and bug fixes. The general steps for contributing are as follows:
|
||||
|
||||
1. Fork the main openmc repository from `mit-crpg/openmc`_. This will create a
|
||||
1. Fork the main openmc repository from `openmc-dev/openmc`_. This will create a
|
||||
repository with the same name under your personal account. As such, you can
|
||||
commit to it as you please without disrupting other developers.
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ features and bug fixes. The general steps for contributing are as follows:
|
|||
ensure that those changes are made on a different branch.
|
||||
|
||||
4. Issue a pull request from GitHub and select the *develop* branch of
|
||||
mit-crpg/openmc as the target.
|
||||
openmc-dev/openmc as the target.
|
||||
|
||||
.. image:: ../_images/pullrequest.png
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ features and bug fixes. The general steps for contributing are as follows:
|
|||
request page itself.
|
||||
|
||||
6. After the pull request has been thoroughly vetted, it is merged back into the
|
||||
*develop* branch of mit-crpg/openmc.
|
||||
*develop* branch of openmc-dev/openmc.
|
||||
|
||||
Private Development
|
||||
-------------------
|
||||
|
|
@ -99,7 +99,7 @@ create a complete copy of the OpenMC repository (not a fork from GitHub). The
|
|||
private repository can then either be stored just locally or in conjunction with
|
||||
a private repository on Github (this requires a `paid plan`_). Alternatively,
|
||||
`Bitbucket`_ offers private repositories for free. If you want to merge some
|
||||
changes you've made in your private repository back to mit-crpg/openmc
|
||||
changes you've made in your private repository back to openmc-dev/openmc
|
||||
repository, simply follow the steps above with an extra step of pulling a branch
|
||||
from your private repository into a public fork.
|
||||
|
||||
|
|
@ -128,9 +128,9 @@ can interfere with virtual environments.
|
|||
.. _GitHub: https://github.com/
|
||||
.. _git flow: http://nvie.com/git-model
|
||||
.. _valgrind: http://valgrind.org/
|
||||
.. _style guide: http://mit-crpg.github.io/openmc/devguide/styleguide.html
|
||||
.. _style guide: http://openmc.readthedocs.io/en/latest/devguide/styleguide.html
|
||||
.. _pull request: https://help.github.com/articles/using-pull-requests
|
||||
.. _mit-crpg/openmc: https://github.com/mit-crpg/openmc
|
||||
.. _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
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ Basic Usage
|
|||
triso
|
||||
candu
|
||||
nuclear-data
|
||||
nuclear-data-resonance-covariance
|
||||
|
||||
------------------------------------
|
||||
Multi-Group Cross Section Generation
|
||||
|
|
|
|||
13
docs/source/examples/nuclear-data-resonance-covariance.rst
Normal file
13
docs/source/examples/nuclear-data-resonance-covariance.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.. _notebook_nuclear_data_resonance_covariance:
|
||||
|
||||
==================================
|
||||
Nuclear Data: Resonance Covariance
|
||||
==================================
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. notebook:: ../../../examples/jupyter/nuclear-data-resonance-covariance.ipynb
|
||||
|
||||
.. only:: latex
|
||||
|
||||
IPython notebooks must be viewed in the online HTML documentation.
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
.. _io_nuclear_data:
|
||||
|
||||
========================
|
||||
Nuclear Data File Format
|
||||
========================
|
||||
=========================
|
||||
Nuclear Data File Formats
|
||||
=========================
|
||||
|
||||
---------------------
|
||||
Incident Neutron Data
|
||||
|
|
@ -10,7 +10,7 @@ Incident Neutron Data
|
|||
|
||||
**/**
|
||||
|
||||
:Attributes:
|
||||
:Attributes: - **filetype** (*char[]*) -- String indicating the type of file
|
||||
- **version** (*int[2]*) -- Major and minor version of the data
|
||||
|
||||
**/<nuclide name>/**
|
||||
|
|
@ -22,7 +22,9 @@ Incident Neutron Data
|
|||
- **atomic_weight_ratio** (*double*) -- Mass in units of neutron masses
|
||||
- **n_reaction** (*int*) -- Number of reactions
|
||||
|
||||
:Datasets: - **energy** (*double[]*) -- Energy points at which cross sections are tabulated
|
||||
:Datasets:
|
||||
- **energy** (*double[]*) -- Energies in [eV] at which cross sections
|
||||
are tabulated
|
||||
|
||||
**/<nuclide name>/kTs/**
|
||||
|
||||
|
|
@ -31,7 +33,7 @@ temperature-dependent data set. For example, the data set corresponding to
|
|||
300 Kelvin would be located at `300K`.
|
||||
|
||||
:Datasets:
|
||||
- **<TTT>K** (*double*) -- kT values (in eV) for each temperature
|
||||
- **<TTT>K** (*double*) -- kT values in [eV] for each temperature
|
||||
TTT (in Kelvin)
|
||||
|
||||
**/<nuclide name>/reactions/reaction_<mt>/**
|
||||
|
|
@ -85,33 +87,117 @@ temperature-dependent data set. For example, the data set corresponding to
|
|||
|
||||
**/<nuclide name>/fission_energy_release/**
|
||||
|
||||
:Datasets: - **fragments** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
:Datasets: - **fragments** (:ref:`function <1d_functions>`) -- Energy
|
||||
released in the form of fragments as a function of incident
|
||||
neutron energy.
|
||||
- **prompt_neutrons** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- Energy released in the form of
|
||||
prompt neutrons as a function of incident neutron energy.
|
||||
- **delayed_neutrons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
- **prompt_neutrons** (:ref:`function <1d_functions>`) -- Energy
|
||||
released in the form of prompt neutrons as a function of incident
|
||||
neutron energy.
|
||||
- **delayed_neutrons** (:ref:`function <1d_functions>`) -- Energy
|
||||
released in the form of delayed neutrons as a function of incident
|
||||
neutron energy.
|
||||
- **prompt_photons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
- **prompt_photons** (:ref:`function <1d_functions>`) -- Energy
|
||||
released in the form of prompt photons as a function of incident
|
||||
neutron energy.
|
||||
- **delayed_photons** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
- **delayed_photons** (:ref:`function <1d_functions>`) -- Energy
|
||||
released in the form of delayed photons as a function of incident
|
||||
neutron energy.
|
||||
- **betas** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of betas as a function of incident
|
||||
neutron energy.
|
||||
- **neutrinos** (:ref:`polynomial <1d_polynomial>`) -- Energy
|
||||
released in the form of neutrinos as a function of incident
|
||||
neutron energy.
|
||||
- **q_prompt** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- The prompt fission Q-value
|
||||
(fragments + prompt neutrons + prompt photons - incident energy)
|
||||
- **q_recoverable** (:ref:`polynomial <1d_polynomial>` or
|
||||
:ref:`tabulated <1d_tabulated>`) -- The recoverable fission Q-value
|
||||
(Q_prompt + delayed neutrons + delayed photons + betas)
|
||||
- **betas** (:ref:`function <1d_functions>`) -- Energy released in
|
||||
the form of betas as a function of incident neutron energy.
|
||||
- **neutrinos** (:ref:`function <1d_functions>`) -- Energy released
|
||||
in the form of neutrinos as a function of incident neutron energy.
|
||||
- **q_prompt** (:ref:`function <1d_functions>`) -- The prompt fission
|
||||
Q-value (fragments + prompt neutrons + prompt photons - incident
|
||||
energy)
|
||||
- **q_recoverable** (:ref:`function <1d_functions>`) -- The
|
||||
recoverable fission Q-value (Q_prompt + delayed neutrons + delayed
|
||||
photons + betas)
|
||||
|
||||
--------------------
|
||||
Incident Photon Data
|
||||
--------------------
|
||||
|
||||
**/**
|
||||
|
||||
:Attributes: - **filetype** (*char[]*) -- String indicating the type of file
|
||||
- **version** (*int[2]*) -- Major and minor version of the data
|
||||
|
||||
**/<element>/**
|
||||
|
||||
:Attributes: - **Z** (*int*) -- Atomic number
|
||||
|
||||
:Datasets:
|
||||
- **energy** (*double[]*) -- Energies in [eV] at which cross sections
|
||||
are tabulated
|
||||
|
||||
**/<element>/bremsstrahlung/**
|
||||
|
||||
:Datasets: - **electron_energy** (*double[]*) -- Incident electron energy in [eV]
|
||||
- **photon_energy** (*double[]*) -- Outgoing photon energy as
|
||||
fraction of incident electron energy
|
||||
- **dcs** (*double[][]*) -- Bremsstrahlung differential cross section
|
||||
at each incident energy in [mb/eV]
|
||||
|
||||
**/<element>/coherent/**
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Coherent scattering cross section in [b]
|
||||
- **integrated_scattering_factor** (:ref:`tabulated <1d_tabulated>`)
|
||||
-- Integrated coherent scattering form factor
|
||||
- **anomalous_real** (:ref:`tabulated <1d_tabulated>`) -- Real part
|
||||
of the anomalous scattering factor
|
||||
- **anomalous_imag** (:ref:`tabulated <1d_tabulated>`) -- Imaginary
|
||||
part of the anomalous scattering factor
|
||||
|
||||
**/<element>/compton_profiles/**
|
||||
|
||||
:Datasets: - **binding_energy** (*double[]*) -- Binding energy for each subshell in [eV]
|
||||
- **num_electrons** (*double[]*) -- Number of electrons in each subshell
|
||||
- **pz** (*double[]*) -- Projection of the electron momentum on the
|
||||
scattering vector in units of :math:`me^2 / \hbar` where :math:`m`
|
||||
is the electron rest mass and :math:`e` is the electron charge
|
||||
- **J** (*double[][]*) -- Compton profile for each subshell in units
|
||||
of :math:`\hbar / (me^2)`
|
||||
|
||||
**/<element>/incoherent/**
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Incoherent scattering cross section in [b]
|
||||
- **scattering_factor** (:ref:`tabulated <1d_tabulated>`) --
|
||||
|
||||
**/<element>/pair_production_electron/**
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Pair production (electron field) cross section in [b]
|
||||
|
||||
**/<element>/pair_production_nuclear/**
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Pair production (nuclear field) cross section in [b]
|
||||
|
||||
**/<element>/photoelectric/**
|
||||
|
||||
:Datasets: - **xs** (*double[]*) -- Total photoionization cross section in [b]
|
||||
|
||||
**/<element>/stopping_powers/**
|
||||
|
||||
:Datasets: - **I** (*double*) -- Mean excitation energy in [eV]
|
||||
- **energy** (*double[]*) -- Energies in [eV]
|
||||
- **s_collision** (*double[]*) -- Collision stopping power in [eV-cm\ :sup:`2`\ /g]
|
||||
- **s_radiative** (*double[]*) -- Radiative stopping power in [eV-cm\ :sup:`2`\ /g]
|
||||
|
||||
**/<element>/subshells/**
|
||||
|
||||
:Attributes: - **designators** (*char[][]*) -- Designator for each shell, e.g. 'M2'
|
||||
|
||||
**/<element>/subshells/<designator>/**
|
||||
|
||||
:Attributes: - **binding_energy** (*double*) -- Binding energy of the subshell in [eV]
|
||||
- **num_electrons** (*double*) -- Number of electrons in the subshell
|
||||
|
||||
:Datasets: - **transitions** (*double[][]*) -- Atomic relaxation data
|
||||
- **xs** (*double[]*) -- Photoionization cross section for subshell
|
||||
in [b] tabulated against the main energy grid
|
||||
|
||||
:Attributes:
|
||||
- **threshold_idx** (*int*) -- Index on the energy
|
||||
grid of the reaction threshold
|
||||
|
||||
-------------------------------
|
||||
Thermal Neutron Scattering Data
|
||||
|
|
|
|||
|
|
@ -32,6 +32,19 @@ standard deviation.
|
|||
|
||||
*Default*: false
|
||||
|
||||
-------------------------------------
|
||||
``<create_fission_neutrons>`` Element
|
||||
-------------------------------------
|
||||
|
||||
The ``<create_fission_neutrons>`` element indicates whether fission neutrons
|
||||
should be created or not. If this element is set to "true", fission neutrons
|
||||
will be created; otherwise the fission is treated as capture and no fission
|
||||
neutron will be created. Note that this option is only applied to fixed source
|
||||
calculation. For eigenvalue calculation, fission will always be treated as real
|
||||
fission.
|
||||
|
||||
*Default*: true
|
||||
|
||||
--------------------
|
||||
``<cutoff>`` Element
|
||||
--------------------
|
||||
|
|
@ -55,31 +68,35 @@ you care. This element has the following attributes/sub-elements:
|
|||
|
||||
*Default*: 1.0
|
||||
|
||||
:energy:
|
||||
The energy under which particles will be killed.
|
||||
:energy_neutron:
|
||||
The energy under which neutrons will be killed.
|
||||
|
||||
*Default*: 0.0
|
||||
|
||||
-------------------------
|
||||
``<energy_grid>`` Element
|
||||
-------------------------
|
||||
:energy_photon:
|
||||
The energy under which photons will be killed.
|
||||
|
||||
The ``<energy_grid>`` element determines the treatment of the energy grid during
|
||||
a simulation. The valid options are "nuclide", "logarithm", and
|
||||
"material-union". Setting this element to "nuclide" will cause OpenMC to use a
|
||||
nuclide's energy grid when determining what points to interpolate between for
|
||||
determining cross sections (i.e. non-unionized energy grid). Setting this
|
||||
element to "logarithm" causes OpenMC to use a logarithmic mapping technique
|
||||
described in LA-UR-14-24530_. Setting this element to "material-union" will
|
||||
cause OpenMC to create energy grids that are unionized material-by-material and
|
||||
use these grids when determining the energy-cross section pairs to interpolate
|
||||
cross section values between.
|
||||
*Default*: 1000.0
|
||||
|
||||
*Default*: logarithm
|
||||
:energy_electron:
|
||||
The energy under which electrons will be killed.
|
||||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
*Default*: 0.0
|
||||
|
||||
.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
|
||||
:energy_positron:
|
||||
The energy under which positrons will be killed.
|
||||
|
||||
*Default*: 0.0
|
||||
|
||||
--------------------------------
|
||||
``<electron_treatment>`` Element
|
||||
--------------------------------
|
||||
|
||||
When photon transport is enabled, the ``<electron_treatment>`` element tells
|
||||
OpenMC whether to deposit all energy from electrons locally (``led``) or create
|
||||
secondary bremsstrahlung photons (``ttb``).
|
||||
|
||||
*Default*: ttb
|
||||
|
||||
.. _energy_mode:
|
||||
|
||||
|
|
@ -153,8 +170,7 @@ the estimated eigenvalue. It has the following attributes/sub-elements:
|
|||
|
||||
*Default*: None
|
||||
|
||||
.. note:: See section on the :ref:`trigger` for more information.
|
||||
|
||||
.. note:: See section on the :ref:`trigger` for more information.
|
||||
|
||||
---------------------------
|
||||
``<log_grid_bins>`` Element
|
||||
|
|
@ -169,6 +185,8 @@ based on the recommended value in LA-UR-14-24530_.
|
|||
|
||||
.. note:: This element is not used in the multi-group :ref:`energy_mode`.
|
||||
|
||||
.. _LA-UR-14-24530: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf
|
||||
|
||||
---------------------------
|
||||
``<max_order>`` Element
|
||||
---------------------------
|
||||
|
|
@ -259,11 +277,21 @@ out the file and "false" will not.
|
|||
-----------------------
|
||||
|
||||
This element indicates the number of neutrons to simulate per fission source
|
||||
iteration when a k-eigenvalue calculation is performed or the number of neutrons
|
||||
per batch for a fixed source simulation.
|
||||
iteration when a k-eigenvalue calculation is performed or the number of
|
||||
particles per batch for a fixed source simulation.
|
||||
|
||||
*Default*: None
|
||||
|
||||
------------------------------
|
||||
``<photon_transport>`` Element
|
||||
------------------------------
|
||||
|
||||
The ``<photon_transport>`` element determines whether photon transport is
|
||||
enabled. This element has no attributes or sub-elements and can be set to
|
||||
either "false" or "true".
|
||||
|
||||
*Default*: false
|
||||
|
||||
---------------------
|
||||
``<ptables>`` Element
|
||||
---------------------
|
||||
|
|
@ -379,6 +407,11 @@ attributes/sub-elements:
|
|||
|
||||
*Default*: 1.0
|
||||
|
||||
:particle:
|
||||
The source particle type, either ``neutron`` or ``photon``.
|
||||
|
||||
*Default*: neutron
|
||||
|
||||
:file:
|
||||
If this attribute is given, it indicates that the source is to be read from
|
||||
a binary source file whose path is given by the value of this element. Note,
|
||||
|
|
@ -812,20 +845,6 @@ and 10. The verbosity levels are defined as follows:
|
|||
|
||||
*Default*: 7
|
||||
|
||||
-------------------------------------
|
||||
``<create_fission_neutrons>`` Element
|
||||
-------------------------------------
|
||||
|
||||
The ``<create_fission_neutrons>`` element indicates whether fission neutrons
|
||||
should be created or not. If this element is set to "true", fission neutrons
|
||||
will be created; otherwise the fission is treated as capture and no fission
|
||||
neutron will be created. Note that this option is only applied to fixed source
|
||||
calculation. For eigenvalue calculation, fission will always be treated as real
|
||||
fission.
|
||||
|
||||
*Default*: true
|
||||
|
||||
|
||||
-------------------------
|
||||
``<volume_calc>`` Element
|
||||
-------------------------
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ attributes/sub-elements:
|
|||
:type:
|
||||
The type of the filter. Accepted options are "cell", "cellfrom",
|
||||
"cellborn", "surface", "material", "universe", "energy", "energyout", "mu",
|
||||
"polar", "azimuthal", "mesh", "distribcell", "delayedgroup", and
|
||||
"energyfunction".
|
||||
"polar", "azimuthal", "mesh", "distribcell", "delayedgroup",
|
||||
"energyfunction", and "particle".
|
||||
|
||||
:bins:
|
||||
A description of the bins for each type of filter can be found in
|
||||
|
|
@ -299,6 +299,9 @@ should be set to:
|
|||
``energyfunction`` filters do not use the ``bins`` entry. Instead
|
||||
they use ``energy`` and ``y``.
|
||||
|
||||
:particle:
|
||||
A list of integers indicating the type of particles to tally ('neutron' = 1,
|
||||
'photon' = 2, 'electron' = 3, 'positron' = 4).
|
||||
|
||||
------------------
|
||||
``<mesh>`` Element
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
License Agreement
|
||||
=================
|
||||
|
||||
Copyright © 2011-2018 Massachusetts Institute of Technology
|
||||
Copyright © 2011-2018 Massachusetts Institute of Technology and OpenMC contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ Theory and Methodology
|
|||
geometry
|
||||
cross_sections
|
||||
random_numbers
|
||||
physics
|
||||
neutron_physics
|
||||
photon_physics
|
||||
tallies
|
||||
eigenvalue
|
||||
parallelization
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
.. _methods_physics:
|
||||
.. _methods_neutron_physics:
|
||||
|
||||
=======
|
||||
Physics
|
||||
=======
|
||||
===============
|
||||
Neutron Physics
|
||||
===============
|
||||
|
||||
There are limited differences between physics treatments used in the
|
||||
continuous-energy and multi-group modes. If distinctions are necessary, each
|
||||
275
docs/source/methods/photon_physics.rst
Normal file
275
docs/source/methods/photon_physics.rst
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
.. _methods_photon_physics:
|
||||
|
||||
==============
|
||||
Photon Physics
|
||||
==============
|
||||
|
||||
Photons, being neutral particles, behave much in the same manner as neutrons,
|
||||
traveling in straight lines and experiencing occasional collisions which change
|
||||
their energy and direction. Photons undergo four basic interactions as they pass
|
||||
through matter: coherent (Rayleigh) scattering, incoherent (Compton) scattering,
|
||||
photoelectric effect, and pair/triplet production. Photons with energy in the
|
||||
MeV range may also undergo photonuclear reactions with an atomic nucleus. In
|
||||
addition to these primary interaction mechanisms, all processes other than
|
||||
coherent scattering can result in the excitation/ionization of atoms. The
|
||||
de-excitation of these atoms can result in the emission of electrons and
|
||||
photons. Electrons themselves also can produce photons by means of
|
||||
bremsstrahlung radiation.
|
||||
|
||||
-------------------
|
||||
Photon Interactions
|
||||
-------------------
|
||||
|
||||
Coherent (Rayleigh) Scattering
|
||||
------------------------------
|
||||
|
||||
The elastic scattering of a photon off a free charged particle is known as
|
||||
Thomson scattering. The differential cross section is independent of the energy
|
||||
of the incident photon. For scattering off a free electron, the differential
|
||||
cross section is
|
||||
|
||||
.. math::
|
||||
:label: thomson
|
||||
|
||||
\frac{d\sigma}{d\mu} = \pi r_e^2 ( 1 + \mu^2 )
|
||||
|
||||
where :math:`\mu` is the cosine of the scattering angle and :math:`r_e` is the
|
||||
classical electron radius. Thomson scattering can generally occur when the
|
||||
photon energy is much less than the rest mass energy of the particle.
|
||||
|
||||
In practice, most elastic scattering of photons off electrons happens not with
|
||||
free electrons but those bound in atoms. This process is known as Rayleigh
|
||||
scattering. The radiation scattered off of individual bound electrons combines
|
||||
coherently, and thus Rayleigh scattering is also known as coherent
|
||||
scattering. Even though conceptually we think of the photon interacting with a
|
||||
single electron, because the wave functions combine constructively it is really
|
||||
as though the photon is interacting with the entire atom.
|
||||
|
||||
The differential cross section for Rayleigh scattering is given by
|
||||
|
||||
.. math::
|
||||
:label: coherent-xs
|
||||
|
||||
\frac{d\sigma(E,E',\mu)}{d\mu} &= \pi r_e^2 ( 1 + \mu^2 )~\left| F(x,Z)
|
||||
+ F' + iF'' \right|^2 \\
|
||||
&= \pi r_e^2 ( 1 + \mu^2 ) \left [ ( F(x,Z)
|
||||
+ F'(E) )^2 + F''(E)^2 \right ]
|
||||
|
||||
where :math:`F(x,Z)` is a form factor as a function of the momentum transfer
|
||||
:math:`x` and the atomic number :math:`Z` and the term :math:`F' + iF''`
|
||||
accounts for `anomalous scattering`_ which can occur near absorption edges. In
|
||||
a Monte Carlo simulation, when coherent scattering occurs, we only need to
|
||||
sample the scattering angle using the differential cross section in
|
||||
:eq:`coherent-xs` since the energy of the photon does not change. In OpenMC,
|
||||
anomalous scattering is ignored such that the differential cross section
|
||||
becomes
|
||||
|
||||
.. math::
|
||||
:label: coherent-xs-openmc
|
||||
|
||||
\frac{d\sigma(E,E',\mu)}{d\mu} = \pi r_e^2 ( 1 + \mu^2 ) F(x, Z)^2
|
||||
|
||||
To construct a proper probability density, we need to normalize the
|
||||
differential cross section in :eq:`coherent-xs-openmc` by the integrated
|
||||
coherent scattering cross section:
|
||||
|
||||
.. math::
|
||||
:label: coherent-pdf-1
|
||||
|
||||
p(\mu) d\mu = \frac{\pi r_e^2}{\sigma(E)} ( 1 + \mu^2 ) F(x, Z)^2 d\mu.
|
||||
|
||||
Since the form factor is given in terms of the momentum transfer, it is more
|
||||
convenient to change variables of the probability density to :math:`x^2`. The
|
||||
momentum transfer is traditionally expressed as
|
||||
|
||||
.. math::
|
||||
:label: momentum-transfer
|
||||
|
||||
x = \kappa \alpha \sqrt{1 - \mu}
|
||||
|
||||
where :math:`\alpha` is the ratio of the photon energy to the electron rest
|
||||
mass, and the coefficient :math:`\kappa` can be shown to be
|
||||
|
||||
.. math::
|
||||
:label: kappa
|
||||
|
||||
\kappa = \frac{m_e c^2}{\sqrt{2}hc} \approx 29.14329,
|
||||
|
||||
where :math:`m_e` is the mass of the electron, :math:`c` is the speed of light
|
||||
in a vacuum, and :math:`h` is Planck's constant. Using :eq:`momentum-transfer`,
|
||||
we have :math:`\mu = 1 - [x/(\kappa\alpha)]^2` and :math:`d\mu/dx^2 =
|
||||
-1/(\kappa\alpha)^2`. The probability density in :math:`x^2` is
|
||||
|
||||
.. math::
|
||||
:label: coherent-pdf-x2
|
||||
|
||||
p(x^2) dx^2 = p(\mu) \left | \frac{d\mu}{dx^2} \right | dx^2 = \frac{2\pi
|
||||
r_e^2 A(\bar{x}^2,Z)}{(\kappa\alpha)^2 \sigma(E)} \left (
|
||||
\frac{1 + \mu^2}{2} \right ) \left ( \frac{F(x, Z)^2}{A(\bar{x}^2, Z)} \right ) dx^2
|
||||
|
||||
where :math:`\bar{x}` is the maximum value of :math:`x` that occurs for
|
||||
:math:`\mu=-1`,
|
||||
|
||||
.. math::
|
||||
:label: xmax
|
||||
|
||||
\bar{x} = \kappa \alpha \sqrt{2} = \frac{m_e c^2}{hc} \alpha,
|
||||
|
||||
and :math:`A(x^2, Z)` is the integral of the square of the form factor:
|
||||
|
||||
.. math::
|
||||
:label: coherent-int-ff
|
||||
|
||||
A(x^2, Z) = \int_0^{x^2} F(x,Z)^2 dx^2.
|
||||
|
||||
As you see, we have multiplied and divided the probability density by the
|
||||
integral of the squared form factor so that the density in :eq:`coherent-pdf-x2`
|
||||
is expressed as the product of two separate densities in parentheses. In OpenMC,
|
||||
a table of :math:`A(x^2, Z)` versus :math:`x^2` is pre-generated and used at
|
||||
run-time to do a table search on the cumulative distribution function:
|
||||
|
||||
.. math::
|
||||
:label: coherent-form-factor-cdf
|
||||
|
||||
\frac{\int_0^{x^2} F(x,Z)^2 dx^2}{\int_0^{\bar{x}^2} F(x,Z)^2 dx^2}
|
||||
|
||||
Once a trial :math:`x^2` value has been selected, we can calculate :math:`\mu`
|
||||
and perform rejection sampling using the Thomson scattering differential cross
|
||||
section. The complete algorithm is as follows:
|
||||
|
||||
1. Determine :math:`\bar{x}^2` using :eq:`xmax`.
|
||||
|
||||
2. Determine :math:`A_{max} = A(\bar{x}^2, Z)` using the pre-generated
|
||||
tabulated data.
|
||||
|
||||
3. Sample the cumulative density by calculating :math:`A' = \xi_1 A_{max}` where
|
||||
:math:`\xi_1` is a uniformly distributed random number.
|
||||
|
||||
4. Perform a binary search to determine the value of :math:`x^2` which satisfies
|
||||
:math:`A(x^2, Z) = A'`.
|
||||
|
||||
5. By combining :eq:`momentum-transfer` and :eq:`xmax`, calculate :math:`\mu =
|
||||
1 - 2x^2/\bar{x}^2`.
|
||||
|
||||
6. If :math:`\xi_2 < (1 + \mu^2)/2`, accept :math:`\mu`. Otherwise, repeat the
|
||||
sampling at step 3.
|
||||
|
||||
Incoherent (Compton) Scattering
|
||||
-------------------------------
|
||||
|
||||
Before we noted that the Thomson cross section gives the behavior for photons
|
||||
scattering off of free electrons valid at low energies. The formula for photon
|
||||
scattering off of free electrons that is valid for all energies can be found
|
||||
using quantum electrodynamics and is known as the Klein-Nishina_ formula after
|
||||
the two authors who discovered it:
|
||||
|
||||
.. math::
|
||||
:label: klein-nishina
|
||||
|
||||
\frac{d\sigma_{KN}}{d\mu} = \pi r_e^2 \left ( \frac{\alpha'}{\alpha} \right
|
||||
)^2 \left [ \frac{\alpha'}{\alpha} + \frac{\alpha}{\alpha'} + \mu^2 - 1
|
||||
\right ]
|
||||
|
||||
where :math:`\alpha` and :math:`\alpha'` are the ratios of the incoming and
|
||||
exiting photon energies to the electron rest mass energy equivalent (0.511 MeV),
|
||||
respectively. Although it appears that the outgoing energy and angle are
|
||||
separate, there is actually a one-to-one relationship between them such that
|
||||
only one needs to be sampled:
|
||||
|
||||
.. math::
|
||||
:label: compton-energy-angle
|
||||
|
||||
\alpha' = \frac{\alpha}{1 + \alpha(1 - \mu)}.
|
||||
|
||||
Note that when :math:`\alpha'/\alpha` goes to one, i.e., scattering is elastic,
|
||||
the Klein-Nishina cross section becomes identical to the Thomson cross
|
||||
section. In general though, the scattering is inelastic and is known as Compton
|
||||
scattering. When a photon interacts with a bound electron in an atom, the
|
||||
Klein-Nishina formula must be modified to account for the binding effects. As in
|
||||
the case of coherent scattering, this is done by means of a form factor. The
|
||||
differential cross section for incoherent scattering is given by
|
||||
|
||||
.. math::
|
||||
:label: incoherent-xs
|
||||
|
||||
\frac{d\sigma}{d\mu} = \frac{d\sigma_{KN}}{d\mu} S(x,Z) = \pi r_e^2 \left (
|
||||
\frac{\alpha'}{\alpha} \right )^2 \left [ \frac{\alpha'}{\alpha} +
|
||||
\frac{\alpha}{\alpha'} + \mu^2 - 1 \right ] S(x,Z)
|
||||
|
||||
where :math:`S(x,Z)` is the form factor. The approach in OpenMC is to first
|
||||
sample the Klein-Nishina cross section and then perform rejection sampling on
|
||||
the form factor. As in other codes, `Kahn's rejection method`_ is used for
|
||||
:math:`\alpha < 3` and a direct method by Koblinger_ is used for :math:`\alpha
|
||||
\ge 3`. The complete algorithm is as follows:
|
||||
|
||||
1. If :math:`\alpha < 3`, sample :math:`\mu` from the Klein-Nishina cross
|
||||
section using Kahn's rejection method. Otherwise, use Koblinger's direct
|
||||
method.
|
||||
|
||||
2. Calculate :math:`x` and :math:`\bar{x}` using :eq:`momentum-transfer` and
|
||||
:eq:`xmax`, respectively.
|
||||
|
||||
3. If :math:`\xi < S(x, Z)/S(\bar{x}, Z)`, accept :math:`\mu`. Otherwise repeat
|
||||
from step 1.
|
||||
|
||||
Doppler Energy Broadening
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
LA-UR-04-0487_ and LA-UR-04-0488_
|
||||
|
||||
Compton Electrons
|
||||
+++++++++++++++++
|
||||
|
||||
|
||||
Photoelectric Effect
|
||||
--------------------
|
||||
|
||||
|
||||
Pair Production
|
||||
---------------
|
||||
|
||||
|
||||
-------------------
|
||||
Secondary Processes
|
||||
-------------------
|
||||
|
||||
New photons may be produced in secondary processes related to the main photon
|
||||
interactions discussed above. A Compton-scattered photon transfers a portion of
|
||||
its energy to the kinetic energy of the recoil electron, which in turn may lose
|
||||
the energy as bremsstrahlung radiation. The vacancy left in the shell by the
|
||||
ejected electron is filled through atomic relaxation, creating a shower of
|
||||
electrons and fluorescence photons. Similarly, the vacancy left by the electron
|
||||
emitted in the photoelectric effect is filled through atomic relaxation. Pair
|
||||
production generates an electron and a positron, both of which can emit
|
||||
bremsstrahlung radiation before the positron eventually collides with an
|
||||
electron, resulting in annihilation of the pair and the creation of two
|
||||
additional photons.
|
||||
|
||||
Atomic Relaxation
|
||||
-----------------
|
||||
|
||||
|
||||
Electron-Positron Annihilation
|
||||
------------------------------
|
||||
|
||||
|
||||
Bremsstrahlung
|
||||
--------------
|
||||
|
||||
.. _ttb:
|
||||
|
||||
Thick-Target Bremsstrahlung Approximation
|
||||
+++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
|
||||
.. _Koblinger: https://doi.org/10.13182/NSE75-A26663
|
||||
|
||||
.. _anomalous scattering: http://pd.chem.ucl.ac.uk/pdnn/diff1/anomscat.htm
|
||||
|
||||
.. _Kahn's rejection method: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/aecu-3259_kahn.pdf
|
||||
|
||||
.. _Klein-Nishina: https://en.wikipedia.org/wiki/Klein%E2%80%93Nishina_formula
|
||||
|
||||
.. _LA-UR-04-0487: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-04-0487.pdf
|
||||
|
||||
.. _LA-UR-04-0488: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-04-0488.pdf
|
||||
|
|
@ -122,6 +122,7 @@ Constructing Tallies
|
|||
openmc.SpatialLegendreFilter
|
||||
openmc.SphericalHarmonicsFilter
|
||||
openmc.ZernikeFilter
|
||||
openmc.ParticleFilter
|
||||
openmc.Mesh
|
||||
openmc.Trigger
|
||||
openmc.TallyDerivative
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ Core Classes
|
|||
openmc.data.CoherentElastic
|
||||
openmc.data.FissionEnergyRelease
|
||||
openmc.data.DataLibrary
|
||||
openmc.data.IncidentPhoton
|
||||
openmc.data.PhotonReaction
|
||||
openmc.data.AtomicRelaxation
|
||||
openmc.data.Decay
|
||||
openmc.data.FissionProductYields
|
||||
openmc.data.WindowedMultipole
|
||||
|
|
@ -79,6 +82,11 @@ Resonance Data
|
|||
openmc.data.MultiLevelBreitWigner
|
||||
openmc.data.ReichMoore
|
||||
openmc.data.RMatrixLimited
|
||||
openmc.data.ResonanceCovariances
|
||||
openmc.data.ResonanceCovarianceRange
|
||||
openmc.data.SingleLevelBreitWignerCovariance
|
||||
openmc.data.MultiLevelBreitWignerCovariance
|
||||
openmc.data.ReichMooreCovariance
|
||||
openmc.data.ParticlePair
|
||||
openmc.data.SpinGroup
|
||||
openmc.data.Unresolved
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ Installing from Source on Linux or Mac OS X
|
|||
-------------------------------------------
|
||||
|
||||
All OpenMC source code is hosted on `GitHub
|
||||
<https://github.com/mit-crpg/openmc>`_. If you have `git
|
||||
<https://github.com/openmc-dev/openmc>`_. If you have `git
|
||||
<https://git-scm.com>`_, the `gcc <https://gcc.gnu.org/>`_ compiler suite,
|
||||
`CMake <http://www.cmake.org>`_, and `HDF5 <https://www.hdfgroup.org/HDF5/>`_
|
||||
installed, you can download and install OpenMC be entering the following
|
||||
|
|
@ -91,7 +91,7 @@ commands in a terminal:
|
|||
|
||||
.. code-block:: sh
|
||||
|
||||
git clone https://github.com/mit-crpg/openmc.git
|
||||
git clone https://github.com/openmc-dev/openmc.git
|
||||
cd openmc
|
||||
mkdir build && cd build
|
||||
cmake ..
|
||||
|
|
|
|||
|
|
@ -71,14 +71,14 @@ Bug Fixes
|
|||
- 0c6915_: Bugfix for generating thermal scattering data
|
||||
- 61ecb4_: Fix bugs in Python multipole objects
|
||||
|
||||
.. _937469: https://github.com/mit-crpg/openmc/commit/937469
|
||||
.. _a149ef: https://github.com/mit-crpg/openmc/commit/a149ef
|
||||
.. _2c9b21: https://github.com/mit-crpg/openmc/commit/2c9b21
|
||||
.. _8047f6: https://github.com/mit-crpg/openmc/commit/8047f6
|
||||
.. _0beb4c: https://github.com/mit-crpg/openmc/commit/0beb4c
|
||||
.. _f124be: https://github.com/mit-crpg/openmc/commit/f124be
|
||||
.. _0c6915: https://github.com/mit-crpg/openmc/commit/0c6915
|
||||
.. _61ecb4: https://github.com/mit-crpg/openmc/commit/61ecb4
|
||||
.. _937469: https://github.com/openmc-dev/openmc/commit/937469
|
||||
.. _a149ef: https://github.com/openmc-dev/openmc/commit/a149ef
|
||||
.. _2c9b21: https://github.com/openmc-dev/openmc/commit/2c9b21
|
||||
.. _8047f6: https://github.com/openmc-dev/openmc/commit/8047f6
|
||||
.. _0beb4c: https://github.com/openmc-dev/openmc/commit/0beb4c
|
||||
.. _f124be: https://github.com/openmc-dev/openmc/commit/f124be
|
||||
.. _0c6915: https://github.com/openmc-dev/openmc/commit/0c6915
|
||||
.. _61ecb4: https://github.com/openmc-dev/openmc/commit/61ecb4
|
||||
|
||||
------------
|
||||
Contributors
|
||||
|
|
|
|||
|
|
@ -8,19 +8,19 @@ A Beginner's Guide to OpenMC
|
|||
What does OpenMC do?
|
||||
--------------------
|
||||
|
||||
In a nutshell, OpenMC simulates neutral particles (presently only neutrons)
|
||||
moving stochastically through an arbitrarily defined model that represents an
|
||||
real-world experimental setup. The experiment could be as simple as a sphere of
|
||||
metal or as complicated as a full-scale `nuclear reactor`_. This is what's known
|
||||
as `Monte Carlo`_ simulation. In the case of a nuclear reactor model, neutrons
|
||||
are especially important because they are the particles that induce `fission`_
|
||||
in isotopes of uranium and other elements. Knowing the behavior of neutrons
|
||||
allows one to determine how often and where fission occurs. The amount of energy
|
||||
released is then directly proportional to the fission reaction rate since most
|
||||
heat is produced by fission. By simulating many neutrons (millions or billions),
|
||||
it is possible to determine the average behavior of these neutrons (or the
|
||||
behavior of the energy produced, or any other quantity one is interested in)
|
||||
very accurately.
|
||||
In a nutshell, OpenMC simulates neutral particles (presently neutrons and
|
||||
photons) moving stochastically through an arbitrarily defined model that
|
||||
represents an real-world experimental setup. The experiment could be as simple
|
||||
as a sphere of metal or as complicated as a full-scale `nuclear reactor`_. This
|
||||
is what's known as `Monte Carlo`_ simulation. In the case of a nuclear reactor
|
||||
model, neutrons are especially important because they are the particles that
|
||||
induce `fission`_ in isotopes of uranium and other elements. Knowing the
|
||||
behavior of neutrons allows one to determine how often and where fission
|
||||
occurs. The amount of energy released is then directly proportional to the
|
||||
fission reaction rate since most heat is produced by fission. By simulating
|
||||
many neutrons (millions or billions), it is possible to determine the average
|
||||
behavior of these neutrons (or the behavior of the energy produced, or any
|
||||
other quantity one is interested in) very accurately.
|
||||
|
||||
Using Monte Carlo methods to determine the average behavior of various physical
|
||||
quantities in a system is quite different from other means of solving the same
|
||||
|
|
@ -153,9 +153,9 @@ and `Volume II`_. You may also find it helpful to review the following terms:
|
|||
.. _Reactor Concepts Manual: http://www.tayloredge.com/periodic/trivia/ReactorConcepts.pdf
|
||||
.. _Volume I: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v1
|
||||
.. _Volume II: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v2
|
||||
.. _OpenMC source code: https://github.com/mit-crpg/openmc
|
||||
.. _OpenMC source code: https://github.com/openmc-dev/openmc
|
||||
.. _GitHub: https://github.com/
|
||||
.. _bug reports: https://github.com/mit-crpg/openmc/issues
|
||||
.. _bug reports: https://github.com/openmc-dev/openmc/issues
|
||||
.. _Neutron cross section: http://en.wikipedia.org/wiki/Neutron_cross_section
|
||||
.. _Effective multiplication factor: https://en.wikipedia.org/wiki/Nuclear_chain_reaction#Effective_neutron_multiplication_factor
|
||||
.. _Flux: http://en.wikipedia.org/wiki/Neutron_flux
|
||||
|
|
|
|||
|
|
@ -258,6 +258,49 @@ method using :attr:`Settings.resonance_scattering`.
|
|||
running the :meth:`IncidentNeutron.add_elastic_0K_from_endf` method
|
||||
may take several minutes to complete.
|
||||
|
||||
Photon Cross Sections
|
||||
---------------------
|
||||
|
||||
Photon interaction data is needed to run OpenMC with photon transport enabled.
|
||||
Some of this data, namely bremsstrahlung cross sections from `Seltzer and
|
||||
Berger`_, stopping powers from the `NIST ESTAR database`_, and Compton profiles
|
||||
calculated by `Biggs et al.`_ and available in the Geant4 G4EMLOW data file, is
|
||||
distributed with OpenMC. The rest is available from the NNDC, which provides
|
||||
ENDF data from the photo-atomic and atomic relaxation sublibraries of the
|
||||
ENDF/B-VII.1 library. By default, the :ref:`scripts_nndc` script will download
|
||||
the ENDF data in addition to the neutron and thermal scattering data, extract
|
||||
it, combine it with the data from other sources, and convert it to an HDF5
|
||||
library. Alternatively, the :ref:`scripts_photon` script can be used to
|
||||
download the photon data on its own and create the HDF5 library:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
openmc-get-photon-data
|
||||
|
||||
As with neutrons and thermal scattering, it is possible to use the Python API
|
||||
directly to convert photon interaction data from an ENDF or ACE file to an HDF5
|
||||
file. The :class:`openmc.data.IncidentPhoton` class contains an
|
||||
:meth:`IncidentPhoton.from_ace` method that will generate photon data from an
|
||||
ACE table and an :meth:`IncidentPhoton.export_to_hdf5` method that writes the
|
||||
data to an HDF5 file:
|
||||
|
||||
::
|
||||
|
||||
u = openmc.data.IncidentPhoton.from_ace('92000.12p')
|
||||
u.export_to_hdf5('U.h5')
|
||||
|
||||
Similarly, the :meth:`IncidentPhoton.from_endf` method can be used to read
|
||||
photon data from an ENDF file. In the case, both the photo-atomic and atomic
|
||||
relaxation sublibrary files are required:
|
||||
|
||||
::
|
||||
|
||||
u = openmc.data.IncidentPhoton.from_endf('photoat-092_U_000.endf',
|
||||
'atom-092_U_000.endf')
|
||||
|
||||
Once the HDF5 files have been generated, a library can be created using the
|
||||
:class:`DataLibrary` class as described in :ref:`create_xs_library`.
|
||||
|
||||
-----------------------
|
||||
Windowed Multipole Data
|
||||
-----------------------
|
||||
|
|
@ -291,3 +334,6 @@ For an example of how to create a multi-group library, see
|
|||
.. _MCNP: http://mcnp.lanl.gov
|
||||
.. _Serpent: http://montecarlo.vtt.fi
|
||||
.. _TENDL: https://tendl.web.psi.ch/tendl_2015/tendl2015.html
|
||||
.. _Seltzer and Berger: https://www.sciencedirect.com/science/article/pii/0092640X86900148?via%3Dihub
|
||||
.. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html
|
||||
.. _Biggs et al.: https://www.sciencedirect.com/science/article/pii/0092640X75900303
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ with GitHub since this involves setting up ssh_ keys. With git installed and
|
|||
setup, the following command will download the full source code from the GitHub
|
||||
repository::
|
||||
|
||||
git clone https://github.com/mit-crpg/openmc.git
|
||||
git clone https://github.com/openmc-dev/openmc.git
|
||||
|
||||
By default, the cloned repository will be set to the development branch. To
|
||||
switch to the source of the latest stable release, run the following commands::
|
||||
|
|
@ -189,7 +189,7 @@ switch to the source of the latest stable release, run the following commands::
|
|||
cd openmc
|
||||
git checkout master
|
||||
|
||||
.. _GitHub: https://github.com/mit-crpg/openmc
|
||||
.. _GitHub: https://github.com/openmc-dev/openmc
|
||||
.. _git: https://git-scm.com
|
||||
.. _ssh: https://en.wikipedia.org/wiki/Secure_Shell
|
||||
|
||||
|
|
|
|||
|
|
@ -166,12 +166,57 @@ ENDF/B-VII.1. It has the following optional arguments:
|
|||
``openmc-get-nndc-data``
|
||||
------------------------
|
||||
|
||||
This script downloads `ENDF/B-VII.1 ACE data
|
||||
<http://www.nndc.bnl.gov/endf/b7.1/acefiles.html>`_ from NNDC and converts it to
|
||||
an HDF5 library for use with OpenMC. This script has the following optional
|
||||
arguments:
|
||||
This script downloads `ENDF/B-VII.1
|
||||
<http://www.nndc.bnl.gov/endf/b7.1/acefiles.html>`_ incident neutron ACE data
|
||||
and incident photon ENDF data from NNDC and converts it to an HDF5 library for
|
||||
use with OpenMC. This script has the following optional arguments:
|
||||
|
||||
-b, --batch Suppress standard in
|
||||
-b, --batch
|
||||
Suppress standard in
|
||||
|
||||
-n, --neutron_only
|
||||
Whether to exclude photon interaction/atomic data
|
||||
|
||||
.. _scripts_photon:
|
||||
|
||||
--------------------------
|
||||
``openmc-get-photon-data``
|
||||
--------------------------
|
||||
|
||||
This script downloads `ENDF data <http://www.nndc.bnl.gov/endf/b7.1/zips/>`_
|
||||
from NNDC for photo-atomic and atomic relaxation sublibraries and converts it
|
||||
to an HDF5 library for use with photon transport in OpenMC. This script has the
|
||||
following optional arguments:
|
||||
|
||||
-b, --batch
|
||||
Suppress standard in
|
||||
|
||||
-c, --cross-sections
|
||||
cross_sections.xml file to append libraries to
|
||||
|
||||
.. _scripts_compton:
|
||||
|
||||
-----------------------
|
||||
``openmc-make-compton``
|
||||
-----------------------
|
||||
|
||||
This script generates an HDF5 file called ``compton_profiles.h5`` that contains
|
||||
Compton profile data using an existing data library from `Geant4
|
||||
<http://geant4.cern.ch/>`_. Note that OpenMC includes this data file by default
|
||||
so it should not be necessary in practice to generate it yourself.
|
||||
|
||||
.. _scripts_stopping:
|
||||
|
||||
-------------------------------
|
||||
``openmc-make-stopping-powers``
|
||||
-------------------------------
|
||||
|
||||
This script generates an HDF5 file called ``stopping_power.h5`` that contains
|
||||
radiative and collision stopping powers and mean excitation energy pulled from
|
||||
the `NIST ESTAR database
|
||||
<https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html>`_. Note that OpenMC
|
||||
includes this data file by default so it should not be necessary in practice to
|
||||
generate it yourself.
|
||||
|
||||
.. _scripts_plot:
|
||||
|
||||
|
|
|
|||
|
|
@ -151,10 +151,19 @@ time and another that should be sampled 30% of the time::
|
|||
|
||||
settings.source = [src1, src2]
|
||||
|
||||
Finally, the :attr:`Source.particle` attribute can be used to indicate the
|
||||
source should be composed of particles other than neutrons. For example, the
|
||||
following would generate a photon source::
|
||||
|
||||
source = openmc.Source()
|
||||
source.particle = 'photon'
|
||||
...
|
||||
|
||||
settings.source = source
|
||||
|
||||
For a full list of all classes related to statistical distributions, see
|
||||
:ref:`pythonapi_stats`.
|
||||
|
||||
|
||||
---------------
|
||||
Shannon Entropy
|
||||
---------------
|
||||
|
|
@ -190,6 +199,52 @@ property::
|
|||
|
||||
settings.entropy_mesh = m
|
||||
|
||||
----------------
|
||||
Photon Transport
|
||||
----------------
|
||||
|
||||
In addition to neutrons, OpenMC is also capable of simulating the passage of
|
||||
photons through matter. This allows the modeling of photon production from
|
||||
neutrons as well as pure photon calculations. The
|
||||
:attr:`Settings.photon_transport` attribute can be used to enable photon
|
||||
transport::
|
||||
|
||||
settings.photon_transport = True
|
||||
|
||||
The way in which OpenMC handles secondary charged particles can be specified
|
||||
with the :attr:`Settings.electron_treatment` attribute. By default, the
|
||||
:ref:`thick-target bremsstrahlung <ttb>` (TTB) approximation is used to generate
|
||||
bremsstrahlung radiation emitted by electrons and positrons created in photon
|
||||
interactions. To neglect secondary bremsstrahlung photons and instead deposit
|
||||
all energy from electrons locally, the local energy deposition option can be
|
||||
selected::
|
||||
|
||||
settings.electron_treatment = 'led'
|
||||
|
||||
.. warning::
|
||||
Currently, collision stopping powers used in the TTB approximation come from
|
||||
the `NIST ESTAR database`_, which provides data for each element calculated
|
||||
using by default the material density at standard temperature and pressure.
|
||||
In OpenMC, stopping powers for compounds are calculated from this elemental
|
||||
data using Bragg's additivity rule. However, this is not a good
|
||||
approximation --- the collision stopping power is a function of certain
|
||||
quantities, such as the mean excitation energy and particularly the density
|
||||
effect correction, that depend on material properties. Data for constituent
|
||||
elements in a compound cannot simply be summed together, but rather these
|
||||
quantities should be calculated for the material. This treatment will be
|
||||
especially poor when the density of a material is different from the
|
||||
densities used in the NIST data.
|
||||
|
||||
.. note::
|
||||
Some features related to photon transport are not currently implemented,
|
||||
including:
|
||||
|
||||
* Tallying photon energy deposition.
|
||||
* Properly accounting for energy deposition in coupled n-p calculations.
|
||||
* Generating a photon source from a neutron calculation that can be used
|
||||
for a later fixed source photon calculation.
|
||||
* Photoneutron reactions.
|
||||
|
||||
--------------------------
|
||||
Generation of Output Files
|
||||
--------------------------
|
||||
|
|
@ -224,3 +279,5 @@ As an example, to write a statepoint file every five batches::
|
|||
|
||||
settings.batches = n
|
||||
settings.statepoint = {'batches': range(5, n + 5, 5)}
|
||||
|
||||
.. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html
|
||||
|
|
|
|||
964
examples/jupyter/nuclear-data-resonance-covariance.ipynb
Normal file
964
examples/jupyter/nuclear-data-resonance-covariance.ipynb
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -14,6 +14,7 @@ extern "C" {
|
|||
double uvw[3];
|
||||
double E;
|
||||
int delayed_group;
|
||||
int particle;
|
||||
};
|
||||
|
||||
int openmc_calculate_volumes();
|
||||
|
|
@ -51,7 +52,7 @@ extern "C" {
|
|||
int openmc_init_f(const int* intracomm);
|
||||
int openmc_legendre_filter_get_order(int32_t index, int* order);
|
||||
int openmc_legendre_filter_set_order(int32_t index, int order);
|
||||
int openmc_load_nuclide(char name[]);
|
||||
int openmc_load_nuclide(const char name[]);
|
||||
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);
|
||||
|
|
@ -92,18 +93,23 @@ extern "C" {
|
|||
int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]);
|
||||
int openmc_statepoint_write(const char filename[]);
|
||||
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);
|
||||
int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n);
|
||||
int openmc_tally_get_n_realizations(int32_t index, int32_t* n);
|
||||
int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n);
|
||||
int openmc_tally_get_scores(int32_t index, int** scores, int* n);
|
||||
int openmc_tally_get_type(int32_t index, int32_t* type);
|
||||
int openmc_tally_reset(int32_t index);
|
||||
int openmc_tally_results(int32_t index, double** ptr, int shape_[3]);
|
||||
int openmc_tally_set_active(int32_t index, bool active);
|
||||
int openmc_tally_set_estimator(int32_t index, const char* estimator);
|
||||
int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices);
|
||||
int openmc_tally_set_id(int32_t index, int32_t id);
|
||||
int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides);
|
||||
int openmc_tally_set_scores(int32_t index, int n, const char** scores);
|
||||
int openmc_tally_set_type(int32_t index, const char* type);
|
||||
int openmc_tally_update_type(int32_t index, const char* type);
|
||||
int openmc_zernike_filter_get_order(int32_t index, int* order);
|
||||
int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r);
|
||||
int openmc_zernike_filter_set_order(int32_t index, int order);
|
||||
|
|
@ -127,6 +133,7 @@ extern "C" {
|
|||
extern char openmc_err_msg[256];
|
||||
extern double openmc_keff;
|
||||
extern double openmc_keff_std;
|
||||
extern int32_t gen_per_batch;
|
||||
extern int32_t n_batches;
|
||||
extern int32_t n_cells;
|
||||
extern int32_t n_filters;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ Indicates the default path to a directory containing windowed multipole data if
|
|||
the user has not specified the <multipole_library> tag in
|
||||
.I materials.xml\fP.
|
||||
.SH LICENSE
|
||||
Copyright \(co 2011-2018 Massachusetts Institute of Technology.
|
||||
Copyright \(co 2011-2018 Massachusetts Institute of Technology and OpenMC
|
||||
contributors.
|
||||
.PP
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
|
@ -79,7 +80,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
.SH REPORTING BUGS
|
||||
The OpenMC source code is hosted on GitHub at
|
||||
https://github.com/mit-crpg/openmc. With a github account, you can submit issues
|
||||
https://github.com/openmc-dev/openmc. With a github account, you can submit issues
|
||||
directly on the github repository that will then be reviewed by OpenMC
|
||||
developers. Alternatively, you can send a bug report to
|
||||
.I openmc-users@googlegroups.com\fP.
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ def run_in_memory(intracomm=None):
|
|||
MPI intracommunicator
|
||||
|
||||
"""
|
||||
init(intracomm)
|
||||
init(intracomm=intracomm)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ _dll.openmc_global_tallies.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
|
||||
_dll.openmc_tally_get_estimator.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_tally_get_estimator.restype = c_int
|
||||
_dll.openmc_tally_get_estimator.errcheck = _error_handler
|
||||
_dll.openmc_tally_get_id.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_tally_get_id.restype = c_int
|
||||
_dll.openmc_tally_get_id.errcheck = _error_handler
|
||||
|
|
@ -47,6 +50,9 @@ _dll.openmc_tally_get_scores.argtypes = [
|
|||
c_int32, POINTER(POINTER(c_int)), POINTER(c_int)]
|
||||
_dll.openmc_tally_get_scores.restype = c_int
|
||||
_dll.openmc_tally_get_scores.errcheck = _error_handler
|
||||
_dll.openmc_tally_get_type.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_tally_get_type.restype = c_int
|
||||
_dll.openmc_tally_get_type.errcheck = _error_handler
|
||||
_dll.openmc_tally_results.argtypes = [
|
||||
c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)]
|
||||
_dll.openmc_tally_results.restype = c_int
|
||||
|
|
@ -57,6 +63,9 @@ _dll.openmc_tally_set_active.errcheck = _error_handler
|
|||
_dll.openmc_tally_set_filters.argtypes = [c_int32, c_int, POINTER(c_int32)]
|
||||
_dll.openmc_tally_set_filters.restype = c_int
|
||||
_dll.openmc_tally_set_filters.errcheck = _error_handler
|
||||
_dll.openmc_tally_set_estimator.argtypes = [c_int32, c_char_p]
|
||||
_dll.openmc_tally_set_estimator.restype = c_int
|
||||
_dll.openmc_tally_set_estimator.errcheck = _error_handler
|
||||
_dll.openmc_tally_set_id.argtypes = [c_int32, c_int32]
|
||||
_dll.openmc_tally_set_id.restype = c_int
|
||||
_dll.openmc_tally_set_id.errcheck = _error_handler
|
||||
|
|
@ -69,6 +78,9 @@ _dll.openmc_tally_set_scores.errcheck = _error_handler
|
|||
_dll.openmc_tally_set_type.argtypes = [c_int32, c_char_p]
|
||||
_dll.openmc_tally_set_type.restype = c_int
|
||||
_dll.openmc_tally_set_type.errcheck = _error_handler
|
||||
_dll.openmc_tally_update_type.argtypes = [c_int32, c_char_p]
|
||||
_dll.openmc_tally_update_type.restype = c_int
|
||||
_dll.openmc_tally_update_type.errcheck = _error_handler
|
||||
|
||||
|
||||
_SCORES = {
|
||||
|
|
@ -78,6 +90,13 @@ _SCORES = {
|
|||
-12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt',
|
||||
-15: 'fission-q-recoverable', -16: 'decay-rate'
|
||||
}
|
||||
_ESTIMATORS = {
|
||||
1: 'analog', 2: 'tracklength', 3: 'collision'
|
||||
}
|
||||
_TALLY_TYPES = {
|
||||
1: 'volume', 2: 'mesh-surface', 3: 'surface'
|
||||
}
|
||||
|
||||
|
||||
|
||||
def global_tallies():
|
||||
|
|
@ -140,6 +159,8 @@ class Tally(_FortranObjectWithID):
|
|||
----------
|
||||
id : int
|
||||
ID of the tally
|
||||
estimator: str
|
||||
Estimator type of tally (analog, tracklength, collision)
|
||||
filters : list
|
||||
List of tally filters
|
||||
mean : numpy.ndarray
|
||||
|
|
@ -152,6 +173,8 @@ class Tally(_FortranObjectWithID):
|
|||
Array of tally results
|
||||
std_dev : numpy.ndarray
|
||||
An array containing the sample standard deviation for each bin
|
||||
type : str
|
||||
Type of tally (volume, mesh_surface, surface)
|
||||
|
||||
"""
|
||||
__instances = WeakValueDictionary()
|
||||
|
|
@ -190,6 +213,30 @@ class Tally(_FortranObjectWithID):
|
|||
_dll.openmc_tally_get_active(self._index, active)
|
||||
return active.value
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
type = c_int32()
|
||||
_dll.openmc_tally_get_type(self._index, type)
|
||||
return _TALLY_TYPES[type.value]
|
||||
|
||||
@type.setter
|
||||
def type(self, type):
|
||||
_dll.openmc_tally_update_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]
|
||||
|
||||
@estimator.setter
|
||||
def estimator(self, estimator):
|
||||
_dll.openmc_tally_set_estimator(self._index, estimator.encode())
|
||||
|
||||
@active.setter
|
||||
def active(self, active):
|
||||
_dll.openmc_tally_set_active(self._index, active)
|
||||
|
|
|
|||
28521
openmc/data/BREMX.DAT
Normal file
28521
openmc/data/BREMX.DAT
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -9,12 +9,13 @@ WMP_VERSION = 'v0.2'
|
|||
|
||||
from .data import *
|
||||
from .neutron import *
|
||||
from .photon import *
|
||||
from .decay import *
|
||||
from .reaction import *
|
||||
from .ace import *
|
||||
from . import ace
|
||||
from .angle_distribution import *
|
||||
from .function import *
|
||||
from .endf import *
|
||||
from . import endf
|
||||
from .energy_distribution import *
|
||||
from .product import *
|
||||
from .angle_energy import *
|
||||
|
|
@ -27,5 +28,6 @@ from .urr import *
|
|||
from .library import *
|
||||
from .fission_energy import *
|
||||
from .resonance import *
|
||||
from .resonance_covariance import *
|
||||
from .multipole import *
|
||||
from .grid import *
|
||||
|
|
|
|||
|
|
@ -16,13 +16,82 @@ generates ACE-format cross sections.
|
|||
"""
|
||||
|
||||
from os import SEEK_CUR
|
||||
from pathlib import PurePath
|
||||
import struct
|
||||
import sys
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openmc.mixin import EqualityMixin
|
||||
from openmc.data.endf import _ENDF_FLOAT_RE
|
||||
import openmc.checkvalue as cv
|
||||
from .data import ATOMIC_SYMBOL, gnd_name
|
||||
from .endf import ENDF_FLOAT_RE
|
||||
|
||||
|
||||
def get_metadata(zaid, metastable_scheme='nndc'):
|
||||
"""Return basic identifying data for a nuclide with a given ZAID.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zaid : int
|
||||
ZAID (1000*Z + A) obtained from a library
|
||||
metastable_scheme : {'nndc', 'mcnp'}
|
||||
Determine how ZAID identifiers are to be interpreted in the case of
|
||||
a metastable nuclide. Because the normal ZAID (=1000*Z + A) does not
|
||||
encode metastable information, different conventions are used among
|
||||
different libraries. In MCNP libraries, the convention is to add 400
|
||||
for a metastable nuclide except for Am242m, for which 95242 is
|
||||
metastable and 95642 (or 1095242 in newer libraries) is the ground
|
||||
state. For NNDC libraries, ZAID is given as 1000*Z + A + 100*m.
|
||||
|
||||
Returns
|
||||
-------
|
||||
name : str
|
||||
Name of the table
|
||||
element : str
|
||||
The atomic symbol of the isotope in the table; e.g., Zr.
|
||||
Z : int
|
||||
Number of protons in the nucleus
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_type('zaid', zaid, int)
|
||||
cv.check_value('metastable_scheme', metastable_scheme, ['nndc', 'mcnp'])
|
||||
|
||||
Z = zaid // 1000
|
||||
mass_number = zaid % 1000
|
||||
|
||||
if metastable_scheme == 'mcnp':
|
||||
if zaid > 1000000:
|
||||
# New SZA format
|
||||
Z = Z % 1000
|
||||
if zaid == 1095242:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = zaid // 1000000
|
||||
else:
|
||||
if zaid == 95242:
|
||||
metastable = 1
|
||||
elif zaid == 95642:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
elif metastable_scheme == 'nndc':
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
|
||||
while mass_number > 3 * Z:
|
||||
mass_number -= 100
|
||||
|
||||
# Determine name
|
||||
element = ATOMIC_SYMBOL[Z]
|
||||
name = gnd_name(Z, mass_number, metastable)
|
||||
|
||||
return (name, element, Z, mass_number, metastable)
|
||||
|
||||
|
||||
def ascii_to_binary(ascii_file, binary_file):
|
||||
"""Convert an ACE file in ASCII format (type 1) to binary format (type 2).
|
||||
|
|
@ -160,7 +229,7 @@ class Library(EqualityMixin):
|
|||
|
||||
# Determine whether file is ASCII or binary
|
||||
try:
|
||||
fh = open(filename, 'rb')
|
||||
fh = open(str(filename), 'rb')
|
||||
# Grab 10 lines of the library
|
||||
sb = b''.join([fh.readline() for i in range(10)])
|
||||
|
||||
|
|
@ -349,7 +418,7 @@ class Library(EqualityMixin):
|
|||
# after it). If it's too short, then we apply the ENDF float regular
|
||||
# expression. We don't do this by default because it's expensive!
|
||||
if xss.size != nxs[1] + 1:
|
||||
datastr = _ENDF_FLOAT_RE.sub(r'\1e\2', datastr)
|
||||
datastr = ENDF_FLOAT_RE.sub(r'\1e\2', datastr)
|
||||
xss = np.fromstring(datastr, sep=' ')
|
||||
assert xss.size == nxs[1] + 1
|
||||
|
||||
|
|
|
|||
BIN
openmc/data/compton_profiles.h5
Normal file
BIN
openmc/data/compton_profiles.h5
Normal file
Binary file not shown.
|
|
@ -22,10 +22,31 @@ from .function import Tabulated1D, INTERPOLATION_SCHEME
|
|||
from openmc.stats.univariate import Uniform, Tabular, Legendre
|
||||
|
||||
|
||||
LIBRARIES = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF',
|
||||
4: 'ENDF/B High Energy', 5: 'CENDL', 6: 'JENDL',
|
||||
31: 'INDL/V', 32: 'INDL/A', 33: 'FENDL', 34: 'IRDF',
|
||||
35: 'BROND', 36: 'INGDB-90', 37: 'FENDL/A', 41: 'BROND'}
|
||||
_LIBRARY = {0: 'ENDF/B', 1: 'ENDF/A', 2: 'JEFF', 3: 'EFF',
|
||||
4: 'ENDF/B High Energy', 5: 'CENDL', 6: 'JENDL',
|
||||
17: 'TENDL', 18: 'ROSFOND', 21: 'SG-21', 31: 'INDL/V',
|
||||
32: 'INDL/A', 33: 'FENDL', 34: 'IRDF', 35: 'BROND',
|
||||
36: 'INGDB-90', 37: 'FENDL/A', 41: 'BROND'}
|
||||
|
||||
_SUBLIBRARY = {
|
||||
0: 'Photo-nuclear data',
|
||||
1: 'Photo-induced fission product yields',
|
||||
3: 'Photo-atomic data',
|
||||
4: 'Radioactive decay data',
|
||||
5: 'Spontaneous fission product yields',
|
||||
6: 'Atomic relaxation data',
|
||||
10: 'Incident-neutron data',
|
||||
11: 'Neutron-induced fission product yields',
|
||||
12: 'Thermal neutron scattering data',
|
||||
19: 'Neutron standards',
|
||||
113: 'Electro-atomic data',
|
||||
10010: 'Incident-proton data',
|
||||
10011: 'Proton-induced fission product yields',
|
||||
10020: 'Incident-deuteron data',
|
||||
10030: 'Incident-triton data',
|
||||
20030: 'Incident-helion (3He) data',
|
||||
20040: 'Incident-alpha data'
|
||||
}
|
||||
|
||||
SUM_RULES = {1: [2, 3],
|
||||
3: [4, 5, 11, 16, 17, 22, 23, 24, 25, 27, 28, 29, 30, 32, 33, 34, 35,
|
||||
|
|
@ -45,7 +66,7 @@ SUM_RULES = {1: [2, 3],
|
|||
106: list(range(750, 800)),
|
||||
107: list(range(800, 850))}
|
||||
|
||||
_ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)')
|
||||
ENDF_FLOAT_RE = re.compile(r'([\s\-\+]?\d*\.\d+)([\+\-]\d+)')
|
||||
|
||||
|
||||
def float_endf(s):
|
||||
|
|
@ -68,7 +89,25 @@ def float_endf(s):
|
|||
The number
|
||||
|
||||
"""
|
||||
return float(_ENDF_FLOAT_RE.sub(r'\1e\2', s))
|
||||
return float(ENDF_FLOAT_RE.sub(r'\1e\2', s))
|
||||
|
||||
|
||||
def _int_endf(s):
|
||||
"""Convert string to int. Used for INTG records where blank entries
|
||||
indicate a 0.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
s : str
|
||||
Integer or spaces
|
||||
|
||||
Returns
|
||||
-------
|
||||
integer
|
||||
The number or 0
|
||||
"""
|
||||
s = s.strip()
|
||||
return int(s) if s else 0
|
||||
|
||||
|
||||
def get_text_record(file_obj):
|
||||
|
|
@ -250,6 +289,50 @@ def get_tab2_record(file_obj):
|
|||
return params, Tabulated2D(breakpoints, interpolation)
|
||||
|
||||
|
||||
def get_intg_record(file_obj):
|
||||
"""
|
||||
Return data from an INTG record in an ENDF-6 file. Used to store the
|
||||
covariance matrix in a compact format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_obj : file-like object
|
||||
ENDF-6 file to read from
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
The correlation matrix described in the INTG record
|
||||
"""
|
||||
# determine how many items are in list and NDIGIT
|
||||
items = get_cont_record(file_obj)
|
||||
ndigit = int(items[2])
|
||||
npar = int(items[3]) # Number of parameters
|
||||
nlines = int(items[4]) # Lines to read
|
||||
NROW_RULES = {2: 18, 3: 12, 4: 11, 5: 9, 6: 8}
|
||||
nrow = NROW_RULES[ndigit]
|
||||
|
||||
# read lines and build correlation matrix
|
||||
corr = np.identity(npar)
|
||||
for i in range(nlines):
|
||||
line = file_obj.readline()
|
||||
ii = _int_endf(line[:5]) - 1 # -1 to account for 0 indexing
|
||||
jj = _int_endf(line[5:10]) - 1
|
||||
factor = 10**ndigit
|
||||
for j in range(nrow):
|
||||
if jj+j >= ii:
|
||||
break
|
||||
element = _int_endf(line[11+(ndigit+1)*j:11+(ndigit+1)*(j+1)])
|
||||
if element > 0:
|
||||
corr[ii, jj] = (element+0.5)/factor
|
||||
elif element < 0:
|
||||
corr[ii, jj] = (element-0.5)/factor
|
||||
|
||||
# Symmetrize the correlation matrix
|
||||
corr = corr + corr.T - np.diag(corr.diagonal())
|
||||
return corr
|
||||
|
||||
|
||||
def get_evaluations(filename):
|
||||
"""Return a list of all evaluations within an ENDF file.
|
||||
|
||||
|
|
@ -288,7 +371,7 @@ class Evaluation(object):
|
|||
Attributes
|
||||
----------
|
||||
info : dict
|
||||
Miscallaneous information about the evaluation.
|
||||
Miscellaneous information about the evaluation.
|
||||
target : dict
|
||||
Information about the target material, such as its mass, isomeric state,
|
||||
whether it's stable, and whether it's fissionable.
|
||||
|
|
@ -348,6 +431,14 @@ class Evaluation(object):
|
|||
|
||||
self._read_header()
|
||||
|
||||
def __repr__(self):
|
||||
if 'zsymam' in self.target:
|
||||
name = self.target['zsymam'].replace(' ', '')
|
||||
else:
|
||||
name = 'Unknown'
|
||||
return '<{} for {} {}>'.format(self.info['sublibrary'], name,
|
||||
self.info['library'])
|
||||
|
||||
def _read_header(self):
|
||||
file_obj = io.StringIO(self.section[1, 451])
|
||||
|
||||
|
|
@ -360,8 +451,7 @@ class Evaluation(object):
|
|||
self._LRP = items[2]
|
||||
self.target['fissionable'] = (items[3] == 1)
|
||||
try:
|
||||
global LIBRARIES
|
||||
library = LIBRARIES[items[4]]
|
||||
library = _LIBRARY[items[4]]
|
||||
except KeyError:
|
||||
library = 'Unknown'
|
||||
self.info['modification'] = items[5]
|
||||
|
|
@ -384,7 +474,7 @@ class Evaluation(object):
|
|||
self.projectile['mass'] = items[0]
|
||||
self.info['energy_max'] = items[1]
|
||||
library_release = items[2]
|
||||
self.info['sublibrary'] = items[4]
|
||||
self.info['sublibrary'] = _SUBLIBRARY[items[4]]
|
||||
library_version = items[5]
|
||||
self.info['library'] = (library, library_version, library_release)
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -6,173 +6,18 @@ import sys
|
|||
import h5py
|
||||
import numpy as np
|
||||
|
||||
from .data import ATOMIC_SYMBOL, EV_PER_MEV
|
||||
from .endf import get_cont_record, get_list_record, Evaluation
|
||||
from .function import Function1D, Tabulated1D, Polynomial, Sum
|
||||
from .data import EV_PER_MEV
|
||||
from .endf import get_cont_record, get_list_record, get_tab1_record, Evaluation
|
||||
from .function import Function1D, Tabulated1D, Polynomial, sum_functions
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
||||
|
||||
def _extract_458_data(ev):
|
||||
"""Read an ENDF file and extract the MF=1, MT=458 values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ev : openmc.data.Evaluation
|
||||
ENDF evaluation
|
||||
|
||||
Returns
|
||||
-------
|
||||
value : dict of str to list of float
|
||||
Dictionary that gives lists of coefficients for each energy component.
|
||||
The keys are the 2-3 letter strings used in ENDF-102, e.g. 'EFR' and
|
||||
'ET'. The list will have a length of 1 for Sher-Beck data, more for
|
||||
polynomial data.
|
||||
uncertainty : dict of str to list of float
|
||||
A dictionary with the same format as above. This is probably a
|
||||
one-standard deviation value, but that is not specified explicitly in
|
||||
ENDF-102. Also, some evaluations will give zero uncertainty. Use with
|
||||
caution.
|
||||
|
||||
"""
|
||||
cv.check_type('evaluation', ev, Evaluation)
|
||||
|
||||
if not ev.target['fissionable']:
|
||||
# This nuclide isn't fissionable.
|
||||
return None
|
||||
|
||||
if (1, 458) not in ev.section:
|
||||
# No 458 data here.
|
||||
return None
|
||||
|
||||
file_obj = StringIO(ev.section[1, 458])
|
||||
|
||||
# Read the number of coefficients in this LIST record.
|
||||
items = get_cont_record(file_obj)
|
||||
NPL = items[3]
|
||||
|
||||
# Parse the ENDF LIST into an array.
|
||||
items, data = get_list_record(file_obj)
|
||||
|
||||
# Declare the coefficient names and the order they are given in. The LIST
|
||||
# contains a value followed immediately by an uncertainty for each of these
|
||||
# components, times the polynomial order + 1.
|
||||
labels = ('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER', 'ET')
|
||||
|
||||
# Associate each set of values and uncertainties with its label.
|
||||
value = {}
|
||||
uncertainty = {}
|
||||
for i, label in enumerate(labels):
|
||||
value[label] = data[2*i::18]
|
||||
uncertainty[label] = data[2*i + 1::18]
|
||||
|
||||
# In ENDF/B-7.1, data for 2nd-order coefficients were mistakenly not
|
||||
# converted from MeV to eV. Check for this error and fix it if present.
|
||||
n_coeffs = len(value['EFR'])
|
||||
if n_coeffs == 3: # Only check 2nd-order data.
|
||||
# Check each energy component for the error. If a 1 MeV neutron
|
||||
# causes a change of more than 100 MeV, we know something is wrong.
|
||||
error_present = False
|
||||
for coeffs in value.values():
|
||||
second_order = coeffs[2]
|
||||
if abs(second_order) * 1e12 > 1e8:
|
||||
error_present = True
|
||||
break
|
||||
|
||||
# If we found the error, reduce all 2nd-order coeffs by 10**6.
|
||||
if error_present:
|
||||
for coeffs in value.values():
|
||||
coeffs[2] /= EV_PER_MEV
|
||||
for coeffs in uncertainty.values():
|
||||
coeffs[2] /= EV_PER_MEV
|
||||
|
||||
return value, uncertainty
|
||||
|
||||
|
||||
def write_compact_458_library(endf_files, output_name='fission_Q_data.h5',
|
||||
comment=None, verbose=False):
|
||||
"""Read ENDF files, strip the MF=1 MT=458 data and write to small HDF5.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
endf_files : Collection of str
|
||||
Strings giving the paths to the ENDF files that will be parsed for data.
|
||||
output_name : str
|
||||
Name of the output HDF5 file. Default is 'fission_Q_data.h5'.
|
||||
comment : str
|
||||
Comment to write in the output HDF5 file. Defaults to no comment.
|
||||
verbose : bool
|
||||
If True, print the name of each isomer as it is read. Defaults to
|
||||
False.
|
||||
|
||||
"""
|
||||
# Open the output file.
|
||||
out = h5py.File(output_name, 'w', libver='earliest')
|
||||
|
||||
# Write comments, if given. This commented out comment is the one used for
|
||||
# the library distributed with OpenMC.
|
||||
#comment = ('This data is extracted from ENDF/B-VII.1 library. Thanks '
|
||||
# 'evaluators, for all your hard work :) Citation: '
|
||||
# 'M. B. Chadwick, M. Herman, P. Oblozinsky, '
|
||||
# 'M. E. Dunn, Y. Danon, A. C. Kahler, D. L. Smith, '
|
||||
# 'B. Pritychenko, G. Arbanas, R. Arcilla, R. Brewer, '
|
||||
# 'D. A. Brown, R. Capote, A. D. Carlson, Y. S. Cho, H. Derrien, '
|
||||
# 'K. Guber, G. M. Hale, S. Hoblit, S. Holloway, T. D. Johnson, '
|
||||
# 'T. Kawano, B. C. Kiedrowski, H. Kim, S. Kunieda, '
|
||||
# 'N. M. Larson, L. Leal, J. P. Lestone, R. C. Little, '
|
||||
# 'E. A. McCutchan, R. E. MacFarlane, M. MacInnes, '
|
||||
# 'C. M. Mattoon, R. D. McKnight, S. F. Mughabghab, '
|
||||
# 'G. P. A. Nobre, G. Palmiotti, A. Palumbo, M. T. Pigni, '
|
||||
# 'V. G. Pronyaev, R. O. Sayer, A. A. Sonzogni, N. C. Summers, '
|
||||
# 'P. Talou, I. J. Thompson, A. Trkov, R. L. Vogt, '
|
||||
# 'S. C. van der Marck, A. Wallner, M. C. White, D. Wiarda, '
|
||||
# 'and P. G. Young. ENDF/B-VII.1 nuclear data for science and '
|
||||
# 'technology: Cross sections, covariances, fission product '
|
||||
# 'yields and decay data", Nuclear Data Sheets, '
|
||||
# '112(12):2887-2996 (2011).')
|
||||
if comment is not None:
|
||||
out.attrs['comment'] = np.string_(comment)
|
||||
|
||||
# Declare the order of the components. Use fixed-length numpy strings
|
||||
# because they work well with h5py.
|
||||
labels = np.array(('EFR', 'ENP', 'END', 'EGP', 'EGD', 'EB', 'ENU', 'ER',
|
||||
'ET'), dtype='S3')
|
||||
out.attrs['component order'] = labels
|
||||
|
||||
# Iterate over the given files.
|
||||
if verbose: print('Reading ENDF files:')
|
||||
for fname in endf_files:
|
||||
if verbose: print(fname)
|
||||
|
||||
ev = Evaluation(fname)
|
||||
|
||||
# Skip non-fissionable nuclides.
|
||||
if not ev.target['fissionable']:
|
||||
continue
|
||||
|
||||
# Get the important bits.
|
||||
data = _extract_458_data(ev)
|
||||
if data is None: continue
|
||||
value, uncertainty = data
|
||||
|
||||
# Make a group for this isomer.
|
||||
name = ATOMIC_SYMBOL[ev.target['atomic_number']] + \
|
||||
str(ev.target['mass_number'])
|
||||
if ev.target['isomeric_state'] != 0:
|
||||
name += '_m' + str(ev.target['isomeric_state'])
|
||||
nuclide_group = out.create_group(name)
|
||||
|
||||
# Write all the coefficients into one array. The first dimension gives
|
||||
# the component (e.g. fragments or prompt neutrons); the second switches
|
||||
# between value and uncertainty; the third gives the polynomial order.
|
||||
n_coeffs = len(value['EFR'])
|
||||
data_out = np.zeros((len(labels), 2, n_coeffs))
|
||||
for i, label in enumerate(labels):
|
||||
data_out[i, 0, :] = value[label.decode()]
|
||||
data_out[i, 1, :] = uncertainty[label.decode()]
|
||||
nuclide_group.create_dataset('data', data=data_out)
|
||||
|
||||
out.close()
|
||||
_NAMES = (
|
||||
'fragments', 'prompt_neutrons', 'delayed_neutrons',
|
||||
'prompt_photons', 'delayed_photons', 'betas',
|
||||
'neutrinos', 'recoverable', 'total'
|
||||
)
|
||||
|
||||
|
||||
class FissionEnergyRelease(EqualityMixin):
|
||||
|
|
@ -249,14 +94,15 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
- incident neutron energy).
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self._fragments = None
|
||||
self._prompt_neutrons = None
|
||||
self._delayed_neutrons = None
|
||||
self._prompt_photons = None
|
||||
self._delayed_photons = None
|
||||
self._betas = None
|
||||
self._neutrinos = None
|
||||
def __init__(self, fragments, prompt_neutrons, delayed_neutrons,
|
||||
prompt_photons, delayed_photons, betas, neutrinos):
|
||||
self.fragments = fragments
|
||||
self.prompt_neutrons = prompt_neutrons
|
||||
self.delayed_neutrons = delayed_neutrons
|
||||
self.prompt_photons = prompt_photons
|
||||
self.delayed_photons = delayed_photons
|
||||
self.betas = betas
|
||||
self.neutrinos = neutrinos
|
||||
|
||||
@property
|
||||
def fragments(self):
|
||||
|
|
@ -288,27 +134,33 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
|
||||
@property
|
||||
def recoverable(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.delayed_neutrons,
|
||||
self.prompt_photons, self.delayed_photons, self.betas])
|
||||
components = ['fragments', 'prompt_neutrons', 'delayed_neutrons',
|
||||
'prompt_photons', 'delayed_photons', 'betas']
|
||||
return sum_functions(getattr(self, c) for c in components)
|
||||
|
||||
@property
|
||||
def total(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.delayed_neutrons,
|
||||
self.prompt_photons, self.delayed_photons, self.betas,
|
||||
self.neutrinos])
|
||||
components = ['fragments', 'prompt_neutrons', 'delayed_neutrons',
|
||||
'prompt_photons', 'delayed_photons', 'betas',
|
||||
'neutrinos']
|
||||
return sum_functions(getattr(self, c) for c in components)
|
||||
|
||||
@property
|
||||
def q_prompt(self):
|
||||
return Sum([self.fragments, self.prompt_neutrons, self.prompt_photons,
|
||||
lambda E: -E])
|
||||
# Use a polynomial to subtract incident energy.
|
||||
funcs = [self.fragments, self.prompt_neutrons, self.prompt_photons,
|
||||
Polynomial((0.0, -1.0))]
|
||||
return sum_functions(funcs)
|
||||
|
||||
@property
|
||||
def q_recoverable(self):
|
||||
return Sum([self.recoverable, lambda E: -E])
|
||||
# Use a polynomial to subtract incident energy.
|
||||
return sum_functions([self.recoverable, Polynomial((0.0, -1.0))])
|
||||
|
||||
@property
|
||||
def q_total(self):
|
||||
return Sum([self.total, lambda E: -E])
|
||||
# Use a polynomial to subtract incident energy.
|
||||
return sum_functions([self.total, Polynomial((0.0, -1.0))])
|
||||
|
||||
@fragments.setter
|
||||
def fragments(self, energy_release):
|
||||
|
|
@ -345,92 +197,6 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
cv.check_type('neutrinos', energy_release, Callable)
|
||||
self._neutrinos = energy_release
|
||||
|
||||
@classmethod
|
||||
def _from_dictionary(cls, energy_release, incident_neutron):
|
||||
"""Generate fission energy release data from a dictionary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_release : dict of str to list of float
|
||||
Dictionary that gives lists of coefficients for each energy
|
||||
component. The keys are the 2-3 letter strings used in ENDF-102,
|
||||
e.g. 'EFR' and 'ET'. The list will have a length of 1 for Sher-Beck
|
||||
data, more for polynomial data.
|
||||
incident_neutron : openmc.data.IncidentNeutron
|
||||
Corresponding incident neutron dataset
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease
|
||||
Fission energy release data
|
||||
|
||||
"""
|
||||
out = cls()
|
||||
|
||||
# How many coefficients are given for each component? If we only find
|
||||
# one value for each, then we need to use the Sher-Beck formula for
|
||||
# energy dependence. Otherwise, it is a polynomial.
|
||||
n_coeffs = len(energy_release['EFR'])
|
||||
if n_coeffs > 1:
|
||||
out.fragments = Polynomial(energy_release['EFR'])
|
||||
out.prompt_neutrons = Polynomial(energy_release['ENP'])
|
||||
out.delayed_neutrons = Polynomial(energy_release['END'])
|
||||
out.prompt_photons = Polynomial(energy_release['EGP'])
|
||||
out.delayed_photons = Polynomial(energy_release['EGD'])
|
||||
out.betas = Polynomial(energy_release['EB'])
|
||||
out.neutrinos = Polynomial(energy_release['ENU'])
|
||||
else:
|
||||
# EFR and ENP are energy independent. Use 0-order polynomials to
|
||||
# make a constant function. The energy-dependence of END is
|
||||
# unspecified in ENDF-102 so assume it is independent.
|
||||
out.fragments = Polynomial((energy_release['EFR'][0]))
|
||||
out.prompt_photons = Polynomial((energy_release['EGP'][0]))
|
||||
out.delayed_neutrons = Polynomial((energy_release['END'][0]))
|
||||
|
||||
# EDP, EB, and ENU are linear.
|
||||
out.delayed_photons = Polynomial((energy_release['EGD'][0], -0.075))
|
||||
out.betas = Polynomial((energy_release['EB'][0], -0.075))
|
||||
out.neutrinos = Polynomial((energy_release['ENU'][0], -0.105))
|
||||
|
||||
# Prompt neutrons require nu-data. It is not clear from ENDF-102
|
||||
# whether prompt or total nu value should be used, but the delayed
|
||||
# neutron fraction is so small that the difference is negligible.
|
||||
# MT=18 (n, fission) might not be available so try MT=19 (n, f) as
|
||||
# well.
|
||||
if 18 in incident_neutron.reactions:
|
||||
nu = [p.yield_ for p in incident_neutron[18].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode in ('prompt', 'total')]
|
||||
elif 19 in incident_neutron.reactions:
|
||||
nu = [p.yield_ for p in incident_neutron[19].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode in ('prompt', 'total')]
|
||||
else:
|
||||
raise ValueError('IncidentNeutron data has no fission '
|
||||
'reaction.')
|
||||
if len(nu) == 0:
|
||||
raise ValueError('Nu data is needed to compute fission energy '
|
||||
'release with the Sher-Beck format.')
|
||||
if len(nu) > 1:
|
||||
raise ValueError('Ambiguous prompt/total nu value.')
|
||||
|
||||
nu = nu[0]
|
||||
if isinstance(nu, Tabulated1D):
|
||||
ENP = deepcopy(nu)
|
||||
ENP.y = (energy_release['ENP'] + 1.307 * nu.x
|
||||
- 8.07e6 * (nu.y - nu.y[0]))
|
||||
elif isinstance(nu, Polynomial):
|
||||
if len(nu) == 1:
|
||||
ENP = Polynomial([energy_release['ENP'][0], 1.307])
|
||||
else:
|
||||
ENP = Polynomial(
|
||||
[energy_release['ENP'][0], 1.307 - 8.07e6*nu.coef[1]]
|
||||
+ [-8.07e6*c for c in nu.coef[2:]])
|
||||
|
||||
out.prompt_neutrons = ENP
|
||||
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev, incident_neutron):
|
||||
"""Generate fission energy release data from an ENDF file.
|
||||
|
|
@ -458,16 +224,117 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
raise ValueError('The atomic mass of the ENDF evaluation does '
|
||||
'not match the given IncidentNeutron.')
|
||||
if ev.target['isomeric_state'] != incident_neutron.metastable:
|
||||
raise ValueError('The metastable state of the ENDF evaluation does '
|
||||
'not match the given IncidentNeutron.')
|
||||
raise ValueError('The metastable state of the ENDF evaluation '
|
||||
'does not match the given IncidentNeutron.')
|
||||
if not ev.target['fissionable']:
|
||||
raise ValueError('The ENDF evaluation is not fissionable.')
|
||||
|
||||
# Read the 458 data from the ENDF file.
|
||||
value, uncertainty = _extract_458_data(ev)
|
||||
if (1, 458) not in ev.section:
|
||||
raise ValueError('ENDF evaluation does not have MF=1, MT=458.')
|
||||
|
||||
# Build the object.
|
||||
return cls._from_dictionary(value, incident_neutron)
|
||||
file_obj = StringIO(ev.section[1, 458])
|
||||
|
||||
# Read first record and check whether any components appear as
|
||||
# tabulated functions
|
||||
items = get_cont_record(file_obj)
|
||||
lfc = items[3]
|
||||
nfc = items[5]
|
||||
|
||||
# Parse the ENDF LIST into an array.
|
||||
items, data = get_list_record(file_obj)
|
||||
npoly = items[3]
|
||||
|
||||
# Associate each set of values and uncertainties with its label.
|
||||
functions = {}
|
||||
for i, name in enumerate(_NAMES):
|
||||
coeffs = data[2*i::18]
|
||||
|
||||
# Ignore recoverable and total since we recalculate those directly
|
||||
if name in ('recoverable', 'total'):
|
||||
continue
|
||||
|
||||
# In ENDF/B-VII.1, data for 2nd-order coefficients were mistakenly
|
||||
# not converted from MeV to eV. Check for this error and fix it if
|
||||
# present.
|
||||
if npoly == 2: # Only check 2nd-order data.
|
||||
# If a 5 MeV neutron causes a change of more than 100 MeV, we
|
||||
# know something is wrong.
|
||||
second_order = coeffs[2]
|
||||
if abs(second_order) * (5e6)**2 > 1e8:
|
||||
# If we found the error, reduce 2nd-order coeff by 10**6.
|
||||
coeffs[2] /= EV_PER_MEV
|
||||
|
||||
# If multiple coefficients were given, we can create the polynomial
|
||||
# and move on to the next component
|
||||
if npoly > 0:
|
||||
functions[name] = Polynomial(coeffs)
|
||||
continue
|
||||
|
||||
# If a single coefficient was given, we need to use the Sher-Beck
|
||||
# formula for energy dependence
|
||||
zeroth_order = coeffs[0]
|
||||
if name in ('delayed_photons', 'betas'):
|
||||
func = Polynomial((zeroth_order, -0.075))
|
||||
elif name == 'neutrinos':
|
||||
func = Polynomial((zeroth_order, -0.105))
|
||||
elif name == 'prompt_neutrons':
|
||||
# Prompt neutrons require nu-data. It is not clear from
|
||||
# ENDF-102 whether prompt or total nu value should be used, but
|
||||
# the delayed neutron fraction is so small that the difference
|
||||
# is negligible. MT=18 (n, fission) might not be available so
|
||||
# try MT=19 (n, f) as well.
|
||||
if 18 in incident_neutron.reactions:
|
||||
nu = [p.yield_ for p in incident_neutron[18].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode in ('prompt', 'total')]
|
||||
elif 19 in incident_neutron.reactions:
|
||||
nu = [p.yield_ for p in incident_neutron[19].products
|
||||
if p.particle == 'neutron'
|
||||
and p.emission_mode in ('prompt', 'total')]
|
||||
else:
|
||||
raise ValueError('IncidentNeutron data has no fission '
|
||||
'reaction.')
|
||||
if len(nu) == 0:
|
||||
raise ValueError(
|
||||
'Nu data is needed to compute fission energy '
|
||||
'release with the Sher-Beck format.'
|
||||
)
|
||||
if len(nu) > 1:
|
||||
raise ValueError('Ambiguous prompt/total nu value.')
|
||||
|
||||
nu = nu[0]
|
||||
if isinstance(nu, Tabulated1D):
|
||||
# Evaluate Sher-Beck polynomial form at each tabulated value
|
||||
func = deepcopy(nu)
|
||||
func.y = (zeroth_order + 1.307*nu.x - 8.07e6*(nu.y - nu.y[0]))
|
||||
elif isinstance(nu, Polynomial):
|
||||
# Combine polynomials
|
||||
if len(nu) == 1:
|
||||
func = Polynomial([zeroth_order, 1.307])
|
||||
else:
|
||||
func = Polynomial(
|
||||
[zeroth_order, 1.307 - 8.07e6*nu.coef[1]]
|
||||
+ [-8.07e6*c for c in nu.coef[2:]])
|
||||
else:
|
||||
func = Polynomial(coeffs)
|
||||
|
||||
functions[name] = func
|
||||
|
||||
# Check for tabulated data
|
||||
if lfc == 1:
|
||||
for _ in range(nfc):
|
||||
# Get tabulated function
|
||||
items, eifc = get_tab1_record(file_obj)
|
||||
|
||||
# Determine which component it is
|
||||
ifc = items[3]
|
||||
name = _NAMES[ifc - 1]
|
||||
|
||||
# Replace value in dictionary
|
||||
functions[name] = eifc
|
||||
|
||||
# Build the object
|
||||
return cls(**functions)
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group):
|
||||
|
|
@ -485,54 +352,16 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
|
||||
"""
|
||||
|
||||
obj = cls()
|
||||
fragments = Function1D.from_hdf5(group['fragments'])
|
||||
prompt_neutrons = Function1D.from_hdf5(group['prompt_neutrons'])
|
||||
delayed_neutrons = Function1D.from_hdf5(group['delayed_neutrons'])
|
||||
prompt_photons = Function1D.from_hdf5(group['prompt_photons'])
|
||||
delayed_photons = Function1D.from_hdf5(group['delayed_photons'])
|
||||
betas = Function1D.from_hdf5(group['betas'])
|
||||
neutrinos = Function1D.from_hdf5(group['neutrinos'])
|
||||
|
||||
obj.fragments = Function1D.from_hdf5(group['fragments'])
|
||||
obj.prompt_neutrons = Function1D.from_hdf5(group['prompt_neutrons'])
|
||||
obj.delayed_neutrons = Function1D.from_hdf5(group['delayed_neutrons'])
|
||||
obj.prompt_photons = Function1D.from_hdf5(group['prompt_photons'])
|
||||
obj.delayed_photons = Function1D.from_hdf5(group['delayed_photons'])
|
||||
obj.betas = Function1D.from_hdf5(group['betas'])
|
||||
obj.neutrinos = Function1D.from_hdf5(group['neutrinos'])
|
||||
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def from_compact_hdf5(cls, fname, incident_neutron):
|
||||
"""Generate fission energy release data from a small HDF5 library.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname : str
|
||||
Path to an HDF5 file containing fission energy release data. This
|
||||
file should have been generated form the
|
||||
:func:`openmc.data.write_compact_458_library` function.
|
||||
incident_neutron : openmc.data.IncidentNeutron
|
||||
Corresponding incident neutron dataset
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.FissionEnergyRelease or None
|
||||
Fission energy release data for the given nuclide if it is present
|
||||
in the data file
|
||||
|
||||
"""
|
||||
|
||||
fin = h5py.File(fname, 'r')
|
||||
|
||||
components = [s.decode() for s in fin.attrs['component order']]
|
||||
|
||||
nuclide_name = ATOMIC_SYMBOL[incident_neutron.atomic_number]
|
||||
nuclide_name += str(incident_neutron.mass_number)
|
||||
if incident_neutron.metastable != 0:
|
||||
nuclide_name += '_m' + str(incident_neutron.metastable)
|
||||
|
||||
if nuclide_name not in fin: return None
|
||||
|
||||
data = {c: fin[nuclide_name + '/data'][i, 0, :]
|
||||
for i, c in enumerate(components)}
|
||||
|
||||
return cls._from_dictionary(data, incident_neutron)
|
||||
return cls(fragments, prompt_neutrons, delayed_neutrons, prompt_photons,
|
||||
delayed_photons, betas, neutrinos)
|
||||
|
||||
def to_hdf5(self, group):
|
||||
"""Write energy release data to an HDF5 group
|
||||
|
|
@ -551,33 +380,5 @@ class FissionEnergyRelease(EqualityMixin):
|
|||
self.delayed_photons.to_hdf5(group, 'delayed_photons')
|
||||
self.betas.to_hdf5(group, 'betas')
|
||||
self.neutrinos.to_hdf5(group, 'neutrinos')
|
||||
|
||||
if isinstance(self.prompt_neutrons, Polynomial):
|
||||
# Add the polynomials for the relevant components together. Use a
|
||||
# Polynomial((0.0, -1.0)) to subtract incident energy.
|
||||
q_prompt = (self.fragments + self.prompt_neutrons +
|
||||
self.prompt_photons + Polynomial((0.0, -1.0)))
|
||||
q_prompt.to_hdf5(group, 'q_prompt')
|
||||
q_recoverable = (self.fragments + self.prompt_neutrons +
|
||||
self.delayed_neutrons + self.prompt_photons +
|
||||
self.delayed_photons + self.betas +
|
||||
Polynomial((0.0, -1.0)))
|
||||
q_recoverable.to_hdf5(group, 'q_recoverable')
|
||||
|
||||
elif isinstance(self.prompt_neutrons, Tabulated1D):
|
||||
# Make a Tabulated1D and evaluate the polynomial components at the
|
||||
# table x points to get new y points. Subtract x from y to remove
|
||||
# incident energy.
|
||||
q_prompt = deepcopy(self.prompt_neutrons)
|
||||
q_prompt.y += self.fragments(q_prompt.x)
|
||||
q_prompt.y += self.prompt_photons(q_prompt.x)
|
||||
q_prompt.y -= q_prompt.x
|
||||
q_prompt.to_hdf5(group, 'q_prompt')
|
||||
q_recoverable = q_prompt
|
||||
q_recoverable.y += self.delayed_neutrons(q_recoverable.x)
|
||||
q_recoverable.y += self.delayed_photons(q_recoverable.x)
|
||||
q_recoverable.y += self.betas(q_recoverable.x)
|
||||
q_recoverable.to_hdf5(group, 'q_recoverable')
|
||||
|
||||
else:
|
||||
raise ValueError('Unrecognized energy release format')
|
||||
self.q_prompt.to_hdf5(group, 'q_prompt')
|
||||
self.q_recoverable.to_hdf5(group, 'q_recoverable')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import Iterable, Callable
|
||||
from functools import reduce
|
||||
from itertools import zip_longest
|
||||
from numbers import Real, Integral
|
||||
|
||||
import numpy as np
|
||||
|
|
@ -13,6 +15,46 @@ INTERPOLATION_SCHEME = {1: 'histogram', 2: 'linear-linear', 3: 'linear-log',
|
|||
4: 'log-linear', 5: 'log-log'}
|
||||
|
||||
|
||||
def sum_functions(funcs):
|
||||
"""Add tabulated/polynomials functions together
|
||||
|
||||
Parameters
|
||||
----------
|
||||
funcs : list of Function1D
|
||||
Functions to add
|
||||
|
||||
Returns
|
||||
-------
|
||||
Function1D
|
||||
Sum of polynomial/tabulated functions
|
||||
|
||||
"""
|
||||
# Copy so we can iterate multiple times
|
||||
funcs = list(funcs)
|
||||
|
||||
# Get x values for all tabulated components
|
||||
xs = []
|
||||
for f in funcs:
|
||||
if isinstance(f, Tabulated1D):
|
||||
xs.append(f.x)
|
||||
if not np.all(f.interpolation == 2):
|
||||
raise ValueError('Only linear-linear tabulated functions '
|
||||
'can be combined')
|
||||
|
||||
if xs:
|
||||
# Take the union of all energies (sorted)
|
||||
x = reduce(np.union1d, xs)
|
||||
|
||||
# Evaluate each function and add together
|
||||
y = sum(f(x) for f in funcs)
|
||||
return Tabulated1D(x, y)
|
||||
else:
|
||||
# If no tabulated functions are present, we need to combine the
|
||||
# polynomials by adding their coefficients
|
||||
coeffs = [sum(x) for x in zip_longest(*funcs, fillvalue=0.0)]
|
||||
return Polynomial(coeffs)
|
||||
|
||||
|
||||
class Function1D(EqualityMixin, metaclass=ABCMeta):
|
||||
"""A function of one independent variable with HDF5 support."""
|
||||
@abstractmethod
|
||||
|
|
|
|||
|
|
@ -52,14 +52,17 @@ class DataLibrary(EqualityMixin):
|
|||
Path to the file to be registered.
|
||||
|
||||
"""
|
||||
h5file = h5py.File(filename, 'r')
|
||||
with h5py.File(filename, 'r') as h5file:
|
||||
|
||||
materials = []
|
||||
filetype = 'neutron'
|
||||
for name in h5file:
|
||||
if name.startswith('c_'):
|
||||
filetype = 'thermal'
|
||||
materials.append(name)
|
||||
materials = []
|
||||
if 'filetype' in h5file.attrs:
|
||||
filetype = h5file.attrs['filetype'].decode().lstrip('data_')
|
||||
else:
|
||||
filetype = 'neutron'
|
||||
for name in h5file:
|
||||
if name.startswith('c_'):
|
||||
filetype = 'thermal'
|
||||
materials.append(name)
|
||||
|
||||
library = {'path': filename, 'type': filetype, 'materials': materials}
|
||||
self.libraries.append(library)
|
||||
|
|
@ -81,10 +84,9 @@ class DataLibrary(EqualityMixin):
|
|||
if common_dir == '':
|
||||
common_dir = '.'
|
||||
|
||||
directory = os.path.relpath(common_dir, os.path.dirname(path))
|
||||
if directory != '.':
|
||||
if os.path.relpath(common_dir, os.path.dirname(path)) != '.':
|
||||
dir_element = ET.SubElement(root, "directory")
|
||||
dir_element.text = directory
|
||||
dir_element.text = os.path.realpath(common_dir)
|
||||
|
||||
for library in self.libraries:
|
||||
lib_element = ET.SubElement(root, "library")
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import numpy as np
|
|||
import h5py
|
||||
|
||||
from . import HDF5_VERSION, HDF5_VERSION_MAJOR
|
||||
from .ace import Library, Table, get_table
|
||||
from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV, gnd_name
|
||||
from .ace import Library, Table, get_table, get_metadata
|
||||
from .data import ATOMIC_SYMBOL, K_BOLTZMANN, EV_PER_MEV
|
||||
from .endf import Evaluation, SUM_RULES, get_head_record, get_tab1_record
|
||||
from .fission_energy import FissionEnergyRelease
|
||||
from .function import Tabulated1D, Sum, ResonancesWithBackground
|
||||
|
|
@ -24,6 +24,7 @@ from .njoy import make_ace
|
|||
from .product import Product
|
||||
from .reaction import Reaction, _get_photon_products_ace
|
||||
from . import resonance as res
|
||||
from . import resonance_covariance as res_cov
|
||||
from .urr import ProbabilityTables
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
|
@ -33,88 +34,26 @@ from openmc.mixin import EqualityMixin
|
|||
_RESONANCE_ENERGY_GRID = np.logspace(-3, 3, 61)
|
||||
|
||||
|
||||
def _get_metadata(zaid, metastable_scheme='nndc'):
|
||||
"""Return basic identifying data for a nuclide with a given ZAID.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
zaid : int
|
||||
ZAID (1000*Z + A) obtained from a library
|
||||
metastable_scheme : {'nndc', 'mcnp'}
|
||||
Determine how ZAID identifiers are to be interpreted in the case of
|
||||
a metastable nuclide. Because the normal ZAID (=1000*Z + A) does not
|
||||
encode metastable information, different conventions are used among
|
||||
different libraries. In MCNP libraries, the convention is to add 400
|
||||
for a metastable nuclide except for Am242m, for which 95242 is
|
||||
metastable and 95642 (or 1095242 in newer libraries) is the ground
|
||||
state. For NNDC libraries, ZAID is given as 1000*Z + A + 100*m.
|
||||
|
||||
Returns
|
||||
-------
|
||||
name : str
|
||||
Name of the table
|
||||
element : str
|
||||
The atomic symbol of the isotope in the table; e.g., Zr.
|
||||
Z : int
|
||||
Number of protons in the nucleus
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
|
||||
"""
|
||||
|
||||
cv.check_type('zaid', zaid, int)
|
||||
cv.check_value('metastable_scheme', metastable_scheme, ['nndc', 'mcnp'])
|
||||
|
||||
Z = zaid // 1000
|
||||
mass_number = zaid % 1000
|
||||
|
||||
if metastable_scheme == 'mcnp':
|
||||
if zaid > 1000000:
|
||||
# New SZA format
|
||||
Z = Z % 1000
|
||||
if zaid == 1095242:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = zaid // 1000000
|
||||
else:
|
||||
if zaid == 95242:
|
||||
metastable = 1
|
||||
elif zaid == 95642:
|
||||
metastable = 0
|
||||
else:
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
elif metastable_scheme == 'nndc':
|
||||
metastable = 1 if mass_number > 300 else 0
|
||||
|
||||
while mass_number > 3 * Z:
|
||||
mass_number -= 100
|
||||
|
||||
# Determine name
|
||||
element = ATOMIC_SYMBOL[Z]
|
||||
name = gnd_name(Z, mass_number, metastable)
|
||||
|
||||
return (name, element, Z, mass_number, metastable)
|
||||
|
||||
|
||||
class IncidentNeutron(EqualityMixin):
|
||||
"""Continuous-energy neutron interaction data.
|
||||
|
||||
Instances of this class are not normally instantiated by the user but rather
|
||||
created using the factory methods :meth:`IncidentNeutron.from_hdf5` and
|
||||
:meth:`IncidentNeutron.from_ace`.
|
||||
This class stores data derived from an ENDF-6 format neutron interaction
|
||||
sublibrary. Instances of this class are not normally instantiated by the
|
||||
user but rather created using the factory methods
|
||||
:meth:`IncidentNeutron.from_hdf5`, :meth:`IncidentNeutron.from_ace`, and
|
||||
:meth:`IncidentNeutron.from_endf`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of the nuclide using the GND naming convention
|
||||
atomic_number : int
|
||||
Number of protons in the nucleus
|
||||
Number of protons in the target nucleus
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
Number of nucleons in the target nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
Metastable state of the target nucleus. A value of zero indicates ground
|
||||
state.
|
||||
atomic_weight_ratio : float
|
||||
Atomic mass ratio of the target nuclide.
|
||||
kTs : Iterable of float
|
||||
|
|
@ -124,22 +63,19 @@ class IncidentNeutron(EqualityMixin):
|
|||
Attributes
|
||||
----------
|
||||
atomic_number : int
|
||||
Number of protons in the nucleus
|
||||
Number of protons in the target nucleus
|
||||
atomic_symbol : str
|
||||
Atomic symbol of the nuclide, e.g., 'Zr'
|
||||
atomic_weight_ratio : float
|
||||
Atomic weight ratio of the target nuclide.
|
||||
energy : dict of numpy.ndarray
|
||||
The energy values (eV) at which reaction cross-sections are tabulated.
|
||||
They keys of the dict are the temperature string ('294K') for each
|
||||
set of energies
|
||||
fission_energy : None or openmc.data.FissionEnergyRelease
|
||||
The energy released by fission, tabulated by component (e.g. prompt
|
||||
neutrons or beta particles) and dependent on incident neutron energy
|
||||
mass_number : int
|
||||
Number of nucleons in the nucleus
|
||||
Number of nucleons in the target nucleus
|
||||
metastable : int
|
||||
Metastable state of the nucleus. A value of zero indicates ground state.
|
||||
Metastable state of the target nucleus. A value of zero indicates ground
|
||||
state.
|
||||
name : str
|
||||
Name of the nuclide using the GND naming convention
|
||||
reactions : collections.OrderedDict
|
||||
|
|
@ -148,6 +84,8 @@ class IncidentNeutron(EqualityMixin):
|
|||
and the values are Reaction objects.
|
||||
resonances : openmc.data.Resonances or None
|
||||
Resonance parameters
|
||||
resonance_covariance : openmc.data.ResonanceCovariance or None
|
||||
Covariance for resonance parameters
|
||||
summed_reactions : collections.OrderedDict
|
||||
Contains summed cross sections, e.g., the total cross section. The keys
|
||||
are the MT values and the values are Reaction objects.
|
||||
|
|
@ -228,6 +166,10 @@ class IncidentNeutron(EqualityMixin):
|
|||
def resonances(self):
|
||||
return self._resonances
|
||||
|
||||
@property
|
||||
def resonance_covariance(self):
|
||||
return self._resonance_covariance
|
||||
|
||||
@property
|
||||
def summed_reactions(self):
|
||||
return self._summed_reactions
|
||||
|
|
@ -289,6 +231,12 @@ class IncidentNeutron(EqualityMixin):
|
|||
cv.check_type('resonances', resonances, res.Resonances)
|
||||
self._resonances = resonances
|
||||
|
||||
@resonance_covariance.setter
|
||||
def resonance_covariance(self, resonance_covariance):
|
||||
cv.check_type('resonance covariance', resonance_covariance,
|
||||
res_cov.ResonanceCovariances)
|
||||
self._resonance_covariance = resonance_covariance
|
||||
|
||||
@summed_reactions.setter
|
||||
def summed_reactions(self, summed_reactions):
|
||||
cv.check_type('summed reactions', summed_reactions, Mapping)
|
||||
|
|
@ -499,6 +447,7 @@ class IncidentNeutron(EqualityMixin):
|
|||
|
||||
# Open file and write version
|
||||
f = h5py.File(path, mode, libver=libver)
|
||||
f.attrs['filetype'] = np.string_('data_neutron')
|
||||
f.attrs['version'] = np.array(HDF5_VERSION)
|
||||
|
||||
# Write basic data
|
||||
|
|
@ -673,7 +622,7 @@ class IncidentNeutron(EqualityMixin):
|
|||
# If mass number hasn't been specified, make an educated guess
|
||||
zaid, xs = ace.name.split('.')
|
||||
name, element, Z, mass_number, metastable = \
|
||||
_get_metadata(int(zaid), metastable_scheme)
|
||||
get_metadata(int(zaid), metastable_scheme)
|
||||
|
||||
# Assign temperature to the running list
|
||||
kTs = [ace.temperature*EV_PER_MEV]
|
||||
|
|
@ -744,7 +693,7 @@ class IncidentNeutron(EqualityMixin):
|
|||
return data
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev_or_filename):
|
||||
def from_endf(cls, ev_or_filename, covariance=False):
|
||||
"""Generate incident neutron continuous-energy data from an ENDF evaluation
|
||||
|
||||
Parameters
|
||||
|
|
@ -753,6 +702,10 @@ class IncidentNeutron(EqualityMixin):
|
|||
ENDF evaluation to read from. If given as a string, it is assumed to
|
||||
be the filename for the ENDF file.
|
||||
|
||||
covariance : bool
|
||||
Flag to indicate whether or not covariance data from File 32 should be
|
||||
retrieved
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.IncidentNeutron
|
||||
|
|
@ -784,6 +737,11 @@ class IncidentNeutron(EqualityMixin):
|
|||
if (2, 151) in ev.section:
|
||||
data.resonances = res.Resonances.from_endf(ev)
|
||||
|
||||
if (32, 151) in ev.section and covariance:
|
||||
data.resonance_covariance = (
|
||||
res_cov.ResonanceCovariances.from_endf(ev, data.resonances)
|
||||
)
|
||||
|
||||
# Read each reaction
|
||||
for mf, mt, nc, mod in ev.reaction_list:
|
||||
if mf == 3:
|
||||
|
|
|
|||
1005
openmc/data/photon.py
Normal file
1005
openmc/data/photon.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1093,7 +1093,7 @@ class Reaction(EqualityMixin):
|
|||
ev : openmc.data.endf.Evaluation
|
||||
ENDF evaluation
|
||||
mt : int
|
||||
The MT value of the reaction to get angular distributions for
|
||||
The MT value of the reaction to get data for
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ except ImportError:
|
|||
_reconstruct = False
|
||||
import openmc.checkvalue as cv
|
||||
|
||||
|
||||
class Resonances(object):
|
||||
"""Resolved and unresolved resonance data
|
||||
|
||||
|
|
@ -90,14 +91,14 @@ class Resonances(object):
|
|||
|
||||
# Determine whether discrete or continuous representation
|
||||
items = get_head_record(file_obj)
|
||||
n_isotope = items[4] # Number of isotopes
|
||||
n_isotope = items[4] # Number of isotopes
|
||||
|
||||
ranges = []
|
||||
for iso in range(n_isotope):
|
||||
items = get_cont_record(file_obj)
|
||||
abundance = items[1]
|
||||
fission_widths = (items[3] == 1) # fission widths are given?
|
||||
n_ranges = items[4] # number of resonance energy ranges
|
||||
fission_widths = (items[3] == 1) # fission widths are given?
|
||||
n_ranges = items[4] # number of resonance energy ranges
|
||||
|
||||
for j in range(n_ranges):
|
||||
items = get_cont_record(file_obj)
|
||||
|
|
@ -112,7 +113,7 @@ class Resonances(object):
|
|||
# unresolved resonance region
|
||||
erange = Unresolved.from_endf(file_obj, items, fission_widths)
|
||||
|
||||
#erange.material = self
|
||||
# erange.material = self
|
||||
ranges.append(erange)
|
||||
|
||||
return cls(ranges)
|
||||
|
|
@ -162,6 +163,13 @@ class ResonanceRange(object):
|
|||
self._prepared = False
|
||||
self._parameter_matrix = {}
|
||||
|
||||
def __copy__(self):
|
||||
cls = type(self)
|
||||
new_copy = cls.__new__(cls)
|
||||
new_copy.__dict__.update(self.__dict__)
|
||||
new_copy._prepared = False
|
||||
return new_copy
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev, file_obj, items):
|
||||
"""Create resonance range from an ENDF evaluation.
|
||||
|
|
@ -437,7 +445,7 @@ class MultiLevelBreitWigner(ResonanceRange):
|
|||
self._l_values = np.array(l_values)
|
||||
self._competitive = np.array(competitive)
|
||||
for l in l_values:
|
||||
self._parameter_matrix[l] = df[df.L == l].as_matrix()
|
||||
self._parameter_matrix[l] = df[df.L == l].values
|
||||
|
||||
self._prepared = True
|
||||
|
||||
|
|
@ -682,7 +690,7 @@ class ReichMoore(ResonanceRange):
|
|||
self._l_values = np.array(l_values)
|
||||
for (l, J) in lj_values:
|
||||
self._parameter_matrix[l, J] = df[(df.L == l) &
|
||||
(abs(df.J) == J)].as_matrix()
|
||||
(abs(df.J) == J)].values
|
||||
|
||||
self._prepared = True
|
||||
|
||||
|
|
|
|||
708
openmc/data/resonance_covariance.py
Normal file
708
openmc/data/resonance_covariance.py
Normal file
|
|
@ -0,0 +1,708 @@
|
|||
from collections import MutableSequence
|
||||
import warnings
|
||||
import io
|
||||
import copy
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from . import endf
|
||||
import openmc.checkvalue as cv
|
||||
from .resonance import Resonances
|
||||
|
||||
|
||||
def _add_file2_contributions(file32params, file2params):
|
||||
"""Function for aiding in adding resonance parameters from File 2 that are
|
||||
not always present in File 32. Uses already imported resonance data.
|
||||
|
||||
Paramaters
|
||||
----------
|
||||
file32params : pandas.Dataframe
|
||||
Incomplete set of resonance parameters contained in File 32.
|
||||
file2params : pandas.Dataframe
|
||||
Resonance parameters from File 2. Ordered by energy.
|
||||
|
||||
Returns
|
||||
-------
|
||||
parameters : pandas.Dataframe
|
||||
Complete set of parameters ordered by L-values and then energy
|
||||
|
||||
"""
|
||||
# Use l-values and competitiveWidth from File 2 data
|
||||
# Re-sort File 2 by energy to match File 32
|
||||
file2params = file2params.sort_values(by=['energy'])
|
||||
file2params.reset_index(drop=True, inplace=True)
|
||||
# Sort File 32 parameters by energy as well (maintaining index)
|
||||
file32params.sort_values(by=['energy'], inplace=True)
|
||||
# Add in values (.values converts to array first to ignore index)
|
||||
file32params['L'] = file2params['L'].values
|
||||
if 'competitiveWidth' in file2params.columns:
|
||||
file32params['competitiveWidth'] = file2params['competitiveWidth'].values
|
||||
# Resort to File 32 order (by L then by E) for use with covariance
|
||||
file32params.sort_index(inplace=True)
|
||||
return file32params
|
||||
|
||||
|
||||
class ResonanceCovariances(Resonances):
|
||||
"""Resolved resonance covariance data
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ranges : list of openmc.data.ResonanceCovarianceRange
|
||||
Distinct energy ranges for resonance data
|
||||
|
||||
Attributes
|
||||
----------
|
||||
ranges : list of openmc.data.ResonanceCovarianceRange
|
||||
Distinct energy ranges for resonance data
|
||||
|
||||
"""
|
||||
|
||||
@property
|
||||
def ranges(self):
|
||||
return self._ranges
|
||||
|
||||
@ranges.setter
|
||||
def ranges(self, ranges):
|
||||
cv.check_type('resonance ranges', ranges, MutableSequence)
|
||||
self._ranges = cv.CheckedList(ResonanceCovarianceRange,
|
||||
'resonance range', ranges)
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev, resonances):
|
||||
"""Generate resonance covariance data from an ENDF evaluation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ev : openmc.data.endf.Evaluation
|
||||
ENDF evaluation
|
||||
resonances : openmc.data.Resonance object
|
||||
openmc.data.Resonanance object generated from the same evaluation
|
||||
used to import values not contained in File 32
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.ResonanceCovariances
|
||||
Resonance covariance data
|
||||
|
||||
"""
|
||||
file_obj = io.StringIO(ev.section[32, 151])
|
||||
|
||||
# Determine whether discrete or continuous representation
|
||||
items = endf.get_head_record(file_obj)
|
||||
n_isotope = items[4] # Number of isotopes
|
||||
|
||||
ranges = []
|
||||
for iso in range(n_isotope):
|
||||
items = endf.get_cont_record(file_obj)
|
||||
abundance = items[1]
|
||||
fission_widths = (items[3] == 1) # Flag for fission widths
|
||||
n_ranges = items[4] # Number of resonance energy ranges
|
||||
|
||||
for j in range(n_ranges):
|
||||
items = endf.get_cont_record(file_obj)
|
||||
# Unresolved flags - 0: only scattering radius given
|
||||
# 1: resolved parameters given
|
||||
# 2: unresolved parameters given
|
||||
unresolved_flag = items[2]
|
||||
formalism = items[3] # resonance formalism
|
||||
|
||||
# Throw error for unsupported formalisms
|
||||
if formalism in [0, 7]:
|
||||
error = 'LRF='+str(formalism)+' covariance not supported '\
|
||||
'for this formalism'
|
||||
raise NotImplementedError(error)
|
||||
|
||||
if unresolved_flag in (0, 1):
|
||||
# Resolved resonance region
|
||||
resonance = resonances.ranges[j]
|
||||
erange = _FORMALISMS[formalism].from_endf(ev, file_obj,
|
||||
items, resonance)
|
||||
ranges.append(erange)
|
||||
|
||||
elif unresolved_flag == 2:
|
||||
warn = 'Unresolved resonance not supported. Covariance '\
|
||||
'values for the unresolved region not imported.'
|
||||
warnings.warn(warn)
|
||||
|
||||
return cls(ranges)
|
||||
|
||||
|
||||
class ResonanceCovarianceRange:
|
||||
"""Resonace covariance range. Base class for different formalisms.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
parameters : pandas.DataFrame
|
||||
Resonance parameters
|
||||
covariance : numpy.array
|
||||
The covariance matrix contained within the ENDF evaluation
|
||||
lcomp : int
|
||||
Flag indicating format of the covariance matrix within the ENDF file
|
||||
file2res : openmc.data.ResonanceRange object
|
||||
Corresponding resonance range with File 2 data.
|
||||
mpar : int
|
||||
Number of parameters in covariance matrix for each individual resonance
|
||||
formalism : str
|
||||
String descriptor of formalism
|
||||
"""
|
||||
def __init__(self, energy_min, energy_max):
|
||||
self.energy_min = energy_min
|
||||
self.energy_max = energy_max
|
||||
|
||||
def subset(self, parameter_str, bounds):
|
||||
"""Produce a subset of resonance parameters and the corresponding
|
||||
covariance matrix to an IncidentNeutron object.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parameter_str : str
|
||||
parameter to be discriminated
|
||||
(i.e. 'energy', 'captureWidth', 'fissionWidthA'...)
|
||||
bounds : np.array
|
||||
[low numerical bound, high numerical bound]
|
||||
|
||||
Returns
|
||||
-------
|
||||
res_cov_range : openmc.data.ResonanceCovarianceRange
|
||||
ResonanceCovarianceRange object that contains a subset of the
|
||||
covariance matrix (upper triangular) as well as a subset parameters
|
||||
within self.file2params
|
||||
|
||||
"""
|
||||
# Copy range and prevent change of original
|
||||
res_cov_range = copy.deepcopy(self)
|
||||
|
||||
parameters = self.file2res.parameters
|
||||
cov = res_cov_range.covariance
|
||||
mpar = res_cov_range.mpar
|
||||
# Create mask
|
||||
mask1 = parameters[parameter_str] >= bounds[0]
|
||||
mask2 = parameters[parameter_str] <= bounds[1]
|
||||
mask = mask1 & mask2
|
||||
res_cov_range.parameters = parameters[mask]
|
||||
indices = res_cov_range.parameters.index.values
|
||||
# Build subset of covariance
|
||||
sub_cov_dim = len(indices)*mpar
|
||||
cov_subset_vals = []
|
||||
for index1 in indices:
|
||||
for i in range(mpar):
|
||||
for index2 in indices:
|
||||
for j in range(mpar):
|
||||
if index2*mpar+j >= index1*mpar+i:
|
||||
cov_subset_vals.append(cov[index1*mpar+i,
|
||||
index2*mpar+j])
|
||||
|
||||
cov_subset = np.zeros([sub_cov_dim, sub_cov_dim])
|
||||
tri_indices = np.triu_indices(sub_cov_dim)
|
||||
cov_subset[tri_indices] = cov_subset_vals
|
||||
|
||||
res_cov_range.file2res.parameters = parameters[mask]
|
||||
res_cov_range.covariance = cov_subset
|
||||
return res_cov_range
|
||||
|
||||
def sample(self, n_samples):
|
||||
"""Sample resonance parameters based on the covariances provided
|
||||
within an ENDF evaluation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n_samples : int
|
||||
The number of samples to produce
|
||||
|
||||
Returns
|
||||
-------
|
||||
samples : list of openmc.data.ResonanceCovarianceRange objects
|
||||
List of samples size `n_samples`
|
||||
|
||||
"""
|
||||
warn_str = 'Sampling routine does not guarantee positive values for '\
|
||||
'parameters. This can lead to undefined behavior in the '\
|
||||
'reconstruction routine.'
|
||||
warnings.warn(warn_str)
|
||||
parameters = self.parameters
|
||||
cov = self.covariance
|
||||
|
||||
# Symmetrizing covariance matrix
|
||||
cov = cov + cov.T - np.diag(cov.diagonal())
|
||||
formalism = self.formalism
|
||||
mpar = self.mpar
|
||||
samples = []
|
||||
|
||||
# Handling MLBW/SLBW sampling
|
||||
if formalism == 'mlbw' or formalism == 'slbw':
|
||||
params = ['energy', 'neutronWidth', 'captureWidth', 'fissionWidth',
|
||||
'competitiveWidth']
|
||||
param_list = params[:mpar]
|
||||
mean_array = parameters[param_list].values
|
||||
mean = mean_array.flatten()
|
||||
par_samples = np.random.multivariate_normal(mean, cov,
|
||||
size=n_samples)
|
||||
spin = parameters['J'].values
|
||||
l_value = parameters['L'].values
|
||||
for sample in par_samples:
|
||||
energy = sample[0::mpar]
|
||||
gn = sample[1::mpar]
|
||||
gg = sample[2::mpar]
|
||||
gf = sample[3::mpar] if mpar > 3 else parameters['fissionWidth'].values
|
||||
gx = sample[4::mpar] if mpar > 4 else parameters['competitiveWidth'].values
|
||||
gt = gn + gg + gf + gx
|
||||
|
||||
records = []
|
||||
for j, E in enumerate(energy):
|
||||
records.append([energy[j], l_value[j], spin[j], gt[j],
|
||||
gn[j], gg[j], gf[j], gx[j]])
|
||||
columns = ['energy', 'L', 'J', 'totalWidth', 'neutronWidth',
|
||||
'captureWidth', 'fissionWidth', 'competitiveWidth']
|
||||
sample_params = pd.DataFrame.from_records(records,
|
||||
columns=columns)
|
||||
# Copy ResonanceRange object
|
||||
res_range = copy.copy(self.file2res)
|
||||
res_range.parameters = sample_params
|
||||
samples.append(res_range)
|
||||
|
||||
# Handling RM sampling
|
||||
elif formalism == 'rm':
|
||||
params = ['energy', 'neutronWidth', 'captureWidth',
|
||||
'fissionWidthA', 'fissionWidthB']
|
||||
param_list = params[:mpar]
|
||||
mean_array = parameters[param_list].values
|
||||
mean = mean_array.flatten()
|
||||
par_samples = np.random.multivariate_normal(mean, cov,
|
||||
size=n_samples)
|
||||
spin = parameters['J'].values
|
||||
l_value = parameters['L'].values
|
||||
for sample in par_samples:
|
||||
energy = sample[0::mpar]
|
||||
gn = sample[1::mpar]
|
||||
gg = sample[2::mpar]
|
||||
gfa = sample[3::mpar] if mpar > 3 else parameters['fissionWidthA'].values
|
||||
gfb = sample[4::mpar] if mpar > 3 else parameters['fissionWidthB'].values
|
||||
|
||||
records = []
|
||||
for j, E in enumerate(energy):
|
||||
records.append([energy[j], l_value[j], spin[j], gn[j],
|
||||
gg[j], gfa[j], gfb[j]])
|
||||
columns = ['energy', 'L', 'J', 'neutronWidth',
|
||||
'captureWidth', 'fissionWidthA', 'fissionWidthB']
|
||||
sample_params = pd.DataFrame.from_records(records,
|
||||
columns=columns)
|
||||
# Copy ResonanceRange object
|
||||
res_range = copy.copy(self.file2res)
|
||||
res_range.parameters = sample_params
|
||||
samples.append(res_range)
|
||||
|
||||
return samples
|
||||
|
||||
|
||||
class MultiLevelBreitWignerCovariance(ResonanceCovarianceRange):
|
||||
"""Multi-level Breit-Wigner resolved resonance formalism covariance data.
|
||||
Parameters
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
parameters : pandas.DataFrame
|
||||
Resonance parameters
|
||||
covariance : numpy.array
|
||||
The covariance matrix contained within the ENDF evaluation
|
||||
mpar : int
|
||||
Number of parameters in covariance matrix for each individual resonance
|
||||
lcomp : int
|
||||
Flag indicating format of the covariance matrix within the ENDF file
|
||||
file2res : openmc.data.ResonanceRange object
|
||||
Corresponding resonance range with File 2 data.
|
||||
formalism : str
|
||||
String descriptor of formalism
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, energy_min, energy_max, parameters, covariance, mpar,
|
||||
lcomp, file2res):
|
||||
super().__init__(energy_min, energy_max)
|
||||
self.parameters = parameters
|
||||
self.covariance = covariance
|
||||
self.mpar = mpar
|
||||
self.lcomp = lcomp
|
||||
self.file2res = copy.copy(file2res)
|
||||
self.formalism = 'mlbw'
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev, file_obj, items, resonance):
|
||||
"""Create MLBW covariance data from an ENDF evaluation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ev : openmc.data.endf.Evaluation
|
||||
ENDF evaluation
|
||||
file_obj : file-like object
|
||||
ENDF file positioned at the second record of a resonance range
|
||||
subsection in MF=32, MT=151
|
||||
items : list
|
||||
Items from the CONT record at the start of the resonance range
|
||||
subsection
|
||||
resonance : openmc.data.ResonanceRange object
|
||||
Corresponding resonance range with File 2 data.
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.MultiLevelBreitWignerCovariance
|
||||
Multi-level Breit-Wigner resonance covariance parameters
|
||||
|
||||
"""
|
||||
|
||||
# Read energy-dependent scattering radius if present
|
||||
energy_min, energy_max = items[0:2]
|
||||
nro, naps = items[4:6]
|
||||
if nro != 0:
|
||||
params, ape = endf.get_tab1_record(file_obj)
|
||||
|
||||
# Other scatter radius parameters
|
||||
items = endf.get_cont_record(file_obj)
|
||||
target_spin = items[0]
|
||||
lcomp = items[3] # Flag for compatibility 0, 1, 2 - 2 is compact form
|
||||
nls = items[4] # number of l-values
|
||||
|
||||
# Build covariance matrix for General Resolved Resonance Formats
|
||||
if lcomp == 1:
|
||||
items = endf.get_cont_record(file_obj)
|
||||
# Number of short range type resonance covariances
|
||||
num_short_range = items[4]
|
||||
# Number of long range type resonance covariances
|
||||
num_long_range = items[5]
|
||||
|
||||
# Read resonance widths, J values, etc
|
||||
records = []
|
||||
for i in range(num_short_range):
|
||||
items, values = endf.get_list_record(file_obj)
|
||||
mpar = items[2]
|
||||
num_res = items[5]
|
||||
num_par_vals = num_res*6
|
||||
res_values = values[:num_par_vals]
|
||||
cov_values = values[num_par_vals:]
|
||||
|
||||
energy = res_values[0::6]
|
||||
spin = res_values[1::6]
|
||||
gt = res_values[2::6]
|
||||
gn = res_values[3::6]
|
||||
gg = res_values[4::6]
|
||||
gf = res_values[5::6]
|
||||
|
||||
for i, E in enumerate(energy):
|
||||
records.append([energy[i], spin[i], gt[i], gn[i],
|
||||
gg[i], gf[i]])
|
||||
|
||||
# Build the upper-triangular covariance matrix
|
||||
cov_dim = mpar*num_res
|
||||
cov = np.zeros([cov_dim, cov_dim])
|
||||
indices = np.triu_indices(cov_dim)
|
||||
cov[indices] = cov_values
|
||||
|
||||
# Compact format - Resonances and individual uncertainties followed by
|
||||
# compact correlations
|
||||
elif lcomp == 2:
|
||||
items, values = endf.get_list_record(file_obj)
|
||||
mean = items
|
||||
num_res = items[5]
|
||||
energy = values[0::12]
|
||||
spin = values[1::12]
|
||||
gt = values[2::12]
|
||||
gn = values[3::12]
|
||||
gg = values[4::12]
|
||||
gf = values[5::12]
|
||||
par_unc = []
|
||||
for i in range(num_res):
|
||||
res_unc = values[i*12+6 : i*12+12]
|
||||
# Delete 0 values (not provided, no fission width)
|
||||
# DAJ/DGT always zero, DGF sometimes nonzero [1, 2, 5]
|
||||
res_unc_nonzero = []
|
||||
for j in range(6):
|
||||
if j in [1, 2, 5] and res_unc[j] != 0.0:
|
||||
res_unc_nonzero.append(res_unc[j])
|
||||
elif j in [0, 3, 4]:
|
||||
res_unc_nonzero.append(res_unc[j])
|
||||
par_unc.extend(res_unc_nonzero)
|
||||
|
||||
records = []
|
||||
for i, E in enumerate(energy):
|
||||
records.append([energy[i], spin[i], gt[i], gn[i],
|
||||
gg[i], gf[i]])
|
||||
|
||||
corr = endf.get_intg_record(file_obj)
|
||||
cov = np.diag(par_unc).dot(corr).dot(np.diag(par_unc))
|
||||
|
||||
# Compatible resolved resonance format
|
||||
elif lcomp == 0:
|
||||
cov = np.zeros([4, 4])
|
||||
records = []
|
||||
cov_index = 0
|
||||
for i in range(nls):
|
||||
items, values = endf.get_list_record(file_obj)
|
||||
num_res = items[5]
|
||||
for j in range(num_res):
|
||||
one_res = values[18*j:18*(j+1)]
|
||||
res_values = one_res[:6]
|
||||
cov_values = one_res[6:]
|
||||
records.append(list(res_values))
|
||||
|
||||
# Populate the coviariance matrix for this resonance
|
||||
# There are no covariances between resonances in lcomp=0
|
||||
cov[cov_index, cov_index] = cov_values[0]
|
||||
cov[cov_index+1, cov_index+1 : cov_index+2] = cov_values[1:2]
|
||||
cov[cov_index+1, cov_index+3] = cov_values[4]
|
||||
cov[cov_index+2, cov_index+2] = cov_values[3]
|
||||
cov[cov_index+2, cov_index+3] = cov_values[5]
|
||||
cov[cov_index+3, cov_index+3] = cov_values[6]
|
||||
|
||||
cov_index += 4
|
||||
if j < num_res-1: # Pad matrix for additional values
|
||||
cov = np.pad(cov, ((0, 4), (0, 4)), 'constant',
|
||||
constant_values=0)
|
||||
|
||||
# Create pandas DataFrame with resonance data, currently
|
||||
# redundant with data.IncidentNeutron.resonance
|
||||
columns = ['energy', 'J', 'totalWidth', 'neutronWidth',
|
||||
'captureWidth', 'fissionWidth']
|
||||
parameters = pd.DataFrame.from_records(records, columns=columns)
|
||||
# Determine mpar (number of parameters for each resonance in
|
||||
# covariance matrix)
|
||||
nparams, params = parameters.shape
|
||||
covsize = cov.shape[0]
|
||||
mpar = int(covsize/nparams)
|
||||
# Add parameters from File 2
|
||||
parameters = _add_file2_contributions(parameters,
|
||||
resonance.parameters)
|
||||
# Create instance of class
|
||||
mlbw = cls(energy_min, energy_max, parameters, cov, mpar, lcomp,
|
||||
resonance)
|
||||
return mlbw
|
||||
|
||||
|
||||
class SingleLevelBreitWignerCovariance(MultiLevelBreitWignerCovariance):
|
||||
"""Single-level Breit-Wigner resolved resonance formalism covariance data.
|
||||
Single-level Breit-Wigner resolved resonance data is is identified by LRF=1
|
||||
in the ENDF-6 format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
parameters : pandas.DataFrame
|
||||
Resonance parameters
|
||||
covariance : numpy.array
|
||||
The covariance matrix contained within the ENDF evaluation
|
||||
mpar : int
|
||||
Number of parameters in covariance matrix for each individual resonance
|
||||
formalism : str
|
||||
String descriptor of formalism
|
||||
lcomp : int
|
||||
Flag indicating format of the covariance matrix within the ENDF file
|
||||
file2res : openmc.data.ResonanceRange object
|
||||
Corresponding resonance range with File 2 data.
|
||||
"""
|
||||
|
||||
def __init__(self, energy_min, energy_max, parameters, covariance, mpar,
|
||||
lcomp, file2res):
|
||||
super().__init__(energy_min, energy_max, parameters, covariance, mpar,
|
||||
lcomp, file2res)
|
||||
self.formalism = 'slbw'
|
||||
|
||||
|
||||
class ReichMooreCovariance(ResonanceCovarianceRange):
|
||||
"""Reich-Moore resolved resonance formalism covariance data.
|
||||
|
||||
Reich-Moore resolved resonance data is identified by LRF=3 in the ENDF-6
|
||||
format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
|
||||
Attributes
|
||||
----------
|
||||
energy_min : float
|
||||
Minimum energy of the resolved resonance range in eV
|
||||
energy_max : float
|
||||
Maximum energy of the resolved resonance range in eV
|
||||
parameters : pandas.DataFrame
|
||||
Resonance parameters
|
||||
covariance : numpy.array
|
||||
The covariance matrix contained within the ENDF evaluation
|
||||
lcomp : int
|
||||
Flag indicating format of the covariance matrix within the ENDF file
|
||||
mpar : int
|
||||
Number of parameters in covariance matrix for each individual resonance
|
||||
file2res : openmc.data.ResonanceRange object
|
||||
Corresponding resonance range with File 2 data.
|
||||
formalism : str
|
||||
String descriptor of formalism
|
||||
"""
|
||||
|
||||
def __init__(self, energy_min, energy_max, parameters, covariance, mpar,
|
||||
lcomp, file2res):
|
||||
super().__init__(energy_min, energy_max)
|
||||
self.parameters = parameters
|
||||
self.covariance = covariance
|
||||
self.mpar = mpar
|
||||
self.lcomp = lcomp
|
||||
self.file2res = copy.copy(file2res)
|
||||
self.formalism = 'rm'
|
||||
|
||||
@classmethod
|
||||
def from_endf(cls, ev, file_obj, items, resonance):
|
||||
"""Create Reich-Moore resonance covariance data from an ENDF
|
||||
evaluation. Includes the resonance parameters contained separately in
|
||||
File 32.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ev : openmc.data.endf.Evaluation
|
||||
ENDF evaluation
|
||||
file_obj : file-like object
|
||||
ENDF file positioned at the second record of a resonance range
|
||||
subsection in MF=2, MT=151
|
||||
items : list
|
||||
Items from the CONT record at the start of the resonance range
|
||||
subsection
|
||||
resonance : openmc.data.Resonance object
|
||||
openmc.data.Resonanance object generated from the same evaluation
|
||||
used to import values not contained in File 32
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.data.ReichMooreCovariance
|
||||
Reich-Moore resonance covariance parameters
|
||||
|
||||
"""
|
||||
# Read energy-dependent scattering radius if present
|
||||
energy_min, energy_max = items[0:2]
|
||||
nro, naps = items[4:6]
|
||||
if nro != 0:
|
||||
params, ape = endf.get_tab1_record(file_obj)
|
||||
|
||||
# Other scatter radius parameters
|
||||
items = endf.get_cont_record(file_obj)
|
||||
target_spin = items[0]
|
||||
lcomp = items[3] # Flag for compatibility 0, 1, 2 - 2 is compact form
|
||||
nls = items[4] # Number of l-values
|
||||
|
||||
# Build covariance matrix for General Resolved Resonance Formats
|
||||
if lcomp == 1:
|
||||
items = endf.get_cont_record(file_obj)
|
||||
# Number of short range type resonance covariances
|
||||
num_short_range = items[4]
|
||||
# Number of long range type resonance covariances
|
||||
num_long_range = items[5]
|
||||
# Read resonance widths, J values, etc
|
||||
channel_radius = {}
|
||||
scattering_radius = {}
|
||||
records = []
|
||||
for i in range(num_short_range):
|
||||
items, values = endf.get_list_record(file_obj)
|
||||
mpar = items[2]
|
||||
num_res = items[5]
|
||||
num_par_vals = num_res*6
|
||||
res_values = values[:num_par_vals]
|
||||
cov_values = values[num_par_vals:]
|
||||
|
||||
energy = res_values[0::6]
|
||||
spin = res_values[1::6]
|
||||
gn = res_values[2::6]
|
||||
gg = res_values[3::6]
|
||||
gfa = res_values[4::6]
|
||||
gfb = res_values[5::6]
|
||||
|
||||
for i, E in enumerate(energy):
|
||||
records.append([energy[i], spin[i], gn[i], gg[i],
|
||||
gfa[i], gfb[i]])
|
||||
|
||||
# Build the upper-triangular covariance matrix
|
||||
cov_dim = mpar*num_res
|
||||
cov = np.zeros([cov_dim, cov_dim])
|
||||
indices = np.triu_indices(cov_dim)
|
||||
cov[indices] = cov_values
|
||||
|
||||
# Compact format - Resonances and individual uncertainties followed by
|
||||
# compact correlations
|
||||
elif lcomp == 2:
|
||||
items, values = endf.get_list_record(file_obj)
|
||||
num_res = items[5]
|
||||
energy = values[0::12]
|
||||
spin = values[1::12]
|
||||
gn = values[2::12]
|
||||
gg = values[3::12]
|
||||
gfa = values[4::12]
|
||||
gfb = values[5::12]
|
||||
par_unc = []
|
||||
for i in range(num_res):
|
||||
res_unc = values[i*12+6 : i*12+12]
|
||||
# Delete 0 values (not provided in evaluation)
|
||||
res_unc = [x for x in res_unc if x != 0.0]
|
||||
par_unc.extend(res_unc)
|
||||
|
||||
records = []
|
||||
for i, E in enumerate(energy):
|
||||
records.append([energy[i], spin[i], gn[i], gg[i],
|
||||
gfa[i], gfb[i]])
|
||||
|
||||
corr = endf.get_intg_record(file_obj)
|
||||
cov = np.diag(par_unc).dot(corr).dot(np.diag(par_unc))
|
||||
|
||||
# Create pandas DataFrame with resonacne data
|
||||
columns = ['energy', 'J', 'neutronWidth', 'captureWidth',
|
||||
'fissionWidthA', 'fissionWidthB']
|
||||
parameters = pd.DataFrame.from_records(records, columns=columns)
|
||||
|
||||
# Determine mpar (number of parameters for each resonance in
|
||||
# covariance matrix)
|
||||
nparams, params = parameters.shape
|
||||
covsize = cov.shape[0]
|
||||
mpar = int(covsize/nparams)
|
||||
|
||||
# Add parameters from File 2
|
||||
parameters = _add_file2_contributions(parameters,
|
||||
resonance.parameters)
|
||||
# Create instance of ReichMooreCovariance
|
||||
rmc = cls(energy_min, energy_max, parameters, cov, mpar, lcomp,
|
||||
resonance)
|
||||
return rmc
|
||||
|
||||
|
||||
_FORMALISMS = {
|
||||
0: ResonanceCovarianceRange,
|
||||
1: SingleLevelBreitWignerCovariance,
|
||||
2: MultiLevelBreitWignerCovariance,
|
||||
3: ReichMooreCovariance
|
||||
# 7: RMatrixLimitedCovariance
|
||||
}
|
||||
BIN
openmc/data/stopping_powers.h5
Normal file
BIN
openmc/data/stopping_powers.h5
Normal file
Binary file not shown.
|
|
@ -279,6 +279,7 @@ class ThermalScattering(EqualityMixin):
|
|||
"""
|
||||
# Open file and write version
|
||||
f = h5py.File(path, mode, libver=libver)
|
||||
f.attrs['filetype'] = np.string_('data_thermal')
|
||||
f.attrs['version'] = np.array(HDF5_VERSION)
|
||||
|
||||
# Write basic data
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
'sphericalharmonics', 'zernike', 'particle'
|
||||
)
|
||||
|
||||
_CURRENT_NAMES = (
|
||||
|
|
@ -31,6 +31,7 @@ _CURRENT_NAMES = (
|
|||
'z-min out', 'z-min in', 'z-max out', 'z-max in'
|
||||
)
|
||||
|
||||
_PARTICLE_IDS = {'neutron': 1, 'photon': 2, 'electron': 3, 'positron': 4}
|
||||
|
||||
class FilterMeta(ABCMeta):
|
||||
def __new__(cls, name, bases, namespace, **kwargs):
|
||||
|
|
@ -540,6 +541,46 @@ class SurfaceFilter(WithIDFilter):
|
|||
expected_type = Surface
|
||||
|
||||
|
||||
class ParticleFilter(Filter):
|
||||
"""Bins tally events based on the Particle type.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
bins : str, int, or iterable of Integral
|
||||
The Particles to tally. Either str with particle type or their
|
||||
ID numbers can be used ('neutron' = 1, 'photon' = 2, 'electron' = 3,
|
||||
'positron' = 4).
|
||||
filter_id : int
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
bins : Iterable of Integral
|
||||
The Particles to tally
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : Integral
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
@property
|
||||
def bins(self):
|
||||
return self._bins
|
||||
|
||||
@bins.setter
|
||||
def bins(self, bins):
|
||||
bins = np.atleast_1d(bins)
|
||||
cv.check_iterable_type('filter bins', bins, (Integral, str))
|
||||
for edge in bins:
|
||||
if isinstance(edge, Integral):
|
||||
cv.check_value('filter bin', edge, _PARTICLE_IDS.values())
|
||||
else:
|
||||
cv.check_value('filter bin', edge, _PARTICLE_IDS.keys())
|
||||
bins = np.atleast_1d([b if isinstance(b, Integral) else _PARTICLE_IDS[b]
|
||||
for b in bins])
|
||||
self._bins = bins
|
||||
|
||||
|
||||
class MeshFilter(Filter):
|
||||
"""Bins tally event locations onto a regular, rectangular mesh.
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ class Particle(object):
|
|||
Type of simulation (criticality or fixed source)
|
||||
id : long
|
||||
Identifier of the particle
|
||||
type : int
|
||||
Particle type (1 = neutron, 2 = photon, 3 = electron, 4 = positron)
|
||||
weight : float
|
||||
Weight of the particle
|
||||
energy : float
|
||||
|
|
@ -65,6 +67,10 @@ class Particle(object):
|
|||
def id(self):
|
||||
return self._f['id'].value
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return self._f['type'].value
|
||||
|
||||
@property
|
||||
def n_particles(self):
|
||||
return self._f['n_particles'].value
|
||||
|
|
|
|||
|
|
@ -30,12 +30,16 @@ class Settings(object):
|
|||
Indicate whether fission neutrons should be created or not.
|
||||
cutoff : dict
|
||||
Dictionary defining weight cutoff and energy cutoff. The dictionary may
|
||||
have three keys, 'weight', 'weight_avg' and 'energy'. Value for 'weight'
|
||||
have six keys, 'weight', 'weight_avg', 'energy_neutron', 'energy_photon',
|
||||
'energy_electron', and 'energy_positron'. Value for 'weight'
|
||||
should be a float indicating weight cutoff below which particle undergo
|
||||
Russian roulette. Value for 'weight_avg' should be a float indicating
|
||||
weight assigned to particles that are not killed after Russian
|
||||
roulette. Value of energy should be a float indicating energy in eV
|
||||
below which particle will be killed.
|
||||
below which particle type will be killed.
|
||||
electron_treatment : {'led', 'ttb'}
|
||||
Whether to deposit all energy from electrons locally ('led') or create
|
||||
secondary bremsstrahlung photons ('ttb').
|
||||
energy_mode : {'continuous-energy', 'multi-group'}
|
||||
Set whether the calculation should be continuous-energy or multi-group.
|
||||
entropy_mesh : openmc.Mesh
|
||||
|
|
@ -68,6 +72,8 @@ class Settings(object):
|
|||
:tallies: Whether the 'tallies.out' file should be written (bool)
|
||||
particles : int
|
||||
Number of particles per generation
|
||||
photon_transport : bool
|
||||
Whether to use photon transport.
|
||||
ptables : bool
|
||||
Determine whether probability tables are used.
|
||||
resonance_scattering : dict
|
||||
|
|
@ -171,7 +177,9 @@ class Settings(object):
|
|||
|
||||
self._confidence_intervals = None
|
||||
self._cross_sections = None
|
||||
self._electron_treatment = None
|
||||
self._multipole_library = None
|
||||
self._photon_transport = None
|
||||
self._ptables = None
|
||||
self._run_cmfd = None
|
||||
self._seed = None
|
||||
|
|
@ -256,10 +264,18 @@ class Settings(object):
|
|||
def confidence_intervals(self):
|
||||
return self._confidence_intervals
|
||||
|
||||
@property
|
||||
def electron_treatment(self):
|
||||
return self._electron_treatment
|
||||
|
||||
@property
|
||||
def ptables(self):
|
||||
return self._ptables
|
||||
|
||||
@property
|
||||
def photon_transport(self):
|
||||
return self._photon_transport
|
||||
|
||||
@property
|
||||
def run_cmfd(self):
|
||||
return self._run_cmfd
|
||||
|
|
@ -485,6 +501,16 @@ class Settings(object):
|
|||
cv.check_type('confidence interval', confidence_intervals, bool)
|
||||
self._confidence_intervals = confidence_intervals
|
||||
|
||||
@electron_treatment.setter
|
||||
def electron_treatment(self, electron_treatment):
|
||||
cv.check_value('electron treatment', electron_treatment, ['led', 'ttb'])
|
||||
self._electron_treatment = electron_treatment
|
||||
|
||||
@photon_transport.setter
|
||||
def photon_transport(self, photon_transport):
|
||||
cv.check_type('photon transport', photon_transport, bool)
|
||||
self._photon_transport = photon_transport
|
||||
|
||||
@ptables.setter
|
||||
def ptables(self, ptables):
|
||||
cv.check_type('probability tables', ptables, bool)
|
||||
|
|
@ -514,16 +540,16 @@ class Settings(object):
|
|||
raise ValueError(msg)
|
||||
for key in cutoff:
|
||||
if key == 'weight':
|
||||
cv.check_type('weight cutoff', cutoff['weight'], Real)
|
||||
cv.check_greater_than('weight cutoff', cutoff['weight'], 0.0)
|
||||
cv.check_type('weight cutoff', cutoff[key], Real)
|
||||
cv.check_greater_than('weight cutoff', cutoff[key], 0.0)
|
||||
elif key == 'weight_avg':
|
||||
cv.check_type('average survival weight', cutoff['weight_avg'],
|
||||
Real)
|
||||
cv.check_type('average survival weight', cutoff[key], Real)
|
||||
cv.check_greater_than('average survival weight',
|
||||
cutoff['weight_avg'], 0.0)
|
||||
elif key == 'energy':
|
||||
cv.check_type('energy cutoff', cutoff['energy'], Real)
|
||||
cv.check_greater_than('energy cutoff', cutoff['energy'], 0.0)
|
||||
cutoff[key], 0.0)
|
||||
elif key in ['energy_neutron', 'energy_photon', 'energy_electron',
|
||||
'energy_positron']:
|
||||
cv.check_type('energy cutoff', cutoff[key], Real)
|
||||
cv.check_greater_than('energy cutoff', cutoff[key], 0.0)
|
||||
else:
|
||||
msg = 'Unable to set cutoff to "{0}" which is unsupported by '\
|
||||
'OpenMC'.format(key)
|
||||
|
|
@ -777,6 +803,16 @@ class Settings(object):
|
|||
element = ET.SubElement(root, "confidence_intervals")
|
||||
element.text = str(self._confidence_intervals).lower()
|
||||
|
||||
def _create_electron_treatment_subelement(self, root):
|
||||
if self._electron_treatment is not None:
|
||||
element = ET.SubElement(root, "electron_treatment")
|
||||
element.text = str(self._electron_treatment)
|
||||
|
||||
def _create_photon_transport_subelement(self, root):
|
||||
if self._photon_transport is not None:
|
||||
element = ET.SubElement(root, "photon_transport")
|
||||
element.text = str(self._photon_transport).lower()
|
||||
|
||||
def _create_ptables_subelement(self, root):
|
||||
if self._ptables is not None:
|
||||
element = ET.SubElement(root, "ptables")
|
||||
|
|
@ -800,17 +836,9 @@ class Settings(object):
|
|||
def _create_cutoff_subelement(self, root):
|
||||
if self._cutoff is not None:
|
||||
element = ET.SubElement(root, "cutoff")
|
||||
if 'weight' in self._cutoff:
|
||||
subelement = ET.SubElement(element, "weight")
|
||||
subelement.text = str(self._cutoff['weight'])
|
||||
|
||||
if 'weight_avg' in self._cutoff:
|
||||
subelement = ET.SubElement(element, "weight_avg")
|
||||
subelement.text = str(self._cutoff['weight_avg'])
|
||||
|
||||
if 'energy' in self._cutoff:
|
||||
subelement = ET.SubElement(element, "energy")
|
||||
subelement.text = str(self._cutoff['energy'])
|
||||
for key, value in self._cutoff.items():
|
||||
subelement = ET.SubElement(element, key)
|
||||
subelement.text = str(value)
|
||||
|
||||
def _create_entropy_mesh_subelement(self, root):
|
||||
if self.entropy_mesh is not None:
|
||||
|
|
@ -941,8 +969,10 @@ class Settings(object):
|
|||
self._create_statepoint_subelement(root_element)
|
||||
self._create_sourcepoint_subelement(root_element)
|
||||
self._create_confidence_intervals(root_element)
|
||||
self._create_electron_treatment_subelement(root_element)
|
||||
self._create_energy_mode_subelement(root_element)
|
||||
self._create_max_order_subelement(root_element)
|
||||
self._create_photon_transport_subelement(root_element)
|
||||
self._create_ptables_subelement(root_element)
|
||||
self._create_run_cmfd_subelement(root_element)
|
||||
self._create_seed_subelement(root_element)
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ class Source(object):
|
|||
Source file from which sites should be sampled
|
||||
strength : Real
|
||||
Strength of the source
|
||||
particle : {'neutron', 'photon'}
|
||||
Source particle type
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -35,10 +37,13 @@ class Source(object):
|
|||
Source file from which sites should be sampled
|
||||
strength : Real
|
||||
Strength of the source
|
||||
particle : {'neutron', 'photon'}
|
||||
Source particle type
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, space=None, angle=None, energy=None, filename=None, strength=1.0):
|
||||
def __init__(self, space=None, angle=None, energy=None, filename=None,
|
||||
strength=1.0, particle='neutron'):
|
||||
self._space = None
|
||||
self._angle = None
|
||||
self._energy = None
|
||||
|
|
@ -53,6 +58,7 @@ class Source(object):
|
|||
if filename is not None:
|
||||
self.file = filename
|
||||
self.strength = strength
|
||||
self.particle = particle
|
||||
|
||||
@property
|
||||
def file(self):
|
||||
|
|
@ -74,6 +80,10 @@ class Source(object):
|
|||
def strength(self):
|
||||
return self._strength
|
||||
|
||||
@property
|
||||
def particle(self):
|
||||
return self._particle
|
||||
|
||||
@file.setter
|
||||
def file(self, filename):
|
||||
cv.check_type('source file', filename, str)
|
||||
|
|
@ -100,6 +110,11 @@ class Source(object):
|
|||
cv.check_greater_than('source strength', strength, 0.0, True)
|
||||
self._strength = strength
|
||||
|
||||
@particle.setter
|
||||
def particle(self, particle):
|
||||
cv.check_value('source particle', particle, ['neutron', 'photon'])
|
||||
self._particle = particle
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the source
|
||||
|
||||
|
|
@ -111,6 +126,8 @@ class Source(object):
|
|||
"""
|
||||
element = ET.Element("source")
|
||||
element.set("strength", str(self.strength))
|
||||
if self.particle != 'neutron':
|
||||
element.set("particle", self.particle)
|
||||
if self.file is not None:
|
||||
element.set("file", self.file)
|
||||
if self.space is not None:
|
||||
|
|
|
|||
|
|
@ -83,6 +83,8 @@ class StatePoint(object):
|
|||
Number of tally realizations
|
||||
path : str
|
||||
Working directory for simulation
|
||||
photon_transport : bool
|
||||
Indicate whether photon transport is active
|
||||
run_mode : str
|
||||
Simulation run mode, e.g. 'eigenvalue'
|
||||
runtime : dict
|
||||
|
|
@ -322,6 +324,10 @@ class StatePoint(object):
|
|||
def path(self):
|
||||
return self._f.attrs['path'].decode()
|
||||
|
||||
@property
|
||||
def photon_transport(self):
|
||||
return self._f.attrs['photon_transport'] > 0
|
||||
|
||||
@property
|
||||
def run_mode(self):
|
||||
return self._f['run_mode'].value.decode()
|
||||
|
|
|
|||
|
|
@ -25,13 +25,6 @@ follows the NNDC data convention (1000*Z + A + 300 + 100*m), or the MCNP data
|
|||
convention (essentially the same as NNDC, except that the first metastable state
|
||||
of Am242 is 95242 and the ground state is 95642).
|
||||
|
||||
The optional --fission_energy_release argument will accept an HDF5 file
|
||||
containing a library of fission energy release (ENDF MF=1 MT=458) data. A
|
||||
library built from ENDF/B-VII.1 data is released with OpenMC and can be found at
|
||||
openmc/data/fission_Q_data_endb71.h5. This data is necessary for
|
||||
'fission-q-prompt' and 'fission-q-recoverable' tallies, but is not needed
|
||||
otherwise.
|
||||
|
||||
"""
|
||||
|
||||
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
||||
|
|
@ -55,8 +48,6 @@ parser.add_argument('--xsdir', help='MCNP xsdir file that lists '
|
|||
'ACE libraries')
|
||||
parser.add_argument('--xsdata', help='Serpent xsdata file that lists '
|
||||
'ACE libraries')
|
||||
parser.add_argument('--fission_energy_release', help='HDF5 file containing '
|
||||
'fission energy release data')
|
||||
parser.add_argument('--libver', choices=['earliest', 'latest'],
|
||||
default='earliest', help="Output HDF5 versioning. Use "
|
||||
"'earliest' for backwards compatibility or 'latest' for "
|
||||
|
|
@ -142,13 +133,6 @@ for filename in ace_libraries:
|
|||
print('Failed to convert {}: {}'.format(table.name, e))
|
||||
continue
|
||||
|
||||
# Fission energy release data, if available
|
||||
if args.fission_energy_release is not None:
|
||||
fer = openmc.data.FissionEnergyRelease.from_compact_hdf5(
|
||||
args.fission_energy_release, neutron)
|
||||
if fer is not None:
|
||||
neutron.fission_energy = fer
|
||||
|
||||
print('Converting {} (ACE) to {} (HDF5)'.format(table.name,
|
||||
neutron.name))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Download ENDF/B-VII.1 incident neutron ACE data and incident photon ENDF data
|
||||
from NNDC and convert it to an HDF5 library for use with OpenMC. This data is
|
||||
used for OpenMC's regression test suite.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
|
@ -13,31 +19,25 @@ from urllib.request import urlopen
|
|||
import openmc.data
|
||||
|
||||
|
||||
description = """
|
||||
Download ENDF/B-VII.1 ACE data from NNDC and convert it to an HDF5 library for
|
||||
use with OpenMC. This data is used for OpenMC's regression test suite.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
||||
argparse.RawDescriptionHelpFormatter):
|
||||
pass
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=description,
|
||||
description=__doc__,
|
||||
formatter_class=CustomFormatter
|
||||
)
|
||||
parser.add_argument('-b', '--batch', action='store_true',
|
||||
help='supresses standard in')
|
||||
parser.add_argument('-n', '--neutron-only', action='store_true',
|
||||
help='Whether to exclude photon interaction/atomic data')
|
||||
parser.add_argument('--libver', choices=['earliest', 'latest'],
|
||||
default='earliest', help="Output HDF5 versioning. Use "
|
||||
"'earliest' for backwards compatibility or 'latest' for "
|
||||
"performance")
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
baseUrl = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/'
|
||||
base_url = 'http://www.nndc.bnl.gov/endf/b7.1/aceFiles/'
|
||||
files = ['ENDF-B-VII.1-neutron-293.6K.tar.gz',
|
||||
'ENDF-B-VII.1-tsl.tar.gz']
|
||||
checksums = ['9729a17eb62b75f285d8a7628ace1449',
|
||||
|
|
@ -47,32 +47,29 @@ block_size = 16384
|
|||
# ==============================================================================
|
||||
# DOWNLOAD FILES FROM NNDC SITE
|
||||
|
||||
filesComplete = []
|
||||
files_complete = []
|
||||
for f in files:
|
||||
# Establish connection to URL
|
||||
url = baseUrl + f
|
||||
url = base_url + f
|
||||
req = urlopen(url)
|
||||
|
||||
# Get file size from header
|
||||
if sys.version_info[0] < 3:
|
||||
file_size = int(req.info().getheaders('Content-Length')[0])
|
||||
else:
|
||||
file_size = req.length
|
||||
file_size = req.length
|
||||
downloaded = 0
|
||||
|
||||
# Check if file already downloaded
|
||||
if os.path.exists(f):
|
||||
if os.path.getsize(f) == file_size:
|
||||
print('Skipping ' + f)
|
||||
filesComplete.append(f)
|
||||
files_complete.append(f)
|
||||
continue
|
||||
else:
|
||||
overwrite = input('Overwrite {0}? ([y]/n) '.format(f))
|
||||
overwrite = input('Overwrite {}? ([y]/n) '.format(f))
|
||||
if overwrite.lower().startswith('n'):
|
||||
continue
|
||||
|
||||
# Copy file to disk
|
||||
print('Downloading {0}... '.format(f), end='')
|
||||
print('Downloading {}... '.format(f), end='')
|
||||
with open(f, 'wb') as fh:
|
||||
while True:
|
||||
chunk = req.read(block_size)
|
||||
|
|
@ -83,7 +80,7 @@ for f in files:
|
|||
downloaded, downloaded * 100. / file_size)
|
||||
print(status + chr(8)*len(status), end='')
|
||||
print('')
|
||||
filesComplete.append(f)
|
||||
files_complete.append(f)
|
||||
|
||||
# ==============================================================================
|
||||
# VERIFY MD5 CHECKSUMS
|
||||
|
|
@ -101,18 +98,18 @@ for f, checksum in zip(files, checksums):
|
|||
# EXTRACT FILES FROM TGZ
|
||||
|
||||
for f in files:
|
||||
if f not in filesComplete:
|
||||
if f not in files_complete:
|
||||
continue
|
||||
|
||||
# Extract files
|
||||
suffix = f[f.rindex('-') + 1:].rstrip('.tar.gz')
|
||||
with tarfile.open(f, 'r') as tgz:
|
||||
print('Extracting {0}...'.format(f))
|
||||
print('Extracting {}...'.format(f))
|
||||
tgz.extractall(path='nndc/' + suffix)
|
||||
|
||||
# Move ACE files down one level
|
||||
for filename in glob.glob('nndc/293.6K/ENDF-B-VII.1-neutron-293.6K/*'):
|
||||
shutil.move(filename, 'nndc/293.6K/')
|
||||
shutil.move(filename, 'nndc/293.6K/' + os.path.basename(filename))
|
||||
|
||||
# ==============================================================================
|
||||
# FIX ZAID ASSIGNMENTS FOR VARIOUS S(A,B) TABLES
|
||||
|
|
@ -142,7 +139,7 @@ else:
|
|||
if not response or response.lower().startswith('y'):
|
||||
for f in files:
|
||||
if os.path.exists(f):
|
||||
print('Removing {0}...'.format(f))
|
||||
print('Removing {}...'.format(f))
|
||||
os.remove(f)
|
||||
|
||||
# ==============================================================================
|
||||
|
|
@ -162,3 +159,10 @@ subprocess.call([ace2hdf5,
|
|||
'-d', 'nndc_hdf5',
|
||||
'--fission_energy_release', fer_file,
|
||||
'--libver', args.libver] + ace_files)
|
||||
|
||||
# Generate photo interaction library files
|
||||
if not args.neutron_only:
|
||||
pwd = os.path.dirname(os.path.realpath(__file__))
|
||||
photo_endf = os.path.join(pwd, 'openmc-get-photon-data')
|
||||
subprocess.call([photo_endf, '-c', 'cross_sections.xml'],
|
||||
cwd='nndc_hdf5')
|
||||
|
|
|
|||
106
scripts/openmc-get-photon-data
Executable file
106
scripts/openmc-get-photon-data
Executable file
|
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Download ENDF/B-VII.1 ENDF data from NNDC for photo-atomic and atomic
|
||||
relaxation data and convert it to an HDF5 library for use with OpenMC.
|
||||
This data is used for photon transport in OpenMC.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import zipfile
|
||||
import argparse
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
|
||||
import openmc.data
|
||||
|
||||
|
||||
class CustomFormatter(argparse.ArgumentDefaultsHelpFormatter,
|
||||
argparse.RawDescriptionHelpFormatter):
|
||||
pass
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description=__doc__,
|
||||
formatter_class=CustomFormatter
|
||||
)
|
||||
parser.add_argument('-c', '--cross-sections',
|
||||
help='cross_sections.xml file to append libraries to')
|
||||
args = parser.parse_args()
|
||||
|
||||
base_url = 'http://www.nndc.bnl.gov/endf/b7.1/zips/'
|
||||
files = ['ENDF-B-VII.1-photoat.zip', 'ENDF-B-VII.1-atomic_relax.zip']
|
||||
block_size = 16384
|
||||
|
||||
# ==============================================================================
|
||||
# DOWNLOAD FILES FROM NNDC SITE
|
||||
|
||||
if not os.path.exists('photon_hdf5'):
|
||||
os.mkdir('photon_hdf5')
|
||||
|
||||
for f in files:
|
||||
# Establish connection to URL
|
||||
url = base_url + f
|
||||
req = urlopen(url)
|
||||
|
||||
# Get file size from header
|
||||
file_size = req.length
|
||||
downloaded = 0
|
||||
|
||||
# Check if file already downloaded
|
||||
if os.path.exists(f):
|
||||
if os.path.getsize(f) == file_size:
|
||||
print('Skipping ' + f)
|
||||
continue
|
||||
else:
|
||||
overwrite = input('Overwrite {}? ([y]/n) '.format(f))
|
||||
if overwrite.lower().startswith('n'):
|
||||
continue
|
||||
|
||||
# Copy file to disk
|
||||
print('Downloading {}... '.format(f), end='')
|
||||
with open(f, 'wb') as fh:
|
||||
while True:
|
||||
chunk = req.read(block_size)
|
||||
if not chunk: break
|
||||
fh.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
status = '{0:10} [{1:3.2f}%]'.format(
|
||||
downloaded, downloaded * 100. / file_size)
|
||||
print(status + chr(8)*len(status), end='')
|
||||
print('')
|
||||
|
||||
# ==============================================================================
|
||||
# EXTRACT FILES
|
||||
|
||||
for f in files:
|
||||
print('Extracting {0}...'.format(f))
|
||||
zipfile.ZipFile(f).extractall()
|
||||
|
||||
# ==============================================================================
|
||||
# GENERATE HDF5 DATA LIBRARY
|
||||
|
||||
# If previous cross_sections.xml was specified, load it in
|
||||
if args.cross_sections is not None:
|
||||
lib_path = args.cross_sections
|
||||
library = openmc.data.DataLibrary.from_xml(lib_path)
|
||||
else:
|
||||
lib_path = os.path.join('photon_hdf5', 'cross_sections.xml')
|
||||
library = openmc.data.DataLibrary()
|
||||
|
||||
for z in range(1, 101):
|
||||
element = openmc.data.ATOMIC_SYMBOL[z]
|
||||
print('Generating HDF5 file for Z={} ({})...'.format(z, element))
|
||||
|
||||
# Generate instance of IncidentPhoton
|
||||
photo_file = os.path.join('photoat', 'photoat-{:03}_{}_000.endf'.format(z, element))
|
||||
atom_file = os.path.join('atomic_relax', 'atom-{:03}_{}_000.endf'.format(z, element))
|
||||
f = openmc.data.IncidentPhoton.from_endf(photo_file, atom_file)
|
||||
|
||||
# Write HDF5 file and register it
|
||||
hdf5_file = os.path.join('photon_hdf5', element + '.h5')
|
||||
f.export_to_hdf5(hdf5_file, 'w')
|
||||
library.register_file(hdf5_file)
|
||||
|
||||
library.export_to_xml(lib_path)
|
||||
99
scripts/openmc-make-compton
Executable file
99
scripts/openmc-make-compton
Executable file
|
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tarfile
|
||||
from urllib.request import urlopen
|
||||
|
||||
import numpy as np
|
||||
import h5py
|
||||
|
||||
|
||||
base_url = 'http://geant4.cern.ch/support/source/'
|
||||
filename = 'G4EMLOW.6.48.tar.gz'
|
||||
block_size = 16384
|
||||
|
||||
# ==============================================================================
|
||||
# DOWNLOAD FILES FROM GEANT4 SITE
|
||||
|
||||
# Establish connection to URL
|
||||
req = urlopen(base_url + filename)
|
||||
|
||||
# Get file size from header
|
||||
file_size = req.length
|
||||
downloaded = 0
|
||||
|
||||
# Check if file already downloaded
|
||||
download = True
|
||||
if os.path.exists(filename):
|
||||
if os.path.getsize(filename) == file_size:
|
||||
print('Already downloaded ' + filename)
|
||||
download = False
|
||||
else:
|
||||
overwrite = input('Overwrite {}? ([y]/n) '.format(filename))
|
||||
if overwrite.lower().startswith('n'):
|
||||
download = False
|
||||
|
||||
if download:
|
||||
# Copy file to disk
|
||||
print('Downloading {}... '.format(filename), end='')
|
||||
with open(filename, 'wb') as fh:
|
||||
while True:
|
||||
chunk = req.read(block_size)
|
||||
if not chunk: break
|
||||
fh.write(chunk)
|
||||
downloaded += len(chunk)
|
||||
status = '{0:10} [{1:3.2f}%]'.format(
|
||||
downloaded, downloaded * 100. / file_size)
|
||||
print(status + chr(8)*len(status), end='')
|
||||
print('')
|
||||
|
||||
# ==============================================================================
|
||||
# EXTRACT FILES FROM TGZ
|
||||
|
||||
if not os.path.isdir('G4EMLOW6.48'):
|
||||
with tarfile.open(filename, 'r') as tgz:
|
||||
print('Extracting {0}...'.format(filename))
|
||||
tgz.extractall()
|
||||
|
||||
# ==============================================================================
|
||||
# GENERATE COMPTON PROFILE HDF5 FILE
|
||||
|
||||
print('Generating compton_profiles.h5...')
|
||||
|
||||
shell_file = os.path.join('G4EMLOW6.48', 'doppler', 'shell-doppler.dat')
|
||||
|
||||
with open(shell_file, 'r') as shell:
|
||||
with h5py.File('compton_profiles.h5', 'w') as f:
|
||||
# Read/write electron momentum values
|
||||
pz = np.loadtxt(os.path.join('G4EMLOW6.48', 'doppler', 'p-biggs.dat'))
|
||||
f.create_dataset('pz', data=pz)
|
||||
|
||||
for Z in range(1, 101):
|
||||
# Create group for this element
|
||||
group = f.create_group('{:03}'.format(Z))
|
||||
|
||||
# Read data into one long array
|
||||
path = os.path.join('G4EMLOW6.48', 'doppler', 'profile-{}.dat'.format(Z))
|
||||
J = np.fromstring(open(path, 'r').read(), sep=' ')
|
||||
|
||||
# Determine number of electron shells and reshape
|
||||
n_shells = J.size // 31
|
||||
J.shape = (n_shells, 31)
|
||||
|
||||
# Write Compton profile for this Z
|
||||
group.create_dataset('J', data=J)
|
||||
|
||||
# Determine binding energies and number of electrons for each shell
|
||||
num_electrons = []
|
||||
binding_energy = []
|
||||
while True:
|
||||
words = shell.readline().split()
|
||||
if words[0] == '-1':
|
||||
break
|
||||
num_electrons.append(float(words[0]))
|
||||
binding_energy.append(float(words[1]))
|
||||
|
||||
# Write binding energies and number of electrons
|
||||
group.create_dataset('num_electrons', data=num_electrons)
|
||||
group.create_dataset('binding_energy', data=binding_energy)
|
||||
50
scripts/openmc-make-stopping-powers
Executable file
50
scripts/openmc-make-stopping-powers
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from urllib.parse import urlencode
|
||||
from urllib.request import urlopen
|
||||
from lxml import html
|
||||
|
||||
import numpy as np
|
||||
import h5py
|
||||
from openmc.data import ATOMIC_SYMBOL
|
||||
|
||||
|
||||
base_url = 'https://physics.nist.gov/cgi-bin/Star/e_table-t.pl'
|
||||
energies = np.logspace(-3, 3, 200)
|
||||
data = {'matno': '', 'Energies': '\n'.join(str(x) for x in energies)}
|
||||
columns = {1: 's_collision', 2: 's_radiative'}
|
||||
|
||||
# ==============================================================================
|
||||
# SCRAPE DATA FROM ESTAR SITE AND GENERATE STOPPING POWER HDF5 FILE
|
||||
|
||||
print('Generating stopping_powers.h5...')
|
||||
|
||||
with h5py.File('stopping_powers.h5', 'w') as f:
|
||||
|
||||
# Write energies
|
||||
f.create_dataset('energy', data=energies)
|
||||
|
||||
for Z in range(1, 99):
|
||||
print('Processing {} data...'.format(ATOMIC_SYMBOL[Z]))
|
||||
|
||||
# Update form-encoded data to send in POST request for this element
|
||||
data['matno'] = '{:03}'.format(Z)
|
||||
payload = urlencode(data).encode("utf-8")
|
||||
|
||||
# Retrieve data from ESTAR site
|
||||
r = urlopen(url=base_url, data=payload).read()
|
||||
|
||||
# Remove text and reformat data
|
||||
r = html.fromstring(r).xpath('//pre//text()')
|
||||
values = np.fromstring(' '.join(r[12:-5]), sep=' ').reshape((-1, 5)).T
|
||||
|
||||
# Create group for this element
|
||||
group = f.create_group('{:03}'.format(Z))
|
||||
|
||||
# Write the mean excitation energy
|
||||
attributes = np.fromstring(r[3], sep=' ')
|
||||
group.attrs['I'] = attributes[2]
|
||||
|
||||
# Write collision and radiative stopping powers
|
||||
for i in columns:
|
||||
group.create_dataset(columns[i], data=values[i])
|
||||
|
|
@ -248,8 +248,7 @@ def update_materials(root):
|
|||
# If a nuclide name is in the ZAID notation (e.g., a number),
|
||||
# convert it to the proper nuclide name.
|
||||
if nucname.strip().isnumeric():
|
||||
nucname = \
|
||||
openmc.data.neutron._get_metadata(int(nucname))[0]
|
||||
nucname = openmc.data.ace.get_metadata(int(nucname))[0]
|
||||
nucname = nucname.replace('Nat', '0')
|
||||
if nucname.endswith('m'):
|
||||
nucname = nucname[:-1] + '_m1'
|
||||
|
|
|
|||
4
setup.py
4
setup.py
|
|
@ -32,14 +32,14 @@ kwargs = {
|
|||
# Data files and librarries
|
||||
'package_data': {
|
||||
'openmc.capi': ['libopenmc.{}'.format(suffix)],
|
||||
'openmc.data': ['mass.mas12', 'fission_Q_data_endfb71.h5']
|
||||
'openmc.data': ['mass.mas12', '*.h5']
|
||||
},
|
||||
|
||||
# Metadata
|
||||
'author': 'The OpenMC Development Team',
|
||||
'author_email': 'openmc-dev@googlegroups.com',
|
||||
'description': 'OpenMC',
|
||||
'url': 'https://github.com/mit-crpg/openmc',
|
||||
'url': 'https://github.com/openmc-dev/openmc',
|
||||
'classifiers': [
|
||||
'Development Status :: 4 - Beta',
|
||||
'Intended Audience :: Developers',
|
||||
|
|
|
|||
14
src/api.F90
14
src/api.F90
|
|
@ -15,7 +15,7 @@ module openmc_api
|
|||
use message_passing
|
||||
use nuclide_header
|
||||
use initialize, only: openmc_init_f
|
||||
use particle_header, only: Particle
|
||||
use particle_header
|
||||
use plot, only: openmc_plot_geometry
|
||||
use random_lcg, only: openmc_get_seed, openmc_set_seed
|
||||
use settings
|
||||
|
|
@ -120,9 +120,10 @@ contains
|
|||
check_overlaps = .false.
|
||||
confidence_intervals = .false.
|
||||
create_fission_neutrons = .true.
|
||||
energy_cutoff = ZERO
|
||||
energy_max_neutron = INFINITY
|
||||
energy_min_neutron = ZERO
|
||||
electron_treatment = ELECTRON_LED
|
||||
energy_cutoff(:) = [ZERO, 1000.0_8, ZERO, ZERO]
|
||||
energy_max(:) = [INFINITY, INFINITY]
|
||||
energy_min(:) = [ZERO, ZERO]
|
||||
entropy_on = .false.
|
||||
gen_per_batch = 1
|
||||
index_entropy_mesh = -1
|
||||
|
|
@ -139,6 +140,7 @@ contains
|
|||
output_summary = .true.
|
||||
output_tallies = .true.
|
||||
particle_restart_run = .false.
|
||||
photon_transport = .false.
|
||||
pred_batches = .false.
|
||||
reduce_tallies = .true.
|
||||
res_scat_on = .false.
|
||||
|
|
@ -196,7 +198,7 @@ contains
|
|||
logical :: found
|
||||
type(Particle) :: p
|
||||
|
||||
call p % initialize()
|
||||
call particle_initialize(p)
|
||||
p % coord(1) % xyz(:) = xyz
|
||||
p % coord(1) % uvw(:) = [ZERO, ZERO, ONE]
|
||||
call find_cell(p, found)
|
||||
|
|
@ -310,6 +312,7 @@ contains
|
|||
subroutine free_memory()
|
||||
|
||||
use cmfd_header
|
||||
use photon_header
|
||||
use plot_header
|
||||
use sab_header
|
||||
use settings
|
||||
|
|
@ -326,6 +329,7 @@ contains
|
|||
call free_memory_volume()
|
||||
call free_memory_simulation()
|
||||
call free_memory_nuclide()
|
||||
call free_memory_photon()
|
||||
call free_memory_settings()
|
||||
call free_memory_sab()
|
||||
call free_memory_source()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ module bank_header
|
|||
real(C_DOUBLE) :: uvw(3) ! diretional cosines
|
||||
real(C_DOUBLE) :: E ! energy / energy group if in MG mode.
|
||||
integer(C_INT) :: delayed_group ! delayed group
|
||||
integer(C_INT) :: particle ! particle type (neutron, photon, etc.)
|
||||
end type Bank
|
||||
|
||||
! Source and fission bank
|
||||
|
|
|
|||
|
|
@ -329,8 +329,7 @@ Cell::to_hdf5(hid_t cell_group) const
|
|||
}
|
||||
|
||||
//TODO: Fix the off-by-one indexing.
|
||||
write_int(cell_group, 0, nullptr, "universe",
|
||||
&global_universes[universe-1]->id, false);
|
||||
write_dataset(cell_group, "universe", global_universes[universe-1]->id);
|
||||
|
||||
// Write the region specification.
|
||||
if (!region.empty()) {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ contains
|
|||
end if
|
||||
else
|
||||
if(.not.allocated(cmfd % egrid)) allocate(cmfd % egrid(2))
|
||||
cmfd % egrid = [ ZERO, energy_max_neutron ]
|
||||
cmfd % egrid = [ ZERO, energy_max(NEUTRON) ]
|
||||
cmfd % indices(4) = 1 ! one energy group
|
||||
end if
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ module constants
|
|||
MASS_NEUTRON = 1.00866491588_8, & ! mass of a neutron in amu
|
||||
MASS_NEUTRON_EV = 939.5654133e6_8, & ! mass of a neutron in eV/c^2
|
||||
MASS_PROTON = 1.007276466879_8, & ! mass of a proton in amu
|
||||
MASS_ELECTRON_EV = 0.5109989461e6_8, & ! electron mass energy equivalent in eV/c^2
|
||||
FINE_STRUCTURE = 137.035999139_8, & ! inverse fine structure constant
|
||||
PLANCK_C = 1.2398419739062977e4_8,& ! Planck's constant times c in eV-Angstroms
|
||||
AMU = 1.660539040e-27_8, & ! 1 amu in kg
|
||||
C_LIGHT = 2.99792458e8_8, & ! speed of light in m/s
|
||||
N_AVOGADRO = 0.6022140857_8, & ! Avogadro's number in 10^24/mol
|
||||
|
|
@ -91,6 +94,14 @@ module constants
|
|||
FOUR = 4.0_8
|
||||
complex(8), parameter :: ONEI = (ZERO, ONE)
|
||||
|
||||
! Electron subshell labels
|
||||
character(3), parameter :: SUBSHELLS(39) = [ &
|
||||
'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 ']
|
||||
|
||||
! ============================================================================
|
||||
! GEOMETRY-RELATED CONSTANTS
|
||||
|
||||
|
|
@ -172,7 +183,8 @@ module constants
|
|||
integer, parameter :: &
|
||||
NEUTRON = 1, &
|
||||
PHOTON = 2, &
|
||||
ELECTRON = 3
|
||||
ELECTRON = 3, &
|
||||
POSITRON = 4
|
||||
|
||||
! Angular distribution type
|
||||
integer, parameter :: &
|
||||
|
|
@ -219,7 +231,9 @@ module constants
|
|||
N_3HEA = 193, N_4N2P = 194, N_4N2A = 195, N_4NPA = 196, N_3P = 197, &
|
||||
N_N3P = 198, N_3N2PA = 199, N_5N2P = 200, N_P0 = 600, N_PC = 649, &
|
||||
N_D0 = 650, N_DC = 699, N_T0 = 700, N_TC = 749, N_3HE0 = 750, &
|
||||
N_3HEC = 799, N_A0 = 800, N_AC = 849, N_2N0 = 875, N_2NC = 891
|
||||
N_3HEC = 799, N_A0 = 800, N_AC = 849, N_2N0 = 875, N_2NC = 891, &
|
||||
COHERENT = 502, INCOHERENT = 504, PHOTOELECTRIC = 522, &
|
||||
PAIR_PROD_ELEC = 515, PAIR_PROD = 516, PAIR_PROD_NUC = 517
|
||||
|
||||
! Depletion reactions
|
||||
integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N]
|
||||
|
|
@ -343,7 +357,7 @@ module constants
|
|||
integer, parameter :: NO_BIN_FOUND = -1
|
||||
|
||||
! Tally filter and map types
|
||||
integer, parameter :: N_FILTER_TYPES = 20
|
||||
integer, parameter :: N_FILTER_TYPES = 21
|
||||
integer, parameter :: &
|
||||
FILTER_UNIVERSE = 1, &
|
||||
FILTER_MATERIAL = 2, &
|
||||
|
|
@ -364,7 +378,8 @@ module constants
|
|||
FILTER_LEGENDRE = 17, &
|
||||
FILTER_SPH_HARMONICS = 18, &
|
||||
FILTER_SPTL_LEGENDRE = 19, &
|
||||
FILTER_ZERNIKE = 20
|
||||
FILTER_ZERNIKE = 20, &
|
||||
FILTER_PARTICLE = 21
|
||||
|
||||
! Mesh types
|
||||
integer, parameter :: &
|
||||
|
|
@ -433,6 +448,7 @@ module constants
|
|||
integer(C_INT), bind(C, name='STREAM_SOURCE') :: STREAM_SOURCE
|
||||
integer(C_INT), bind(C, name='STREAM_URR_PTABLE') :: STREAM_URR_PTABLE
|
||||
integer(C_INT), bind(C, name='STREAM_VOLUME') :: STREAM_VOLUME
|
||||
integer(C_INT), bind(C, name='STREAM_PHOTON') :: STREAM_PHOTON
|
||||
integer(C_INT64_T), parameter :: DEFAULT_SEED = 1_8
|
||||
|
||||
! ============================================================================
|
||||
|
|
@ -455,6 +471,11 @@ module constants
|
|||
MODE_PARTICLE = 4, & ! Particle restart mode
|
||||
MODE_VOLUME = 5 ! Volume calculation mode
|
||||
|
||||
! Electron treatments
|
||||
integer, parameter :: &
|
||||
ELECTRON_LED = 1, & ! Local Energy Deposition
|
||||
ELECTRON_TTB = 2 ! Thick Target Bremsstrahlung
|
||||
|
||||
!=============================================================================
|
||||
! CMFD CONSTANTS
|
||||
|
||||
|
|
|
|||
14
src/endf.F90
14
src/endf.F90
|
|
@ -160,6 +160,20 @@ contains
|
|||
string = '(n,Xa)'
|
||||
case (444)
|
||||
string = '(damage)'
|
||||
case (COHERENT)
|
||||
string = 'coherent scatter'
|
||||
case (INCOHERENT)
|
||||
string = 'incoherent scatter'
|
||||
case (PAIR_PROD_ELEC)
|
||||
string = 'pair production, electron'
|
||||
case (PAIR_PROD)
|
||||
string = 'pair production'
|
||||
case (PAIR_PROD_NUC)
|
||||
string = 'pair production, nuclear'
|
||||
case (PHOTOELECTRIC)
|
||||
string = 'photoelectric'
|
||||
case (534 : 572)
|
||||
string = 'photoelectric, ' // trim(SUBSHELLS(MT - 533)) // ' subshell'
|
||||
case (600 : 648)
|
||||
string = '(n,p' // trim(to_str(MT-600)) // ')'
|
||||
case (649)
|
||||
|
|
|
|||
|
|
@ -331,8 +331,8 @@ contains
|
|||
c_k = c_k1
|
||||
end do
|
||||
|
||||
! Check to make sure k is <= NP - 1
|
||||
k = min(k, n_energy_out - 1)
|
||||
! 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)
|
||||
|
|
@ -359,7 +359,7 @@ contains
|
|||
end if
|
||||
|
||||
! Now interpolate between incident energy bins i and i + 1
|
||||
if (.not. histogram_interp) then
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ module geometry
|
|||
use constants
|
||||
use error, only: fatal_error, warning, write_message
|
||||
use geometry_header
|
||||
use particle_header, only: LocalCoord, Particle
|
||||
use particle_header
|
||||
use simulation_header
|
||||
use settings
|
||||
use surface_header
|
||||
|
|
@ -113,12 +113,18 @@ contains
|
|||
logical :: use_search_cells ! use cells provided as argument
|
||||
|
||||
do j = p % n_coord + 1, MAX_COORD
|
||||
call p % coord(j) % reset()
|
||||
call reset_coord(p % coord(j))
|
||||
end do
|
||||
j = p % n_coord
|
||||
|
||||
! set size of list to search
|
||||
! Determine universe (if not yet set, use root universe)
|
||||
i_universe = p % coord(j) % universe
|
||||
if (i_universe == C_NONE) then
|
||||
p % coord(j) % universe = root_universe
|
||||
i_universe = root_universe
|
||||
end if
|
||||
|
||||
! set size of list to search
|
||||
if (present(search_cells)) then
|
||||
use_search_cells = .true.
|
||||
n = size(search_cells)
|
||||
|
|
@ -330,7 +336,7 @@ contains
|
|||
call find_cell(p, found)
|
||||
if (.not. found) then
|
||||
if (p % alive) then ! Particle may have been killed in find_cell
|
||||
call p % mark_as_lost("Could not locate particle " &
|
||||
call particle_mark_as_lost(p, "Could not locate particle " &
|
||||
// trim(to_str(p % id)) // " after crossing a lattice boundary.")
|
||||
return
|
||||
end if
|
||||
|
|
@ -354,7 +360,7 @@ contains
|
|||
! Search for particle
|
||||
call find_cell(p, found)
|
||||
if (.not. found) then
|
||||
call p % mark_as_lost("Could not locate particle " // &
|
||||
call particle_mark_as_lost(p, "Could not locate particle " // &
|
||||
trim(to_str(p % id)) // " after crossing a lattice boundary.")
|
||||
return
|
||||
end if
|
||||
|
|
@ -434,7 +440,7 @@ contains
|
|||
end select LAT_TYPE
|
||||
|
||||
if (d_lat < ZERO) then
|
||||
call p % mark_as_lost("Particle " // trim(to_str(p % id)) &
|
||||
call particle_mark_as_lost(p, "Particle " // trim(to_str(p % id)) &
|
||||
//" had a negative distance to a lattice boundary. d = " &
|
||||
//trim(to_str(d_lat)))
|
||||
end if
|
||||
|
|
|
|||
6
src/geometry.h
Normal file
6
src/geometry.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef GEOMETRY_H
|
||||
#define GEOMETRY_H
|
||||
|
||||
extern "C" int openmc_root_universe;
|
||||
|
||||
#endif // GEOMETRY_H
|
||||
|
|
@ -288,7 +288,7 @@ module geometry_header
|
|||
end type Cell
|
||||
|
||||
! array index of the root universe
|
||||
integer :: root_universe = -1
|
||||
integer(C_INT), bind(C, name='openmc_root_universe') :: root_universe = -1
|
||||
|
||||
integer(C_INT32_T), bind(C) :: n_cells ! # of cells
|
||||
integer(C_INT32_T), bind(C) :: n_universes ! # of universes
|
||||
|
|
|
|||
|
|
@ -169,9 +169,9 @@ file_open(const char* filename, char mode, bool parallel)
|
|||
}
|
||||
|
||||
hid_t
|
||||
file_open(const std::string& filename, char mode, bool parallel=false)
|
||||
file_open(const std::string& filename, char mode, bool parallel)
|
||||
{
|
||||
file_open(filename.c_str(), mode, parallel);
|
||||
return file_open(filename.c_str(), mode, parallel);
|
||||
}
|
||||
|
||||
void file_close(hid_t file_id)
|
||||
|
|
@ -302,26 +302,24 @@ object_exists(hid_t object_id, const char* name)
|
|||
hid_t
|
||||
open_dataset(hid_t group_id, const char* name)
|
||||
{
|
||||
if (object_exists(group_id, name)) {
|
||||
return H5Dopen(group_id, name, H5P_DEFAULT);
|
||||
} else {
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
return H5Dopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
open_group(hid_t group_id, const char* name)
|
||||
{
|
||||
if (object_exists(group_id, name)) {
|
||||
return H5Gopen(group_id, name, H5P_DEFAULT);
|
||||
} else {
|
||||
if (!object_exists(group_id, name)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
return H5Gopen(group_id, name, H5P_DEFAULT);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -821,4 +819,14 @@ using_mpio_device(hid_t obj_id)
|
|||
return driver == H5FD_MPIO;
|
||||
}
|
||||
|
||||
// Specializations of the H5TypeMap template struct
|
||||
template<>
|
||||
const hid_t H5TypeMap<int>::type_id = H5T_NATIVE_INT;
|
||||
template<>
|
||||
const hid_t H5TypeMap<int64_t>::type_id = H5T_NATIVE_INT64;
|
||||
template<>
|
||||
const hid_t H5TypeMap<double>::type_id = H5T_NATIVE_DOUBLE;
|
||||
template <>
|
||||
const hid_t H5TypeMap<char>::type_id = H5T_NATIVE_CHAR;
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "hdf5_hl.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
|
@ -13,48 +14,28 @@
|
|||
|
||||
namespace openmc {
|
||||
|
||||
extern "C" bool attribute_exists(hid_t obj_id, const char* name);
|
||||
extern "C" size_t attribute_typesize(hid_t obj_id, const char* name);
|
||||
extern "C" hid_t create_group(hid_t parent_id, const char* name);
|
||||
hid_t create_group(hid_t parent_id, const std::string& name);
|
||||
extern "C" void close_dataset(hid_t dataset_id);
|
||||
extern "C" void close_group(hid_t group_id);
|
||||
extern "C" int dataset_ndims(hid_t dset);
|
||||
extern "C" size_t dataset_typesize(hid_t dset);
|
||||
extern "C" hid_t file_open(const char* filename, char mode, bool parallel);
|
||||
hid_t file_open(const std::string& filename, char mode, bool parallel);
|
||||
extern "C" void file_close(hid_t file_id);
|
||||
extern "C" void get_name(hid_t obj_id, char* name);
|
||||
extern "C" int get_num_datasets(hid_t group_id);
|
||||
extern "C" int get_num_groups(hid_t group_id);
|
||||
extern "C" void get_datasets(hid_t group_id, char* name[]);
|
||||
extern "C" void get_groups(hid_t group_id, char* name[]);
|
||||
extern "C" void get_shape(hid_t obj_id, hsize_t* dims);
|
||||
extern "C" void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims);
|
||||
extern "C" bool object_exists(hid_t object_id, const char* name);
|
||||
extern "C" hid_t open_dataset(hid_t group_id, const char* name);
|
||||
extern "C" hid_t open_group(hid_t group_id, const char* name);
|
||||
bool using_mpio_device(hid_t obj_id);
|
||||
//==============================================================================
|
||||
// Low-level internal functions
|
||||
//==============================================================================
|
||||
|
||||
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
const void* buffer);
|
||||
extern "C" void read_attr_double(hid_t obj_id, const char* name, double* buffer);
|
||||
extern "C" void read_attr_int(hid_t obj_id, const char* name, int* buffer);
|
||||
extern "C" void read_attr_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* 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,
|
||||
void* buffer, bool indep);
|
||||
extern "C" void read_double(hid_t obj_id, const char* name, double* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_int(hid_t obj_id, const char* name, int* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer, bool indep);
|
||||
extern "C" void read_complex(hid_t obj_id, const char* name,
|
||||
double _Complex* buffer, bool indep);
|
||||
void write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer, bool indep);
|
||||
bool using_mpio_device(hid_t obj_id);
|
||||
|
||||
//==============================================================================
|
||||
// Normal functions that are used to read/write files
|
||||
//==============================================================================
|
||||
|
||||
hid_t create_group(hid_t parent_id, const std::string& name);
|
||||
hid_t file_open(const std::string& filename, char mode, bool parallel=false);
|
||||
void write_string(hid_t group_id, const char* name, const std::string& buffer,
|
||||
bool indep);
|
||||
|
||||
void
|
||||
read_nd_vector(hid_t obj_id, const char* name, std::vector<double>& result,
|
||||
|
|
@ -89,51 +70,115 @@ 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);
|
||||
|
||||
extern "C" void read_tally_results(hid_t group_id, hsize_t n_filter,
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
bool attribute_exists(hid_t obj_id, const char* name);
|
||||
size_t attribute_typesize(hid_t obj_id, const char* name);
|
||||
hid_t create_group(hid_t parent_id, const char* name);
|
||||
void close_dataset(hid_t dataset_id);
|
||||
void close_group(hid_t group_id);
|
||||
int dataset_ndims(hid_t dset);
|
||||
size_t dataset_typesize(hid_t dset);
|
||||
hid_t file_open(const char* filename, char mode, bool parallel);
|
||||
void file_close(hid_t file_id);
|
||||
void get_name(hid_t obj_id, char* name);
|
||||
int get_num_datasets(hid_t group_id);
|
||||
int get_num_groups(hid_t group_id);
|
||||
void get_datasets(hid_t group_id, char* name[]);
|
||||
void get_groups(hid_t group_id, char* name[]);
|
||||
void get_shape(hid_t obj_id, hsize_t* dims);
|
||||
void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims);
|
||||
bool object_exists(hid_t object_id, const char* name);
|
||||
hid_t open_dataset(hid_t group_id, const char* name);
|
||||
hid_t open_group(hid_t group_id, const char* name);
|
||||
void read_attr_double(hid_t obj_id, const char* name, double* buffer);
|
||||
void read_attr_int(hid_t obj_id, const char* name, int* buffer);
|
||||
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);
|
||||
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,
|
||||
bool indep);
|
||||
void read_llong(hid_t obj_id, const char* name, long long* buffer,
|
||||
bool indep);
|
||||
void read_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer, bool indep);
|
||||
|
||||
|
||||
void read_tally_results(hid_t group_id, hsize_t n_filter,
|
||||
hsize_t n_score, double* results);
|
||||
|
||||
void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer);
|
||||
extern "C" void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const double* buffer);
|
||||
extern "C" void write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
void write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const int* buffer);
|
||||
extern "C" void write_attr_string(hid_t obj_id, const char* name, const char* buffer);
|
||||
|
||||
|
||||
void write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer, bool indep);
|
||||
extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
void write_attr_string(hid_t obj_id, const char* name, const char* buffer);
|
||||
void write_double(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const double* buffer, bool indep);
|
||||
extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
void write_int(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const int* buffer, bool indep);
|
||||
extern "C" void write_llong(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
void write_llong(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const long long* buffer, bool indep);
|
||||
|
||||
extern "C" void write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen,
|
||||
void write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen,
|
||||
const char* name, char const* buffer, bool indep);
|
||||
void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep);
|
||||
|
||||
|
||||
extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score,
|
||||
void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score,
|
||||
const double* results);
|
||||
} // extern "C"
|
||||
|
||||
template<std::size_t array_len> void
|
||||
write_int(hid_t group_id, char const *name,
|
||||
const std::array<int, array_len> &buffer, bool indep)
|
||||
//==============================================================================
|
||||
// Template struct used to map types to HDF5 datatype IDs, which are stored
|
||||
// using the type hid_t. By having a single static data member, the template can
|
||||
// be specialized for each type we know of. The specializations appear in the
|
||||
// .cpp file since they are definitions.
|
||||
//==============================================================================
|
||||
|
||||
template<typename T>
|
||||
struct H5TypeMap { static const hid_t type_id; };
|
||||
|
||||
//==============================================================================
|
||||
// Template functions used to provide simple interface to lower-level functions
|
||||
//==============================================================================
|
||||
|
||||
template<typename T> inline void
|
||||
write_attribute(hid_t obj_id, const char* name, T buffer)
|
||||
{
|
||||
hsize_t dims[1] {array_len};
|
||||
write_dataset(group_id, 1, dims, name, H5T_NATIVE_INT, buffer.data(), indep);
|
||||
write_attr(obj_id, name, 0, nullptr, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
|
||||
template<std::size_t array_len> void
|
||||
write_double(hid_t group_id, char const *name,
|
||||
const std::array<double, array_len> &buffer, bool indep)
|
||||
template<> inline void
|
||||
write_attribute<const char*>(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
hsize_t dims[1] {array_len};
|
||||
write_dataset(group_id, 1, dims, name, H5T_NATIVE_DOUBLE,
|
||||
buffer.data(), indep);
|
||||
write_attr_string(obj_id, name, buffer);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N> inline void
|
||||
write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
||||
{
|
||||
hsize_t dims[] {N};
|
||||
write_attr(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
write_string(obj_id, name, buffer, false);
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N> inline void
|
||||
write_dataset(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
||||
{
|
||||
hsize_t dims[] {N};
|
||||
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -10,25 +10,17 @@
|
|||
#include "hdf5_interface.h"
|
||||
#include "message_passing.h"
|
||||
#include "openmc.h"
|
||||
#include "settings.h"
|
||||
#ifdef _OPENMP
|
||||
#include "omp.h"
|
||||
#endif
|
||||
|
||||
// data/functions from Fortran side
|
||||
extern "C" bool openmc_check_overlaps;
|
||||
extern "C" bool openmc_write_all_tracks;
|
||||
extern "C" bool openmc_particle_restart_run;
|
||||
extern "C" bool openmc_restart_run;
|
||||
extern "C" void print_usage();
|
||||
extern "C" void print_version();
|
||||
|
||||
|
||||
// Paths to various files
|
||||
extern "C" {
|
||||
char* openmc_path_input;
|
||||
char* openmc_path_statepoint;
|
||||
char* openmc_path_sourcepoint;
|
||||
char* openmc_path_particle_restart;
|
||||
bool is_null(void* ptr) {return !ptr;}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ module input_xml
|
|||
use mgxs_interface
|
||||
use nuclide_header
|
||||
use output, only: title, header, print_plot
|
||||
use photon_header
|
||||
use plot_header
|
||||
use random_lcg, only: prn, openmc_set_seed
|
||||
use surface_header
|
||||
|
|
@ -81,6 +82,11 @@ module input_xml
|
|||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_lattices
|
||||
|
||||
subroutine read_settings(node_ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_settings
|
||||
|
||||
function find_root_universe() bind(C) result(root)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T) :: root
|
||||
|
|
@ -228,7 +234,7 @@ contains
|
|||
¬ exist! In order to run OpenMC, you first need a set of input &
|
||||
&files; at a minimum, this includes settings.xml, geometry.xml, &
|
||||
&and materials.xml. Please consult the user's guide at &
|
||||
&http://mit-crpg.github.io/openmc for further information.")
|
||||
&http://openmc.readthedocs.io for further information.")
|
||||
else
|
||||
! The settings.xml file is optional if we just want to make a plot.
|
||||
return
|
||||
|
|
@ -239,6 +245,9 @@ contains
|
|||
call doc % load_file(filename)
|
||||
root = doc % document_element()
|
||||
|
||||
! Read settings from C++ side
|
||||
call read_settings(root % ptr)
|
||||
|
||||
! Verbosity
|
||||
if (check_for_node(root, "verbosity")) then
|
||||
call get_node_value(root, "verbosity", verbosity)
|
||||
|
|
@ -389,6 +398,30 @@ contains
|
|||
call openmc_set_seed(seed)
|
||||
end if
|
||||
|
||||
! Check for electron treatment
|
||||
if (check_for_node(root, "electron_treatment")) then
|
||||
call get_node_value(root, "electron_treatment", temp_str)
|
||||
select case (to_lower(temp_str))
|
||||
case ("led")
|
||||
electron_treatment = ELECTRON_LED
|
||||
case ("ttb")
|
||||
electron_treatment = ELECTRON_TTB
|
||||
case default
|
||||
call fatal_error("Unrecognized electron treatment: " // &
|
||||
trim(temp_str) // ".")
|
||||
end select
|
||||
end if
|
||||
|
||||
! Check for photon transport
|
||||
if (check_for_node(root, "photon_transport")) then
|
||||
call get_node_value(root, "photon_transport", photon_transport)
|
||||
|
||||
if (.not. run_CE .and. photon_transport) then
|
||||
call fatal_error("Photon transport is not currently supported &
|
||||
&in Multi-group mode")
|
||||
end if
|
||||
end if
|
||||
|
||||
! Number of bins for logarithmic grid
|
||||
if (check_for_node(root, "log_grid_bins")) then
|
||||
call get_node_value(root, "log_grid_bins", n_log_bins)
|
||||
|
|
@ -425,6 +458,7 @@ contains
|
|||
if (n == 0) then
|
||||
! Default source is isotropic point source at origin with Watt spectrum
|
||||
allocate(external_source(1))
|
||||
external_source % particle = NEUTRON
|
||||
external_source % strength = ONE
|
||||
|
||||
allocate(SpatialPoint :: external_source(1) % space)
|
||||
|
|
@ -476,8 +510,21 @@ contains
|
|||
if (check_for_node(node_cutoff, "weight_avg")) then
|
||||
call get_node_value(node_cutoff, "weight_avg", weight_survive)
|
||||
end if
|
||||
if (check_for_node(node_cutoff, "energy")) then
|
||||
call get_node_value(node_cutoff, "energy", energy_cutoff)
|
||||
if (check_for_node(node_cutoff, "energy_neutron")) then
|
||||
call get_node_value(node_cutoff, "energy_neutron", energy_cutoff(1))
|
||||
elseif (check_for_node(node_cutoff, "energy")) then
|
||||
call warning("The use of an <energy> cutoff is deprecated and should &
|
||||
&be replaced by <energy_neutron>.")
|
||||
call get_node_value(node_cutoff, "energy", energy_cutoff(1))
|
||||
end if
|
||||
if (check_for_node(node_cutoff, "energy_photon")) then
|
||||
call get_node_value(node_cutoff, "energy_photon", energy_cutoff(2))
|
||||
end if
|
||||
if (check_for_node(node_cutoff, "energy_electron")) then
|
||||
call get_node_value(node_cutoff, "energy_electron", energy_cutoff(3))
|
||||
end if
|
||||
if (check_for_node(node_cutoff, "energy_positron")) then
|
||||
call get_node_value(node_cutoff, "energy_positron", energy_cutoff(4))
|
||||
end if
|
||||
end if
|
||||
|
||||
|
|
@ -1193,8 +1240,8 @@ contains
|
|||
end if
|
||||
|
||||
! Read cell temperatures. If the temperature is not specified, set it to
|
||||
! ERROR_REAL for now. During initialization we'll replace ERROR_REAL with
|
||||
! the temperature from the material data.
|
||||
! a negative number for now. During initialization we'll replace
|
||||
! negatives with the temperature from the material data.
|
||||
if (check_for_node(node_cell, "temperature")) then
|
||||
n = node_word_count(node_cell, "temperature")
|
||||
if (n > 0) then
|
||||
|
|
@ -1219,11 +1266,11 @@ contains
|
|||
c % sqrtkT(:) = sqrt(K_BOLTZMANN * c % sqrtkT(:))
|
||||
else
|
||||
allocate(c % sqrtkT(1))
|
||||
c % sqrtkT(1) = ERROR_REAL
|
||||
c % sqrtkT(1) = -1.0
|
||||
end if
|
||||
else
|
||||
allocate(c % sqrtkT(1))
|
||||
c % sqrtkT = ERROR_REAL
|
||||
c % sqrtkT = -1.0
|
||||
end if
|
||||
|
||||
! Add cell to dictionary
|
||||
|
|
@ -1369,7 +1416,7 @@ contains
|
|||
&materials.xml, settings.xml, or in the OPENMC_CROSS_SECTIONS&
|
||||
& environment variable. OpenMC needs such a file to identify &
|
||||
&where to find ACE cross section libraries. Please consult the&
|
||||
& user's guide at http://mit-crpg.github.io/openmc for &
|
||||
& user's guide at http://openmc.readthedocs.io for &
|
||||
&information on how to set up ACE cross section libraries.")
|
||||
else
|
||||
call warning("The CROSS_SECTIONS environment variable is &
|
||||
|
|
@ -1387,7 +1434,7 @@ contains
|
|||
&materials.xml or in the OPENMC_MG_CROSS_SECTIONS environment &
|
||||
&variable. OpenMC needs such a file to identify where to &
|
||||
&find MG cross section libraries. Please consult the user's &
|
||||
&guide at http://mit-crpg.github.io/openmc for information on &
|
||||
&guide at http://openmc.readthedocs.io for information on &
|
||||
&how to set up MG cross section libraries.")
|
||||
else if (len_trim(env_variable) /= 0) then
|
||||
path_cross_sections = trim(env_variable)
|
||||
|
|
@ -1450,9 +1497,11 @@ contains
|
|||
integer :: n_sab ! number of sab tables for a material
|
||||
integer :: i_library ! index in libraries array
|
||||
integer :: index_nuclide ! index in nuclides
|
||||
integer :: index_element ! index in elements
|
||||
integer :: index_sab ! index in sab_tables
|
||||
logical :: file_exists ! does materials.xml exist?
|
||||
character(20) :: name ! name of nuclide, e.g. 92235.03c
|
||||
character(20) :: name ! name of nuclide, e.g. U235
|
||||
character(3) :: element ! name of element, e.g. Zr
|
||||
character(MAX_WORD_LEN) :: units ! units on density
|
||||
character(MAX_LINE_LEN) :: filename ! absolute path to materials.xml
|
||||
character(MAX_LINE_LEN) :: temp_str ! temporary string when reading
|
||||
|
|
@ -1501,6 +1550,7 @@ contains
|
|||
|
||||
! Initialize count for number of nuclides/S(a,b) tables
|
||||
index_nuclide = 0
|
||||
index_element = 0
|
||||
index_sab = 0
|
||||
|
||||
do i = 1, n_materials
|
||||
|
|
@ -1538,7 +1588,7 @@ contains
|
|||
if (check_for_node(node_mat, "temperature")) then
|
||||
call get_node_value(node_mat, "temperature", material_temps(i))
|
||||
else
|
||||
material_temps(i) = ERROR_REAL
|
||||
material_temps(i) = -1.0
|
||||
end if
|
||||
|
||||
! Get pointer to density element
|
||||
|
|
@ -1723,6 +1773,7 @@ contains
|
|||
mat % n_nuclides = n
|
||||
allocate(mat % names(n))
|
||||
allocate(mat % nuclide(n))
|
||||
allocate(mat % element(n))
|
||||
allocate(mat % atom_density(n))
|
||||
|
||||
ALL_NUCLIDES: do j = 1, mat % n_nuclides
|
||||
|
|
@ -1753,6 +1804,27 @@ contains
|
|||
mat % nuclide(j) = nuclide_dict % get(to_lower(name))
|
||||
end if
|
||||
|
||||
! If the corresponding element hasn't been encountered yet and photon
|
||||
! transport will be used, we need to add its symbol to the element_dict
|
||||
if (photon_transport) then
|
||||
element = name(1:scan(name, '0123456789') - 1)
|
||||
|
||||
! Make sure photon cross section data is available
|
||||
if (.not. library_dict % has(to_lower(element))) then
|
||||
call fatal_error("Could not find element " // trim(element) &
|
||||
// " in cross_sections data file!")
|
||||
end if
|
||||
|
||||
if (.not. element_dict % has(element)) then
|
||||
index_element = index_element + 1
|
||||
mat % element(j) = index_element
|
||||
|
||||
call element_dict % set(element, index_element)
|
||||
else
|
||||
mat % element(j) = element_dict % get(element)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Copy name and atom/weight percent
|
||||
mat % names(j) = name
|
||||
mat % atom_density(j) = densities % data(j)
|
||||
|
|
@ -1868,6 +1940,7 @@ contains
|
|||
|
||||
! Set total number of nuclides and S(a,b) tables
|
||||
n_nuclides = index_nuclide
|
||||
n_elements = index_element
|
||||
n_sab_tables = index_sab
|
||||
|
||||
! Close materials XML file
|
||||
|
|
@ -1885,6 +1958,7 @@ contains
|
|||
integer :: i ! loop over user-specified tallies
|
||||
integer :: j ! loop over words
|
||||
integer :: k ! another loop index
|
||||
integer :: l ! loop over bins
|
||||
integer :: filter_id ! user-specified identifier for filter
|
||||
integer :: i_filt ! index in filters array
|
||||
integer :: i_elem ! index of entry in dictionary
|
||||
|
|
@ -2510,6 +2584,47 @@ contains
|
|||
end if
|
||||
end do
|
||||
end do
|
||||
|
||||
! Check if tally is compatible with particle type
|
||||
if (photon_transport) then
|
||||
if (t % find_filter(FILTER_PARTICLE) == 0) then
|
||||
do j = 1, n_scores
|
||||
select case (t % score_bins(j))
|
||||
case (SCORE_INVERSE_VELOCITY)
|
||||
call fatal_error("Particle filter must be used with photon &
|
||||
&transport on and inverse velocity score")
|
||||
case (SCORE_FLUX, SCORE_TOTAL, SCORE_SCATTER, SCORE_NU_SCATTER, &
|
||||
SCORE_ABSORPTION, SCORE_FISSION, SCORE_NU_FISSION, &
|
||||
SCORE_CURRENT, SCORE_EVENTS, SCORE_DELAYED_NU_FISSION, &
|
||||
SCORE_PROMPT_NU_FISSION, SCORE_DECAY_RATE)
|
||||
call warning("Particle filter is not used with photon transport&
|
||||
& on and " // trim(to_str(t % score_bins(j))) // " score")
|
||||
end select
|
||||
end do
|
||||
else
|
||||
select type(filt => filters(t % find_filter(FILTER_PARTICLE)) % obj)
|
||||
type is (ParticleFilter)
|
||||
do l = 1, filt % n_bins
|
||||
if (filt % particles(l) == ELECTRON .or. filt % particles(l) == POSITRON) then
|
||||
t % estimator = ESTIMATOR_ANALOG
|
||||
end if
|
||||
end do
|
||||
end select
|
||||
end if
|
||||
else
|
||||
if (t % find_filter(FILTER_PARTICLE) > 0) then
|
||||
select type(filt => filters(t % find_filter(FILTER_PARTICLE)) % obj)
|
||||
type is (ParticleFilter)
|
||||
do l = 1, filt % n_bins
|
||||
if (filt % particles(l) /= NEUTRON) then
|
||||
call warning("Particle filter other than NEUTRON used with &
|
||||
&photon transport turned off. All tallies for particle &
|
||||
&type " // trim(to_str(filt % particles(l))) // " will have no scores")
|
||||
end if
|
||||
end do
|
||||
end select
|
||||
end if
|
||||
end if
|
||||
else
|
||||
call fatal_error("No <scores> specified on tally " &
|
||||
// trim(to_str(t % id)) // ".")
|
||||
|
|
@ -3319,6 +3434,8 @@ contains
|
|||
libraries(i) % type = LIBRARY_NEUTRON
|
||||
case ('thermal')
|
||||
libraries(i) % type = LIBRARY_THERMAL
|
||||
case ('photon')
|
||||
libraries(i) % type = LIBRARY_PHOTON
|
||||
end select
|
||||
else
|
||||
call fatal_error("Missing library type")
|
||||
|
|
@ -3408,8 +3525,8 @@ contains
|
|||
end do
|
||||
|
||||
! Get the minimum and maximum energies
|
||||
energy_min_neutron = energy_bins(num_energy_groups + 1)
|
||||
energy_max_neutron = energy_bins(1)
|
||||
energy_min(NEUTRON) = energy_bins(num_energy_groups + 1)
|
||||
energy_max(NEUTRON) = energy_bins(1)
|
||||
|
||||
! Get the datasets present in the library
|
||||
call get_groups(file_id, names)
|
||||
|
|
@ -3519,15 +3636,22 @@ contains
|
|||
integer :: i, j
|
||||
integer :: i_library
|
||||
integer :: i_nuclide
|
||||
integer :: i_element
|
||||
integer :: i_sab
|
||||
integer(HID_T) :: file_id
|
||||
integer(HID_T) :: group_id
|
||||
logical :: mp_found ! if windowed multipole libraries were found
|
||||
character(MAX_WORD_LEN) :: name
|
||||
character(3) :: element
|
||||
type(SetChar) :: already_read
|
||||
type(SetChar) :: element_already_read
|
||||
|
||||
allocate(nuclides(n_nuclides))
|
||||
allocate(elements(n_elements))
|
||||
allocate(sab_tables(n_sab_tables))
|
||||
if (photon_transport .and. electron_treatment == ELECTRON_TTB) then
|
||||
allocate(ttb(n_materials))
|
||||
end if
|
||||
|
||||
! Read cross sections
|
||||
do i = 1, size(materials)
|
||||
|
|
@ -3559,15 +3683,50 @@ contains
|
|||
! Determine if minimum/maximum energy for this nuclide is greater/less
|
||||
! than the previous
|
||||
if (size(nuclides(i_nuclide) % grid) >= 1) then
|
||||
energy_min_neutron = max(energy_min_neutron, &
|
||||
energy_min(NEUTRON) = max(energy_min(NEUTRON), &
|
||||
nuclides(i_nuclide) % grid(1) % energy(1))
|
||||
energy_max_neutron = min(energy_max_neutron, nuclides(i_nuclide) % &
|
||||
energy_max(NEUTRON) = min(energy_max(NEUTRON), nuclides(i_nuclide) % &
|
||||
grid(1) % energy(size(nuclides(i_nuclide) % grid(1) % energy)))
|
||||
end if
|
||||
|
||||
! Add name and alias to dictionary
|
||||
call already_read % add(name)
|
||||
|
||||
! Check if elemental data has been read, if needed
|
||||
element = name(1:scan(name, '0123456789') - 1)
|
||||
if (photon_transport) then
|
||||
if (.not. element_already_read % contains(element)) then
|
||||
! Read photon interaction data from HDF5 photon library
|
||||
i_library = library_dict % get(to_lower(element))
|
||||
i_element = element_dict % get(element)
|
||||
call write_message('Reading ' // trim(element) // ' from ' // &
|
||||
trim(libraries(i_library) % path), 6)
|
||||
|
||||
! Open file and make sure version is sufficient
|
||||
file_id = file_open(libraries(i_library) % path, 'r')
|
||||
call check_data_version(file_id)
|
||||
|
||||
! Read element data from HDF5
|
||||
group_id = open_group(file_id, element)
|
||||
call elements(i_element) % from_hdf5(group_id)
|
||||
call close_group(group_id)
|
||||
call file_close(file_id)
|
||||
|
||||
! Determine if minimum/maximum energy for this element is
|
||||
! greater/less than the previous
|
||||
if (size(elements(i_element) % energy) >= 1) then
|
||||
energy_min(PHOTON) = max(energy_min(PHOTON), &
|
||||
exp(elements(i_element) % energy(1)))
|
||||
energy_max(PHOTON) = min(energy_max(PHOTON), &
|
||||
exp(elements(i_element) % energy(size(elements(i_element) &
|
||||
% energy))))
|
||||
end if
|
||||
|
||||
! Add element to set
|
||||
call element_already_read % add(element)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Read multipole file into the appropriate entry on the nuclides array
|
||||
if (temperature_multipole) call read_multipole_data(i_nuclide)
|
||||
end if
|
||||
|
|
@ -3577,14 +3736,43 @@ contains
|
|||
materials(i) % fissionable = .true.
|
||||
end if
|
||||
end do
|
||||
|
||||
! Generate material bremsstrahlung data for electrons and positrons
|
||||
if (photon_transport .and. electron_treatment == ELECTRON_TTB) then
|
||||
call bremsstrahlung_init(ttb(i) % electron, i, ELECTRON)
|
||||
call bremsstrahlung_init(ttb(i) % positron, i, POSITRON)
|
||||
end if
|
||||
end do
|
||||
|
||||
if (photon_transport .and. electron_treatment == ELECTRON_TTB) then
|
||||
! Deallocate element bremsstrahlung DCS and stopping power data since
|
||||
! only the material bremsstrahlung data is needed
|
||||
do i = 1, size(elements)
|
||||
if (allocated(elements(i) % stopping_power_collision)) &
|
||||
deallocate(elements(i) % stopping_power_collision)
|
||||
if (allocated(elements(i) % stopping_power_radiative)) &
|
||||
deallocate(elements(i) % stopping_power_radiative)
|
||||
if (allocated(elements(i) % dcs)) deallocate(elements(i) % dcs)
|
||||
if (allocated(ttb_k_grid)) deallocate(ttb_k_grid)
|
||||
end do
|
||||
|
||||
! Determine if minimum/maximum energy for bremsstrahlung is greater/less
|
||||
! than the current minimum/maximum
|
||||
if (size(ttb_e_grid) >= 1) then
|
||||
energy_min(PHOTON) = max(energy_min(PHOTON), ttb_e_grid(1))
|
||||
energy_max(PHOTON) = min(energy_max(PHOTON), ttb_e_grid(size(ttb_e_grid)))
|
||||
end if
|
||||
|
||||
! Take logarithm of energies since they are log-log interpolated
|
||||
ttb_e_grid = log(ttb_e_grid)
|
||||
end if
|
||||
|
||||
! Set up logarithmic grid for nuclides
|
||||
do i = 1, size(nuclides)
|
||||
call nuclides(i) % init_grid(energy_min_neutron, &
|
||||
energy_max_neutron, n_log_bins)
|
||||
call nuclides(i) % init_grid(energy_min(NEUTRON), &
|
||||
energy_max(NEUTRON), n_log_bins)
|
||||
end do
|
||||
log_spacing = log(energy_max_neutron/energy_min_neutron) / n_log_bins
|
||||
log_spacing = log(energy_max(NEUTRON)/energy_min(NEUTRON)) / n_log_bins
|
||||
|
||||
do i = 1, size(materials)
|
||||
! Skip materials with no S(a,b) tables
|
||||
|
|
@ -3627,9 +3815,9 @@ contains
|
|||
! grid has not been allocated
|
||||
if (size(nuclides(i) % grid) > 0) then
|
||||
if (nuclides(i) % grid(1) % energy(size(nuclides(i) % grid(1) % energy)) &
|
||||
== energy_max_neutron) then
|
||||
== energy_max(NEUTRON)) then
|
||||
call write_message("Maximum neutron transport energy: " // &
|
||||
trim(to_str(energy_max_neutron)) // " eV for " // &
|
||||
trim(to_str(energy_max(NEUTRON))) // " eV for " // &
|
||||
trim(adjustl(nuclides(i) % name)), 7)
|
||||
exit
|
||||
end if
|
||||
|
|
@ -3667,7 +3855,7 @@ 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) % sqrtkT(1) /= ERROR_REAL) cycle
|
||||
if (cells(i) % sqrtkT(1) >= ZERO) cycle
|
||||
|
||||
! Set the number of temperatures equal to the number of materials.
|
||||
deallocate(cells(i) % sqrtkT)
|
||||
|
|
@ -3683,7 +3871,7 @@ contains
|
|||
|
||||
! Use material default or global default temperature
|
||||
i_material = cells(i) % material(j)
|
||||
if (material_temps(i_material) /= ERROR_REAL) then
|
||||
if (material_temps(i_material) >= ZERO) then
|
||||
cells(i) % sqrtkT(j) = sqrt(K_BOLTZMANN * &
|
||||
material_temps(i_material))
|
||||
else
|
||||
|
|
|
|||
|
|
@ -119,9 +119,9 @@ Lattice::to_hdf5(hid_t lattices_group) const
|
|||
|
||||
if (outer != NO_OUTER_UNIVERSE) {
|
||||
int32_t outer_id = global_universes[outer]->id;
|
||||
write_int(lat_group, 0, nullptr, "outer", &outer_id, false);
|
||||
write_dataset(lat_group, "outer", outer_id);
|
||||
} else {
|
||||
write_int(lat_group, 0, nullptr, "outer", &outer, false);
|
||||
write_dataset(lat_group, "outer", outer);
|
||||
}
|
||||
|
||||
// Call subclass-overriden function to fill in other details.
|
||||
|
|
@ -348,16 +348,16 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const
|
|||
// Write basic lattice information.
|
||||
write_string(lat_group, "type", "rectangular", false);
|
||||
if (is_3d) {
|
||||
write_double(lat_group, "pitch", pitch, false);
|
||||
write_double(lat_group, "lower_left", lower_left, false);
|
||||
write_int(lat_group, "dimension", n_cells, false);
|
||||
write_dataset(lat_group, "pitch", pitch);
|
||||
write_dataset(lat_group, "lower_left", lower_left);
|
||||
write_dataset(lat_group, "dimension", n_cells);
|
||||
} else {
|
||||
std::array<double, 2> pitch_short {{pitch[0], pitch[1]}};
|
||||
write_double(lat_group, "pitch", pitch_short, false);
|
||||
write_dataset(lat_group, "pitch", pitch_short);
|
||||
std::array<double, 2> ll_short {{lower_left[0], lower_left[1]}};
|
||||
write_double(lat_group, "lower_left", ll_short, false);
|
||||
write_dataset(lat_group, "lower_left", ll_short);
|
||||
std::array<int, 2> nc_short {{n_cells[0], n_cells[1]}};
|
||||
write_int(lat_group, "dimension", nc_short, false);
|
||||
write_dataset(lat_group, "dimension", nc_short);
|
||||
}
|
||||
|
||||
// Write the universe ids. The convention here is to switch the ordering on
|
||||
|
|
@ -826,16 +826,16 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const
|
|||
{
|
||||
// Write basic lattice information.
|
||||
write_string(lat_group, "type", "hexagonal", false);
|
||||
write_int(lat_group, 0, nullptr, "n_rings", &n_rings, false);
|
||||
write_int(lat_group, 0, nullptr, "n_axial", &n_axial, false);
|
||||
write_dataset(lat_group, "n_rings", n_rings);
|
||||
write_dataset(lat_group, "n_axial", n_axial);
|
||||
if (is_3d) {
|
||||
write_double(lat_group, "pitch", pitch, false);
|
||||
write_double(lat_group, "center", center, false);
|
||||
write_dataset(lat_group, "pitch", pitch);
|
||||
write_dataset(lat_group, "center", center);
|
||||
} else {
|
||||
std::array<double, 1> pitch_short {{pitch[0]}};
|
||||
write_double(lat_group, "pitch", pitch_short, false);
|
||||
write_dataset(lat_group, "pitch", pitch_short);
|
||||
std::array<double, 2> center_short {{center[0], center[1]}};
|
||||
write_double(lat_group, "center", center_short, false);
|
||||
write_dataset(lat_group, "center", center_short);
|
||||
}
|
||||
|
||||
// Write the universe ids.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ module material_header
|
|||
use constants
|
||||
use dict_header, only: DictIntInt
|
||||
use error
|
||||
use math, only: spline, spline_integrate
|
||||
use nuclide_header
|
||||
use particle_header, only: Particle
|
||||
use photon_header
|
||||
use sab_header
|
||||
use simulation_header, only: log_spacing
|
||||
use stl_vector, only: VectorReal, VectorInt
|
||||
|
|
@ -14,6 +17,7 @@ module material_header
|
|||
implicit none
|
||||
|
||||
private
|
||||
public :: bremsstrahlung_init
|
||||
public :: free_memory_material
|
||||
public :: openmc_extend_materials
|
||||
public :: openmc_get_material_index
|
||||
|
|
@ -33,6 +37,7 @@ module material_header
|
|||
character(len=104) :: name = "" ! User-defined name
|
||||
integer :: n_nuclides = 0 ! number of nuclides
|
||||
integer, allocatable :: nuclide(:) ! index in nuclides array
|
||||
integer, allocatable :: element(:) ! index in elements array
|
||||
real(8) :: density ! total atom density in atom/b-cm
|
||||
real(C_DOUBLE), allocatable :: atom_density(:) ! nuclide atom density in atom/b-cm
|
||||
real(8) :: density_gpcc ! total density in g/cm^3
|
||||
|
|
@ -66,6 +71,8 @@ module material_header
|
|||
procedure :: init_nuclide_index => material_init_nuclide_index
|
||||
procedure :: assign_sab_tables => material_assign_sab_tables
|
||||
procedure :: calculate_xs => material_calculate_xs
|
||||
procedure, private :: calculate_neutron_xs
|
||||
procedure, private :: calculate_photon_xs
|
||||
end type Material
|
||||
|
||||
integer(C_INT32_T), public, bind(C) :: n_materials ! # of materials
|
||||
|
|
@ -251,18 +258,36 @@ contains
|
|||
end subroutine material_assign_sab_tables
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL_CALCULATE_XS determines the macroscopic cross sections for the material the
|
||||
! particle is currently traveling through.
|
||||
! MATERIAL_CALCULATE_XS determines the macroscopic cross sections for the
|
||||
! material the particle is currently traveling through.
|
||||
!===============================================================================
|
||||
|
||||
subroutine material_calculate_xs(this, E, sqrtkT, micro_xs, nuclides, &
|
||||
material_xs)
|
||||
subroutine material_calculate_xs(this, p)
|
||||
class(Material), intent(in) :: this
|
||||
real(8), intent(in) :: E ! Particle energy
|
||||
real(8), intent(in) :: sqrtkT ! Last temperature sampled
|
||||
type(Nuclide), allocatable, intent(in) :: nuclides(:)
|
||||
type(NuclideMicroXS), allocatable, intent(inout) :: micro_xs(:) ! Cache for each nuclide
|
||||
type(MaterialMacroXS), intent(inout) :: material_xs ! Cache for current material
|
||||
type(Particle), intent(in) :: p
|
||||
|
||||
! Set all material macroscopic cross sections to zero
|
||||
material_xs % total = ZERO
|
||||
material_xs % absorption = ZERO
|
||||
material_xs % fission = ZERO
|
||||
material_xs % nu_fission = ZERO
|
||||
|
||||
if (p % type == NEUTRON) then
|
||||
call this % calculate_neutron_xs(p)
|
||||
elseif (p % type == PHOTON) then
|
||||
call this % calculate_photon_xs(p)
|
||||
end if
|
||||
|
||||
end subroutine material_calculate_xs
|
||||
|
||||
!===============================================================================
|
||||
! CALCULATE_NEUTRON_XS determines the neutron cross section for the material the
|
||||
! particle is traveling through
|
||||
!===============================================================================
|
||||
|
||||
subroutine calculate_neutron_xs(this, p)
|
||||
class(Material), intent(in) :: this
|
||||
type(Particle), intent(in) :: p
|
||||
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: i_nuclide ! index into nuclides array
|
||||
|
|
@ -274,14 +299,8 @@ contains
|
|||
real(8) :: sab_frac ! fraction of atoms affected by S(a,b)
|
||||
logical :: check_sab ! should we check for S(a,b) table?
|
||||
|
||||
! Set all material macroscopic cross sections to zero
|
||||
material_xs % total = ZERO
|
||||
material_xs % absorption = ZERO
|
||||
material_xs % fission = ZERO
|
||||
material_xs % nu_fission = ZERO
|
||||
|
||||
! Find energy index on energy grid
|
||||
i_grid = int(log(E/energy_min_neutron)/log_spacing)
|
||||
i_grid = int(log(p % E/energy_min(NEUTRON))/log_spacing)
|
||||
|
||||
! Determine if this material has S(a,b) tables
|
||||
check_sab = (this % n_sab > 0)
|
||||
|
|
@ -307,7 +326,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 (E > sab_tables(i_sab) % data(1) % threshold_inelastic) then
|
||||
if (p % E > sab_tables(i_sab) % data(1) % threshold_inelastic) then
|
||||
i_sab = 0
|
||||
end if
|
||||
|
||||
|
|
@ -326,12 +345,12 @@ contains
|
|||
i_nuclide = this % nuclide(i)
|
||||
|
||||
! Calculate microscopic cross section for this nuclide
|
||||
if (E /= micro_xs(i_nuclide) % last_E &
|
||||
.or. sqrtkT /= micro_xs(i_nuclide) % last_sqrtkT &
|
||||
if (p % E /= micro_xs(i_nuclide) % last_E &
|
||||
.or. p % sqrtkT /= micro_xs(i_nuclide) % last_sqrtkT &
|
||||
.or. i_sab /= micro_xs(i_nuclide) % index_sab &
|
||||
.or. sab_frac /= micro_xs(i_nuclide) % sab_frac) then
|
||||
call nuclides(i_nuclide) % calculate_xs(i_sab, E, i_grid, &
|
||||
sqrtkT, sab_frac, micro_xs(i_nuclide))
|
||||
call nuclides(i_nuclide) % calculate_xs(i_sab, p % E, i_grid, &
|
||||
p % sqrtkT, sab_frac, micro_xs(i_nuclide))
|
||||
end if
|
||||
|
||||
! ======================================================================
|
||||
|
|
@ -357,7 +376,68 @@ contains
|
|||
atom_density * micro_xs(i_nuclide) % nu_fission
|
||||
end do
|
||||
|
||||
end subroutine material_calculate_xs
|
||||
end subroutine calculate_neutron_xs
|
||||
|
||||
!===============================================================================
|
||||
! CALCULATE_PHOTON_XS determines the macroscopic photon cross sections for the
|
||||
! material the particle is currently traveling through.
|
||||
!===============================================================================
|
||||
|
||||
subroutine calculate_photon_xs(this, p)
|
||||
class(Material), intent(in) :: this
|
||||
type(Particle), intent(in) :: p
|
||||
|
||||
integer :: i ! loop index over nuclides
|
||||
integer :: i_element ! index into elements array
|
||||
real(8) :: atom_density ! atom density of a nuclide
|
||||
|
||||
material_xs % coherent = ZERO
|
||||
material_xs % incoherent = ZERO
|
||||
material_xs % photoelectric = ZERO
|
||||
material_xs % pair_production = ZERO
|
||||
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, this % n_nuclides
|
||||
! ========================================================================
|
||||
! CALCULATE MICROSCOPIC CROSS SECTION
|
||||
|
||||
! Determine microscopic cross sections for this nuclide
|
||||
i_element = this % element(i)
|
||||
|
||||
! Calculate microscopic cross section for this nuclide
|
||||
if (p % E /= micro_photon_xs(i_element) % last_E) then
|
||||
call elements(i_element) % calculate_xs(&
|
||||
p % E, micro_photon_xs(i_element))
|
||||
end if
|
||||
|
||||
! ========================================================================
|
||||
! ADD TO MACROSCOPIC CROSS SECTION
|
||||
|
||||
! Copy atom density of nuclide in material
|
||||
atom_density = this % atom_density(i)
|
||||
|
||||
! Add contributions to material macroscopic total cross section
|
||||
material_xs % total = material_xs % total + &
|
||||
atom_density * micro_photon_xs(i_element) % total
|
||||
|
||||
! Add contributions to material macroscopic coherent cross section
|
||||
material_xs % coherent = material_xs % coherent + &
|
||||
atom_density * micro_photon_xs(i_element) % coherent
|
||||
|
||||
! Add contributions to material macroscopic incoherent cross section
|
||||
material_xs % incoherent = material_xs % incoherent + &
|
||||
atom_density * micro_photon_xs(i_element) % incoherent
|
||||
|
||||
! Add contributions to material macroscopic photoelectric cross section
|
||||
material_xs % photoelectric = material_xs % photoelectric + &
|
||||
atom_density * micro_photon_xs(i_element) % photoelectric
|
||||
|
||||
! Add contributions to material macroscopic pair production cross section
|
||||
material_xs % pair_production = material_xs % pair_production + &
|
||||
atom_density * micro_photon_xs(i_element) % pair_production
|
||||
end do
|
||||
|
||||
end subroutine calculate_photon_xs
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_MATERIAL deallocates global arrays defined in this module
|
||||
|
|
@ -620,4 +700,215 @@ contains
|
|||
|
||||
end function openmc_material_set_densities
|
||||
|
||||
subroutine bremsstrahlung_init(this, i_material, particle)
|
||||
class(BremsstrahlungData), intent(inout) :: this
|
||||
integer, intent(in) :: i_material
|
||||
integer, intent(in) :: particle
|
||||
|
||||
integer :: i, j
|
||||
integer :: i_k
|
||||
integer :: n, n_e, n_k
|
||||
real(8) :: c
|
||||
real(8) :: k, k_l, k_r
|
||||
real(8) :: e, e_l, e_r
|
||||
real(8) :: w, w_l, w_r
|
||||
real(8) :: x, x_l, x_r
|
||||
real(8) :: t
|
||||
real(8) :: r
|
||||
real(8) :: awr
|
||||
real(8) :: beta
|
||||
real(8) :: Z_eq_sq
|
||||
real(8) :: atom_density
|
||||
real(8) :: mass_density
|
||||
real(8) :: sum_density
|
||||
real(8), allocatable :: stopping_power_collision(:)
|
||||
real(8), allocatable :: stopping_power_radiative(:)
|
||||
real(8), allocatable :: stopping_power(:)
|
||||
real(8), allocatable :: dcs(:,:)
|
||||
real(8), allocatable :: f(:)
|
||||
real(8), allocatable :: z(:)
|
||||
logical :: positron_
|
||||
type(Material), pointer :: mat
|
||||
type(PhotonInteraction), pointer :: elm
|
||||
|
||||
! Get pointer to this material
|
||||
mat => materials(i_material)
|
||||
|
||||
! Determine whether we are generating electron or positron data
|
||||
positron_ = (particle == POSITRON)
|
||||
|
||||
! Get the size of the energy grids
|
||||
n_k = size(ttb_k_grid)
|
||||
n_e = size(ttb_e_grid)
|
||||
|
||||
! Allocate arrays for TTB data
|
||||
allocate(this % pdf(n_e, n_e), source=ZERO)
|
||||
allocate(this % cdf(n_e, n_e), source=ZERO)
|
||||
allocate(this % yield(n_e))
|
||||
|
||||
! Allocate temporary arrays
|
||||
allocate(stopping_power_collision(n_e), source=ZERO)
|
||||
allocate(stopping_power_radiative(n_e), source=ZERO)
|
||||
allocate(stopping_power(n_e))
|
||||
allocate(dcs(n_k, n_e), source=ZERO)
|
||||
allocate(f(n_e))
|
||||
allocate(z(n_e))
|
||||
|
||||
Z_eq_sq = ZERO
|
||||
sum_density = ZERO
|
||||
|
||||
! Calculate the molecular DCS and the molecular total stopping power using
|
||||
! Bragg's additivity rule.
|
||||
! TODO: The collision stopping power cannot be accurately calculated using
|
||||
! Bragg's additivity rule since the mean excitation energies and the
|
||||
! density effect corrections cannot simply be summed together. Bragg's
|
||||
! additivity rule fails especially when a higher-density compound is
|
||||
! composed of elements that are in lower-density form at normal temperature
|
||||
! and pressure (at which the NIST stopping powers are given). It will be
|
||||
! used to approximate the collision stopping powers for now, but should be
|
||||
! fixed in the future.
|
||||
do i = 1, mat % n_nuclides
|
||||
! Get pointer to current element
|
||||
elm => elements(mat % element(i))
|
||||
|
||||
awr = nuclides(mat % nuclide(i)) % awr
|
||||
|
||||
! Get atomic density and mass density of nuclide given atom percent
|
||||
if (mat % atom_density(1) > ZERO) then
|
||||
atom_density = mat % atom_density(i)
|
||||
mass_density = mat % atom_density(i) * awr
|
||||
! Given weight percent
|
||||
else
|
||||
atom_density = -mat % atom_density(i) / awr
|
||||
mass_density = -mat % atom_density(i)
|
||||
end if
|
||||
|
||||
! Calculate the "equivalent" atomic number Zeq of the material
|
||||
Z_eq_sq = Z_eq_sq + atom_density * elm % Z**2
|
||||
sum_density = sum_density + atom_density
|
||||
|
||||
! Accumulate material DCS
|
||||
dcs = dcs + atom_density * elm % Z**2 * elm % dcs
|
||||
|
||||
! Accumulate material collision stopping power
|
||||
stopping_power_collision = stopping_power_collision + mass_density &
|
||||
* MASS_NEUTRON / N_AVOGADRO * elm % stopping_power_collision
|
||||
|
||||
! Accumulate material radiative stopping power
|
||||
stopping_power_radiative = stopping_power_radiative + mass_density &
|
||||
* MASS_NEUTRON / N_AVOGADRO * elm % stopping_power_radiative
|
||||
end do
|
||||
Z_eq_sq = Z_eq_sq / sum_density
|
||||
|
||||
! Calculate the positron DCS and radiative stopping power. These are
|
||||
! obtained by multiplying the electron DCS and radiative stopping powers by
|
||||
! a factor r, which is a numerical approximation of the ratio of the
|
||||
! radiative stopping powers for positrons and electrons. Source: F. Salvat,
|
||||
! J. M. Fernández-Varea, and J. Sempau, "PENELOPE-2011: A Code System for
|
||||
! Monte Carlo Simulation of Electron and Photon Transport," OECD-NEA,
|
||||
! Issy-les-Moulineaux, France (2011).
|
||||
if (positron_) then
|
||||
do i = 1, n_e
|
||||
t = log(ONE + 1.0e6_8*ttb_e_grid(i)/(Z_eq_sq*MASS_ELECTRON_EV))
|
||||
r = ONE - exp(-1.2359e-1_8*t + 6.1274e-2_8*t**2 - 3.1516e-2_8*t**3 + &
|
||||
7.7446e-3_8*t**4 - 1.0595e-3_8*t**5 + 7.0568e-5_8*t**6 - &
|
||||
1.808e-6_8*t**7)
|
||||
stopping_power_radiative(i) = r*stopping_power_radiative(i)
|
||||
dcs(:,i) = r*dcs(:,i)
|
||||
end do
|
||||
end if
|
||||
|
||||
! Total material stopping power
|
||||
stopping_power = stopping_power_collision + stopping_power_radiative
|
||||
|
||||
! Loop over photon energies
|
||||
do i = 1, n_e - 1
|
||||
w = ttb_e_grid(i)
|
||||
|
||||
! Loop over incident particle energies
|
||||
do j = i, n_e
|
||||
e = ttb_e_grid(j)
|
||||
|
||||
! Reduced photon energy
|
||||
k = w / e
|
||||
|
||||
! Find the lower bounding index of the reduced photon energy
|
||||
i_k = binary_search(ttb_k_grid, n_k, k)
|
||||
|
||||
! Get the interpolation bounds
|
||||
k_l = ttb_k_grid(i_k)
|
||||
k_r = ttb_k_grid(i_k+1)
|
||||
x_l = dcs(i_k, j)
|
||||
x_r = dcs(i_k+1, j)
|
||||
|
||||
! Find the value of the DCS using linear interpolation in reduced
|
||||
! photon energy k
|
||||
x = x_l + (k - k_l) * (x_r - x_l) / (k_r - k_l)
|
||||
|
||||
! Ratio of the velocity of the charged particle to the speed of light
|
||||
beta = sqrt(e*(e + TWO*MASS_ELECTRON_EV)) / (e + MASS_ELECTRON_EV)
|
||||
|
||||
! Compute the integrand of the PDF
|
||||
f(j) = x / (beta**2 * stopping_power(j) * w)
|
||||
end do
|
||||
|
||||
! Number of points to integrate
|
||||
n = n_e - i + 1
|
||||
|
||||
! Integrate the PDF using cubic spline integration over the incident
|
||||
! particle energy
|
||||
if (n > 2) then
|
||||
call spline(n, ttb_e_grid(i:), f(i:), z(i:))
|
||||
|
||||
c = ZERO
|
||||
do j = i, n_e - 1
|
||||
c = c + spline_integrate(n, ttb_e_grid(i:), f(i:), z(i:), &
|
||||
ttb_e_grid(j), ttb_e_grid(j+1))
|
||||
this % pdf(i,j+1) = c
|
||||
end do
|
||||
|
||||
! Integrate the last two points using trapezoidal rule in log-log space
|
||||
else
|
||||
e_l = log(ttb_e_grid(i))
|
||||
e_r = log(ttb_e_grid(i+1))
|
||||
x_l = log(f(i))
|
||||
x_r = log(f(i+1))
|
||||
|
||||
this % pdf(i,i+1) = HALF * (e_r - e_l) * (exp(e_l + x_l) + exp(e_r + x_r))
|
||||
end if
|
||||
end do
|
||||
|
||||
! Loop over incident particle energies
|
||||
do j = 2, n_e
|
||||
! Set last element of PDF to small non-zero value to enable log-log
|
||||
! interpolation
|
||||
this % pdf(j,j) = exp(-500.0_8)
|
||||
|
||||
! Loop over photon energies
|
||||
c = ZERO
|
||||
do i = 1, j - 1
|
||||
! Integrate the CDF from the PDF using the trapezoidal rule in log-log
|
||||
! space
|
||||
w_l = log(ttb_e_grid(i))
|
||||
w_r = log(ttb_e_grid(i+1))
|
||||
x_l = log(this % pdf(i,j))
|
||||
x_r = log(this % pdf(i+1,j))
|
||||
|
||||
c = c + HALF * (w_r - w_l) * (exp(w_l + x_l) + exp(w_r + x_r))
|
||||
this % cdf(i+1,j) = c
|
||||
end do
|
||||
|
||||
! Set photon number yield
|
||||
this % yield(j) = c
|
||||
end do
|
||||
|
||||
! Use logarithm of number yield since it is log-log interpolated
|
||||
where (this % yield > ZERO)
|
||||
this % yield = log(this % yield)
|
||||
elsewhere
|
||||
this % yield = -500.0_8
|
||||
end where
|
||||
|
||||
end subroutine bremsstrahlung_init
|
||||
|
||||
end module material_header
|
||||
|
|
|
|||
42
src/math.F90
42
src/math.F90
|
|
@ -18,11 +18,9 @@ module math
|
|||
public :: faddeeva
|
||||
public :: w_derivative
|
||||
public :: broaden_wmp_polynomials
|
||||
|
||||
!===============================================================================
|
||||
! FADDEEVA_W evaluates the scaled complementary error function. This
|
||||
! interfaces with the MIT C library
|
||||
!===============================================================================
|
||||
public :: spline
|
||||
public :: spline_interpolate
|
||||
public :: spline_integrate
|
||||
|
||||
interface
|
||||
|
||||
|
|
@ -105,6 +103,40 @@ module math
|
|||
real(C_DOUBLE), intent(inout) :: factors(n)
|
||||
end subroutine broaden_wmp_polynomials
|
||||
|
||||
subroutine spline(n, x, y, z) bind(C, name='spline_c')
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
integer(C_INT), value, intent(in) :: n
|
||||
real(C_DOUBLE), intent(in) :: x(n)
|
||||
real(C_DOUBLE), intent(in) :: y(n)
|
||||
real(C_DOUBLE), intent(in) :: z(n)
|
||||
end subroutine spline
|
||||
|
||||
function spline_interpolate(n, x, y, z, xint) &
|
||||
bind(C, name='spline_interpolate_c') result(yint)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
integer(C_INT), value, intent(in) :: n
|
||||
real(C_DOUBLE), intent(in) :: x(n)
|
||||
real(C_DOUBLE), intent(in) :: y(n)
|
||||
real(C_DOUBLE), intent(in) :: z(n)
|
||||
real(C_DOUBLE), value, intent(in) :: xint
|
||||
real(C_DOUBLE) :: yint
|
||||
end function spline_interpolate
|
||||
|
||||
function spline_integrate(n, x, y, z, xa, xb) &
|
||||
bind(C, name='spline_integrate_c') result(s)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
integer(C_INT), value, intent(in) :: n
|
||||
real(C_DOUBLE), intent(in) :: x(n)
|
||||
real(C_DOUBLE), intent(in) :: y(n)
|
||||
real(C_DOUBLE), intent(in) :: z(n)
|
||||
real(C_DOUBLE), value, intent(in) :: xa
|
||||
real(C_DOUBLE), value, intent(in) :: xb
|
||||
real(C_DOUBLE) :: s
|
||||
end function spline_integrate
|
||||
|
||||
function faddeeva_w(z, relerr) bind(C, name='Faddeeva_w') result(w)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
|
|
|
|||
|
|
@ -690,4 +690,96 @@ void broaden_wmp_polynomials_c(double E, double dopp, int n, double factors[]) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void spline_c(int n, const double x[], const double y[], double z[])
|
||||
{
|
||||
double c_new[n-1];
|
||||
|
||||
// Set natural boundary conditions
|
||||
c_new[0] = 0.0;
|
||||
z[0] = 0.0;
|
||||
z[n-1] = 0.0;
|
||||
|
||||
// Solve using tridiagonal matrix algorithm; first do forward sweep
|
||||
for (int i = 1; i < n - 1; i++) {
|
||||
double a = x[i] - x[i-1];
|
||||
double c = x[i+1] - x[i];
|
||||
double b = 2.0*(a + c);
|
||||
double d = 6.0*((y[i+1] - y[i])/c - (y[i] - y[i-1])/a);
|
||||
|
||||
c_new[i] = c/(b - a*c_new[i-1]);
|
||||
z[i] = (d - a*z[i-1])/(b - a*c_new[i-1]);
|
||||
}
|
||||
|
||||
// Back substitution
|
||||
for (int i = n - 2; i >= 0; i--) {
|
||||
z[i] = z[i] - c_new[i]*z[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double spline_interpolate_c(int n, const double x[], const double y[],
|
||||
const double z[], double xint)
|
||||
{
|
||||
// Find the lower bounding index in x of xint
|
||||
int i = n - 1;
|
||||
while (--i) {
|
||||
if (xint >= x[i]) break;
|
||||
}
|
||||
|
||||
double h = x[i+1] - x[i];
|
||||
double r = xint - x[i];
|
||||
|
||||
// Compute the coefficients
|
||||
double b = (y[i+1] - y[i])/h - (h/6.0)*(z[i+1] + 2.0*z[i]);
|
||||
double c = z[i]/2.0;
|
||||
double d = (z[i+1] - z[i])/(h*6.0);
|
||||
|
||||
return y[i] + b*r + c*r*r + d*r*r*r;
|
||||
}
|
||||
|
||||
|
||||
double spline_integrate_c(int n, const double x[], const double y[],
|
||||
const double z[], double xa, double xb)
|
||||
{
|
||||
// Find the lower bounding index in x of the lower limit of integration.
|
||||
int ia = n - 1;
|
||||
while (--ia) {
|
||||
if (xa >= x[ia]) break;
|
||||
}
|
||||
|
||||
// Find the lower bounding index in x of the upper limit of integration.
|
||||
int ib = n - 1;
|
||||
while (--ib) {
|
||||
if (xb >= x[ib]) break;
|
||||
}
|
||||
|
||||
// Evaluate the integral
|
||||
double s = 0.0;
|
||||
for (int i = ia; i <= ib; i++) {
|
||||
double h = x[i+1] - x[i];
|
||||
|
||||
// Compute the coefficients
|
||||
double b = (y[i+1] - y[i])/h - (h/6.0)*(z[i+1] + 2.0*z[i]);
|
||||
double c = z[i]/2.0;
|
||||
double d = (z[i+1] - z[i])/(h*6.0);
|
||||
|
||||
// Subtract the integral from x[ia] to xa
|
||||
if (i == ia) {
|
||||
double r = xa - x[ia];
|
||||
s = s - (y[i]*r + b/2.0*r*r + c/3.0*r*r*r + d/4.0*r*r*r*r);
|
||||
}
|
||||
|
||||
// Integrate from x[ib] to xb in final interval
|
||||
if (i == ib) {
|
||||
h = xb - x[ib];
|
||||
}
|
||||
|
||||
// Accumulate the integral
|
||||
s = s + y[i]*h + b/2.0*h*h + c/3.0*h*h*h + d/4.0*h*h*h*h;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -149,5 +149,57 @@ extern "C" double watt_spectrum_c(double a, double b);
|
|||
extern "C" void broaden_wmp_polynomials_c(double E, double dopp, int n,
|
||||
double factors[]);
|
||||
|
||||
//==============================================================================
|
||||
//! Constructs a natural cubic spline.
|
||||
//!
|
||||
//! Given a tabulated function y_i = f(x_i), this computes the second
|
||||
//! derivative of the interpolating function at each x_i, which can then be
|
||||
//! used in any subsequent calls to spline_interpolate or spline_integrate for
|
||||
//! the same set of x and y values.
|
||||
//!
|
||||
//! @param n Number of points
|
||||
//! @param x Values of the independent variable, which must be strictly
|
||||
//! increasing.
|
||||
//! @param y Values of the dependent variable.
|
||||
//! @param[out] z The second derivative of the interpolating function at each
|
||||
//! value of x.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void spline_c(int n, const double x[], const double y[], double z[]);
|
||||
|
||||
//==============================================================================
|
||||
//! Determine the cubic spline interpolated y-value for a given x-value.
|
||||
//!
|
||||
//! @param n Number of points
|
||||
//! @param x Values of the independent variable, which must be strictly
|
||||
//! increasing.
|
||||
//! @param y Values of the dependent variable.
|
||||
//! @param z The second derivative of the interpolating function at each
|
||||
//! value of x.
|
||||
//! @param xint Point at which to evaluate the cubic spline polynomial
|
||||
//! @result Interpolated value
|
||||
//==============================================================================
|
||||
|
||||
extern "C" double spline_interpolate_c(int n, const double x[], const double y[],
|
||||
const double z[], double xint);
|
||||
|
||||
//==============================================================================
|
||||
//! Evaluate the definite integral of the interpolating cubic spline between
|
||||
//! the given endpoints.
|
||||
//!
|
||||
//! @param n Number of points
|
||||
//! @param x Values of the independent variable, which must be strictly
|
||||
//! increasing.
|
||||
//! @param y Values of the dependent variable.
|
||||
//! @param z The second derivative of the interpolating function at each
|
||||
//! value of x.
|
||||
//! @param xa Lower limit of integration
|
||||
//! @param xb Upper limit of integration
|
||||
//! @result Integral
|
||||
//==============================================================================
|
||||
|
||||
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 // MATH_FUNCTIONS_H
|
||||
|
|
|
|||
|
|
@ -42,14 +42,15 @@ module nuclide_header
|
|||
|
||||
! Positions for first dimension of Nuclide % xs
|
||||
integer, parameter :: &
|
||||
XS_TOTAL = 1, &
|
||||
XS_ABSORPTION = 2, &
|
||||
XS_FISSION = 3, &
|
||||
XS_NU_FISSION = 4
|
||||
XS_TOTAL = 1, &
|
||||
XS_ABSORPTION = 2, &
|
||||
XS_FISSION = 3, &
|
||||
XS_NU_FISSION = 4, &
|
||||
XS_PHOTON_PROD = 5
|
||||
|
||||
! The array within SumXS is of shape (4, n_energy) where the first dimension
|
||||
! The array within SumXS is of shape (5, n_energy) where the first dimension
|
||||
! corresponds to the following values: 1) total, 2) absorption (MT > 100), 3)
|
||||
! fission, 4) neutron production
|
||||
! fission, 4) neutron production, 5) photon production
|
||||
type SumXS
|
||||
real(8), allocatable :: value(:,:)
|
||||
end type SumXS
|
||||
|
|
@ -104,8 +105,8 @@ module nuclide_header
|
|||
integer :: reaction_index(891)
|
||||
|
||||
! Fission energy release
|
||||
class(Function1D), allocatable :: fission_q_prompt ! prompt neutrons, gammas
|
||||
class(Function1D), allocatable :: fission_q_recov ! neutrons, gammas, betas
|
||||
class(Function1D), allocatable :: fission_q_prompt ! fragments and prompt neutrons, gammas
|
||||
class(Function1D), allocatable :: fission_q_recov ! fragments, neutrons, gammas, betas
|
||||
|
||||
contains
|
||||
procedure :: assign_0K_elastic_scattering
|
||||
|
|
@ -138,6 +139,7 @@ module nuclide_header
|
|||
! 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
|
||||
|
||||
! Cross sections for depletion reactions (note that these are not stored in
|
||||
! macroscopic cache)
|
||||
|
|
@ -169,6 +171,13 @@ module nuclide_header
|
|||
real(C_DOUBLE) :: absorption ! macroscopic absorption xs
|
||||
real(C_DOUBLE) :: fission ! macroscopic fission xs
|
||||
real(C_DOUBLE) :: nu_fission ! macroscopic production xs
|
||||
real(C_DOUBLE) :: photon_prod ! macroscopic photon production xs
|
||||
|
||||
! Photon cross sections
|
||||
real(C_DOUBLE) :: coherent ! macroscopic coherent xs
|
||||
real(C_DOUBLE) :: incoherent ! macroscopic incoherent xs
|
||||
real(C_DOUBLE) :: photoelectric ! macroscopic photoelectric xs
|
||||
real(C_DOUBLE) :: pair_production ! macroscopic pair production xs
|
||||
end type MaterialMacroXS
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -196,8 +205,8 @@ module nuclide_header
|
|||
!$omp threadprivate(micro_xs, material_xs)
|
||||
|
||||
! Minimum/maximum energies
|
||||
real(8) :: energy_min_neutron = ZERO
|
||||
real(8) :: energy_max_neutron = INFINITY
|
||||
real(8) :: energy_min(2) = [ZERO, ZERO]
|
||||
real(8) :: energy_max(2) = [INFINITY, INFINITY]
|
||||
|
||||
contains
|
||||
|
||||
|
|
@ -549,34 +558,36 @@ contains
|
|||
if (object_exists(group_id, 'fission_energy_release')) then
|
||||
fer_group = open_group(group_id, 'fission_energy_release')
|
||||
|
||||
! Check to see if this is polynomial or tabulated data
|
||||
! Q-PROMPT
|
||||
fer_dset = open_dataset(fer_group, 'q_prompt')
|
||||
call read_attribute(temp_str, fer_dset, 'type')
|
||||
if (temp_str == 'Polynomial') then
|
||||
! Read the prompt Q-value
|
||||
allocate(Polynomial :: this % fission_q_prompt)
|
||||
call this % fission_q_prompt % from_hdf5(fer_dset)
|
||||
call close_dataset(fer_dset)
|
||||
|
||||
! Read the recoverable energy Q-value
|
||||
allocate(Polynomial :: this % fission_q_recov)
|
||||
fer_dset = open_dataset(fer_group, 'q_recoverable')
|
||||
call this % fission_q_recov % from_hdf5(fer_dset)
|
||||
call close_dataset(fer_dset)
|
||||
else if (temp_str == 'Tabulated1D') then
|
||||
! Read the prompt Q-value
|
||||
allocate(Tabulated1D :: this % fission_q_prompt)
|
||||
call this % fission_q_prompt % from_hdf5(fer_dset)
|
||||
call close_dataset(fer_dset)
|
||||
else
|
||||
call fatal_error('Unrecognized fission prompt energy release format.')
|
||||
end if
|
||||
|
||||
! Read the recoverable energy Q-value
|
||||
! Q-RECOV
|
||||
fer_dset = open_dataset(fer_group, 'q_recoverable')
|
||||
call read_attribute(temp_str, fer_dset, 'type')
|
||||
if (temp_str == 'Polynomial') then
|
||||
allocate(Polynomial :: this % fission_q_recov)
|
||||
call this % fission_q_recov % from_hdf5(fer_dset)
|
||||
call close_dataset(fer_dset)
|
||||
else if (temp_str == 'Tabulated1D') then
|
||||
allocate(Tabulated1D :: this % fission_q_recov)
|
||||
fer_dset = open_dataset(fer_group, 'q_recoverable')
|
||||
call this % fission_q_recov % from_hdf5(fer_dset)
|
||||
call close_dataset(fer_dset)
|
||||
else
|
||||
call fatal_error('Unrecognized fission energy release format.')
|
||||
call fatal_error('Unrecognized fission recoverable energy release format.')
|
||||
end if
|
||||
|
||||
call close_group(fer_group)
|
||||
end if
|
||||
|
||||
|
|
@ -591,7 +602,7 @@ contains
|
|||
subroutine nuclide_create_derived(this)
|
||||
class(Nuclide), intent(inout) :: this
|
||||
|
||||
integer :: i, j, k
|
||||
integer :: i, j, k, l
|
||||
integer :: t
|
||||
integer :: m
|
||||
integer :: n
|
||||
|
|
@ -606,7 +617,7 @@ contains
|
|||
do i = 1, n_temperature
|
||||
! Allocate and initialize derived cross sections
|
||||
n_grid = size(this % grid(i) % energy)
|
||||
allocate(this % xs(i) % value(4,n_grid))
|
||||
allocate(this % xs(i) % value(5,n_grid))
|
||||
this % xs(i) % value(:,:) = ZERO
|
||||
end do
|
||||
|
||||
|
|
@ -638,6 +649,18 @@ contains
|
|||
this % xs(t) % value(XS_TOTAL,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_TOTAL,j:j+n-1) + rx % xs(t) % value
|
||||
|
||||
! Calculate photon production cross section
|
||||
do k = 1, size(rx % products)
|
||||
if (rx % products(k) % particle == 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))
|
||||
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) % &
|
||||
|
|
@ -664,10 +687,6 @@ contains
|
|||
this % xs(t) % value(XS_ABSORPTION,j:j+n-1) = this % xs(t) % &
|
||||
value(XS_ABSORPTION,j:j+n-1) + rx % xs(t) % value
|
||||
|
||||
! If total fission reaction is present, there's no need to store the
|
||||
! reaction cross-section since it was copied to this % fission
|
||||
if (rx % MT == N_FISSION) deallocate(rx % xs(t) % value)
|
||||
|
||||
! Keep track of this reaction for easy searching later
|
||||
if (t == 1) then
|
||||
i_fission = i_fission + 1
|
||||
|
|
@ -975,6 +994,10 @@ contains
|
|||
micro_xs % fission = ZERO
|
||||
micro_xs % nu_fission = ZERO
|
||||
end if
|
||||
|
||||
! Calculate microscopic nuclide photon production cross section
|
||||
micro_xs % photon_prod = (ONE - f) * xs % value(XS_PHOTON_PROD,i_grid) &
|
||||
+ f * xs % value(XS_PHOTON_PROD,i_grid + 1)
|
||||
end associate
|
||||
|
||||
! Depletion-related reactions
|
||||
|
|
@ -1695,8 +1718,8 @@ contains
|
|||
if (res_scat_on) call nuclides(n) % assign_0K_elastic_scattering()
|
||||
|
||||
! Initialize nuclide grid
|
||||
call nuclides(n) % init_grid(energy_min_neutron, &
|
||||
energy_max_neutron, n_log_bins)
|
||||
call nuclides(n) % init_grid(energy_min(NEUTRON), &
|
||||
energy_max(NEUTRON), n_log_bins)
|
||||
else
|
||||
err = E_DATA
|
||||
call set_errmsg("Nuclide '" // trim(name_) // "' is not present &
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ contains
|
|||
write(UNIT=OUTPUT_UNIT, FMT=*) &
|
||||
' | The OpenMC Monte Carlo Code'
|
||||
write(UNIT=OUTPUT_UNIT, FMT=*) &
|
||||
' Copyright | 2011-2018 Massachusetts Institute of Technology'
|
||||
' Copyright | 2011-2018 MIT and OpenMC contributors'
|
||||
write(UNIT=OUTPUT_UNIT, FMT=*) &
|
||||
' License | http://openmc.readthedocs.io/en/latest/license.html'
|
||||
write(UNIT=OUTPUT_UNIT, FMT='(11X,"Version | ",I1,".",I2,".",I1)') &
|
||||
|
|
@ -171,7 +171,7 @@ contains
|
|||
write(UNIT=OUTPUT_UNIT, FMT='(1X,A,A)') "Git SHA1: ", GIT_SHA1
|
||||
#endif
|
||||
write(UNIT=OUTPUT_UNIT, FMT=*) "Copyright (c) 2011-2018 &
|
||||
&Massachusetts Institute of Technology"
|
||||
&Massachusetts Institute of Technology and OpenMC contributors"
|
||||
write(UNIT=OUTPUT_UNIT, FMT=*) "MIT/X license at &
|
||||
&<http://openmc.readthedocs.io/en/latest/license.html>"
|
||||
end if
|
||||
|
|
@ -547,7 +547,7 @@ contains
|
|||
|
||||
! format for write statements
|
||||
100 format (1X,A,T36,"= ",ES11.4," seconds")
|
||||
101 format (1X,A,T36,"= ",A," neutrons/second")
|
||||
101 format (1X,A,T36,"= ",A," particles/second")
|
||||
|
||||
end subroutine print_runtime
|
||||
|
||||
|
|
|
|||
225
src/particle.cpp
Normal file
225
src/particle.cpp
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
#include "particle.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "openmc.h"
|
||||
#include "settings.h"
|
||||
#include "simulation.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// LocalCoord implementation
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
LocalCoord::reset()
|
||||
{
|
||||
cell = 0;
|
||||
universe = 0;
|
||||
lattice = 0;
|
||||
lattice_x = 0;
|
||||
lattice_y = 0;
|
||||
rotated = false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Particle implementation
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
Particle::clear()
|
||||
{
|
||||
// reset any coordinate levels
|
||||
for (int i=0; i<MAX_COORD; ++i) coord[i].reset();
|
||||
}
|
||||
|
||||
void
|
||||
Particle::create_secondary(const double* uvw, double E, int type, bool run_CE)
|
||||
{
|
||||
if (n_secondary == MAX_SECONDARY) {
|
||||
fatal_error("Too many secondary particles created.");
|
||||
}
|
||||
|
||||
int64_t n = n_secondary;
|
||||
secondary_bank[n].particle = type;
|
||||
secondary_bank[n].wgt = wgt;
|
||||
std::copy(coord[0].xyz, coord[0].xyz + 3, secondary_bank[n].xyz);
|
||||
std::copy(uvw, uvw + 3, secondary_bank[n].uvw);
|
||||
secondary_bank[n].E = E;
|
||||
if (!run_CE) secondary_bank[n].E = g;
|
||||
n_secondary += 1;
|
||||
}
|
||||
|
||||
void
|
||||
Particle::initialize()
|
||||
{
|
||||
// Clear coordinate lists
|
||||
clear();
|
||||
|
||||
// Set particle to neutron that's alive
|
||||
type = NEUTRON;
|
||||
alive = true;
|
||||
|
||||
// clear attributes
|
||||
surface = 0;
|
||||
cell_born = 0;
|
||||
material = 0;
|
||||
last_material = 0;
|
||||
last_sqrtkT = 0;
|
||||
wgt = 1.0;
|
||||
last_wgt = 1.0;
|
||||
absorb_wgt = 0.0;
|
||||
n_bank = 0;
|
||||
wgt_bank = 0.0;
|
||||
sqrtkT = -1.0;
|
||||
n_collision = 0;
|
||||
fission = false;
|
||||
delayed_group = 0;
|
||||
for (int i=0; i<MAX_DELAYED_GROUPS; ++i) {
|
||||
n_delayed_bank[i] = 0;
|
||||
}
|
||||
g = 0;
|
||||
|
||||
// Set up base level coordinates
|
||||
coord[0].universe = C_NONE;
|
||||
n_coord = 1;
|
||||
last_n_coord = 1;
|
||||
}
|
||||
|
||||
void
|
||||
Particle::from_source(const Bank* src, bool run_CE, const double* energy_bin_avg)
|
||||
{
|
||||
// set defaults
|
||||
initialize();
|
||||
|
||||
// copy attributes from source bank site
|
||||
type = src->particle;
|
||||
wgt = src->wgt;
|
||||
last_wgt = src->wgt;
|
||||
std::copy(src->xyz, src->xyz + 3, coord[0].xyz);
|
||||
std::copy(src->uvw, src->uvw + 3, coord[0].uvw);
|
||||
std::copy(src->xyz, src->xyz + 3, last_xyz_current);
|
||||
std::copy(src->xyz, src->xyz + 3, last_xyz);
|
||||
std::copy(src->uvw, src->uvw + 3, last_uvw);
|
||||
if (run_CE) {
|
||||
E = src->E;
|
||||
g = 0;
|
||||
} else {
|
||||
g = static_cast<int>(src->E);
|
||||
last_g = static_cast<int>(src->E);
|
||||
E = energy_bin_avg[g - 1];
|
||||
}
|
||||
last_E = E;
|
||||
}
|
||||
|
||||
void
|
||||
Particle::mark_as_lost(const char* message)
|
||||
{
|
||||
// Print warning and write lost particle file
|
||||
warning(message);
|
||||
write_restart();
|
||||
|
||||
// Increment number of lost particles
|
||||
alive = false;
|
||||
#pragma omp atomic
|
||||
openmc_n_lost_particles += 1;
|
||||
|
||||
// Count the total number of simulated particles (on this processor)
|
||||
auto n = openmc_current_batch * gen_per_batch * openmc_work;
|
||||
|
||||
// Abort the simulation if the maximum number of lost particles has been
|
||||
// reached
|
||||
if (openmc_n_lost_particles >= MAX_LOST_PARTICLES &&
|
||||
openmc_n_lost_particles >= REL_MAX_LOST_PARTICLES*n) {
|
||||
fatal_error("Maximum number of lost particles has been reached.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Particle::write_restart()
|
||||
{
|
||||
// Dont write another restart file if in particle restart mode
|
||||
if (openmc_run_mode == RUN_MODE_PARTICLE) return;
|
||||
|
||||
// Set up file name
|
||||
std::stringstream filename;
|
||||
filename << path_output << "particle_" << openmc_current_batch << '_' << id << ".h5";
|
||||
|
||||
#pragma omp critical (WriteParticleRestart)
|
||||
{
|
||||
// Create file
|
||||
hid_t file_id = file_open(filename.str(), 'w');
|
||||
|
||||
// Write filetype and version info
|
||||
write_attribute(file_id, "filetype", "particle restart");
|
||||
write_attribute(file_id, "version", VERSION_PARTICLE_RESTART);
|
||||
write_attribute(file_id, "openmc_version", VERSION);
|
||||
#ifdef GIT_SHA1
|
||||
write_attr_string(file_id, "git_sha1", GIT_SHA1);
|
||||
#endif
|
||||
|
||||
// Write data to file
|
||||
write_dataset(file_id, "current_batch", openmc_current_batch);
|
||||
write_dataset(file_id, "generations_per_batch", gen_per_batch);
|
||||
write_dataset(file_id, "current_generation", openmc_current_gen);
|
||||
write_dataset(file_id, "n_particles", n_particles);
|
||||
switch (openmc_run_mode) {
|
||||
case RUN_MODE_FIXEDSOURCE:
|
||||
write_dataset(file_id, "run_mode", "fixed source");
|
||||
break;
|
||||
case RUN_MODE_EIGENVALUE:
|
||||
write_dataset(file_id, "run_mode", "eigenvalue");
|
||||
break;
|
||||
case RUN_MODE_PARTICLE:
|
||||
write_dataset(file_id, "run_mode", "particle restart");
|
||||
break;
|
||||
}
|
||||
write_dataset(file_id, "id", id);
|
||||
write_dataset(file_id, "type", type);
|
||||
|
||||
// Get pointer to source bank
|
||||
Bank* src;
|
||||
int64_t n;
|
||||
openmc_source_bank(&src, &n);
|
||||
|
||||
int64_t i = openmc_current_work;
|
||||
write_dataset(file_id, "weight", src[i-1].wgt);
|
||||
write_dataset(file_id, "energy", src[i-1].E);
|
||||
hsize_t dims[] {3};
|
||||
write_double(file_id, 1, dims, "xyz", src[i-1].xyz, false);
|
||||
write_double(file_id, 1, dims, "uvw", src[i-1].uvw, false);
|
||||
|
||||
// Close file
|
||||
file_close(file_id);
|
||||
} // #pragma omp critical
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
void reset_coord(LocalCoord* c) { c->reset(); }
|
||||
void particle_clear(Particle* p) { p->clear(); }
|
||||
void particle_create_secondary(Particle* p, const double* uvw, double E,
|
||||
int type, bool run_CE)
|
||||
{
|
||||
p->create_secondary(uvw, E, type, run_CE);
|
||||
}
|
||||
void particle_initialize(Particle* p) { p->initialize(); }
|
||||
void particle_from_source(Particle* p, const Bank* src, bool run_CE,
|
||||
const double* energy_bin_avg)
|
||||
{
|
||||
p->from_source(src, run_CE, energy_bin_avg);
|
||||
}
|
||||
void particle_mark_as_lost(Particle* p, const char* message)
|
||||
{
|
||||
p->mark_as_lost(message);
|
||||
}
|
||||
void particle_write_restart(Particle* p) { p->write_restart(); }
|
||||
|
||||
} // namespace openmc
|
||||
163
src/particle.h
Normal file
163
src/particle.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
#ifndef OPENMC_PARTICLE_H
|
||||
#define OPENMC_PARTICLE_H
|
||||
|
||||
//! \file particle.h
|
||||
//! \brief Particle type
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "openmc.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
constexpr int MAX_DELAYED_GROUPS {8};
|
||||
constexpr int MAX_SECONDARY {1000};
|
||||
constexpr int NEUTRON {1};
|
||||
constexpr int MAX_LOST_PARTICLES {10};
|
||||
constexpr double REL_MAX_LOST_PARTICLES {1.0e-6};
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct LocalCoord {
|
||||
int cell {-1};
|
||||
int universe {-1};
|
||||
int lattice {-1};
|
||||
int lattice_x {-1};
|
||||
int lattice_y {-1};
|
||||
int lattice_z {-1};
|
||||
double xyz[3]; //!< particle position
|
||||
double uvw[3]; //!< particle direction
|
||||
bool rotated {false}; //!< Is the level rotated?
|
||||
|
||||
//! clear data from a single coordinate level
|
||||
void reset();
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
//! State of a particle being transported through geometry
|
||||
//============================================================================
|
||||
|
||||
struct Particle {
|
||||
int64_t id; //!< Unique ID
|
||||
int type; //!< Particle type (n, p, e, etc.)
|
||||
|
||||
int n_coord; //!< number of current coordinate levels
|
||||
int cell_instance; //!< offset for distributed properties
|
||||
LocalCoord coord[MAX_COORD]; //!< coordinates for all levels
|
||||
|
||||
// Particle coordinates before crossing a surface
|
||||
int last_n_coord; //!< number of current coordinates
|
||||
int last_cell[MAX_COORD]; //!< coordinates for all levels
|
||||
|
||||
// Energy data
|
||||
double E; //!< post-collision energy in eV
|
||||
double last_E; //!< pre-collision energy in eV
|
||||
int g; //!< post-collision energy group (MG only)
|
||||
int last_g; //!< pre-collision energy group (MG only)
|
||||
|
||||
// Other physical data
|
||||
double wgt; //!< particle weight
|
||||
double mu; //!< angle of scatter
|
||||
bool alive; //!< is particle alive?
|
||||
|
||||
// Other physical data
|
||||
double last_xyz_current[3]; //!< coordinates of the last collision or
|
||||
//!< reflective/periodic surface crossing for
|
||||
//!< current tallies
|
||||
double last_xyz[3]; //!< previous coordinates
|
||||
double last_uvw[3]; //!< previous direction coordinates
|
||||
double last_wgt; //!< pre-collision particle weight
|
||||
double absorb_wgt; //!< weight absorbed for survival biasing
|
||||
|
||||
// What event took place
|
||||
bool fission; //!< did particle cause implicit fission
|
||||
int event; //!< scatter, absorption
|
||||
int event_nuclide; //!< index in nuclides array
|
||||
int event_MT; //!< reaction MT
|
||||
int delayed_group; //!< delayed group
|
||||
|
||||
// Post-collision physical data
|
||||
int n_bank; //!< number of fission sites banked
|
||||
double wgt_bank; //!< weight of fission sites banked
|
||||
int n_delayed_bank[MAX_DELAYED_GROUPS]; //!< number of delayed fission
|
||||
//!< sites banked
|
||||
|
||||
// Indices for various arrays
|
||||
int surface; //!< index for surface particle is on
|
||||
int cell_born; //!< index for cell particle was born in
|
||||
int material; //!< index for current material
|
||||
int last_material; //!< index for last material
|
||||
|
||||
// Temperature of current cell
|
||||
double sqrtkT; //!< sqrt(k_Boltzmann * temperature) in eV
|
||||
double last_sqrtkT; //!< last temperature
|
||||
|
||||
// Statistical data
|
||||
int n_collision; //!< number of collisions
|
||||
|
||||
// Track output
|
||||
bool write_track {false};
|
||||
|
||||
// Secondary particles created
|
||||
int64_t n_secondary {};
|
||||
Bank secondary_bank[MAX_SECONDARY];
|
||||
|
||||
//! resets all coordinate levels for the particle
|
||||
void clear();
|
||||
|
||||
//! create a secondary particle
|
||||
//
|
||||
//! stores the current phase space attributes of the particle in the
|
||||
//! secondary bank and increments the number of sites in the secondary bank.
|
||||
//! \param uvw Direction of the secondary particle
|
||||
//! \param E Energy of the secondary particle in [eV]
|
||||
//! \param type Particle type
|
||||
//! \param run_CE Whether continuous-energy data is being used
|
||||
void create_secondary(const double* uvw, double E, int type, bool run_CE);
|
||||
|
||||
//! sets default attributes for a particle
|
||||
void initialize();
|
||||
|
||||
//! initialize from a source site
|
||||
//
|
||||
//! initializes a particle from data stored in a source site. The source
|
||||
//! site may have been produced from an external source, from fission, or
|
||||
//! simply as a secondary particle.
|
||||
//! \param src Source site data
|
||||
//! \param run_CE Whether continuous-energy data is being used
|
||||
//! \param energy_bin_avg An array of energy group bin averages
|
||||
void from_source(const Bank* src, bool run_CE, const double* energy_bin_avg);
|
||||
|
||||
//! mark a particle as lost and create a particle restart file
|
||||
//! \param message A warning message to display
|
||||
void mark_as_lost(const char* message);
|
||||
|
||||
//! create a particle restart HDF5 file
|
||||
void write_restart();
|
||||
};
|
||||
|
||||
|
||||
//============================================================================
|
||||
// Fortran compatibility functions
|
||||
//============================================================================
|
||||
|
||||
void reset_coord(LocalCoord* c);
|
||||
void particle_clear(Particle* p);
|
||||
void particle_create_secondary(Particle* p, const double* uvw, double E,
|
||||
int type, bool run_CE);
|
||||
void particle_initialize(Particle* p);
|
||||
void particle_from_source(Particle* p, const Bank* src, bool run_CE,
|
||||
const double* energy_bin_avg);
|
||||
void particle_mark_as_lost(Particle* p, const char* message);
|
||||
void particle_write_restart(Particle* p);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_PARTICLE_H
|
||||
|
|
@ -1,42 +1,34 @@
|
|||
module particle_header
|
||||
|
||||
use bank_header, only: Bank, source_bank
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use bank_header, only: Bank
|
||||
use constants
|
||||
use error, only: fatal_error, warning
|
||||
use geometry_header, only: root_universe
|
||||
use hdf5_interface
|
||||
use settings
|
||||
use simulation_header
|
||||
use string, only: to_str
|
||||
use string, only: to_c_string
|
||||
|
||||
implicit none
|
||||
|
||||
private
|
||||
|
||||
!===============================================================================
|
||||
! LOCALCOORD describes the location of a particle local to a single
|
||||
! universe. When the geometry consists of nested universes, a particle will have
|
||||
! a list of coordinates in each level
|
||||
!===============================================================================
|
||||
|
||||
type, public :: LocalCoord
|
||||
|
||||
type, bind(C) :: LocalCoord
|
||||
! Indices in various arrays for this level
|
||||
integer :: cell = NONE
|
||||
integer :: universe = NONE
|
||||
integer :: lattice = NONE
|
||||
integer :: lattice_x = NONE
|
||||
integer :: lattice_y = NONE
|
||||
integer :: lattice_z = NONE
|
||||
integer(C_INT) :: cell = NONE
|
||||
integer(C_INT) :: universe = NONE
|
||||
integer(C_INT) :: lattice = NONE
|
||||
integer(C_INT) :: lattice_x = NONE
|
||||
integer(C_INT) :: lattice_y = NONE
|
||||
integer(C_INT) :: lattice_z = NONE
|
||||
|
||||
! Particle position and direction for this level
|
||||
real(8) :: xyz(3)
|
||||
real(8) :: uvw(3)
|
||||
real(C_DOUBLE) :: xyz(3)
|
||||
real(C_DOUBLE) :: uvw(3)
|
||||
|
||||
! Is this level rotated?
|
||||
logical :: rotated = .false.
|
||||
contains
|
||||
procedure :: reset => reset_coord
|
||||
logical(C_BOOL) :: rotated = .false.
|
||||
end type LocalCoord
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -44,307 +36,126 @@ module particle_header
|
|||
! geometry
|
||||
!===============================================================================
|
||||
|
||||
type, public :: Particle
|
||||
type, bind(C) :: Particle
|
||||
! Basic data
|
||||
integer(8) :: id ! Unique ID
|
||||
integer :: type ! Particle type (n, p, e, etc)
|
||||
integer(C_INT64_T) :: id ! Unique ID
|
||||
integer(C_INT) :: type ! Particle type (n, p, e, etc)
|
||||
|
||||
! Particle coordinates
|
||||
integer :: n_coord ! number of current coordinates
|
||||
integer :: cell_instance ! offset for distributed properties
|
||||
integer(C_INT) :: n_coord ! number of current coordinates
|
||||
integer(C_INT) :: cell_instance ! offset for distributed properties
|
||||
type(LocalCoord) :: coord(MAX_COORD) ! coordinates for all levels
|
||||
|
||||
! Particle coordinates before crossing a surface
|
||||
integer :: last_n_coord ! number of current coordinates
|
||||
integer :: last_cell(MAX_COORD) ! coordinates for all levels
|
||||
integer(C_INT) :: last_n_coord ! number of current coordinates
|
||||
integer(C_INT) :: last_cell(MAX_COORD) ! coordinates for all levels
|
||||
|
||||
! Energy Data
|
||||
real(8) :: E ! post-collision energy
|
||||
real(8) :: last_E ! pre-collision energy
|
||||
integer :: g ! post-collision energy group (MG only)
|
||||
integer :: last_g ! pre-collision energy group (MG only)
|
||||
real(C_DOUBLE) :: E ! post-collision energy
|
||||
real(C_DOUBLE) :: last_E ! pre-collision energy
|
||||
integer(C_INT) :: g ! post-collision energy group (MG only)
|
||||
integer(C_INT) :: last_g ! pre-collision energy group (MG only)
|
||||
|
||||
! Other physical data
|
||||
real(8) :: wgt ! particle weight
|
||||
real(8) :: mu ! angle of scatter
|
||||
logical :: alive ! is particle alive?
|
||||
real(C_DOUBLE) :: wgt ! particle weight
|
||||
real(C_DOUBLE) :: mu ! angle of scatter
|
||||
logical(C_BOOL) :: alive ! is particle alive?
|
||||
|
||||
! Pre-collision physical data
|
||||
real(8) :: last_xyz_current(3) ! coordinates of the last collision or
|
||||
! reflective/periodic surface crossing
|
||||
! for current tallies
|
||||
real(8) :: last_xyz(3) ! previous coordinates
|
||||
real(8) :: last_uvw(3) ! previous direction coordinates
|
||||
real(8) :: last_wgt ! pre-collision particle weight
|
||||
real(8) :: absorb_wgt ! weight absorbed for survival biasing
|
||||
real(C_DOUBLE) :: last_xyz_current(3) ! coordinates of the last collision or
|
||||
! reflective/periodic surface crossing
|
||||
! for current tallies
|
||||
real(C_DOUBLE) :: last_xyz(3) ! previous coordinates
|
||||
real(C_DOUBLE) :: last_uvw(3) ! previous direction coordinates
|
||||
real(C_DOUBLE) :: last_wgt ! pre-collision particle weight
|
||||
real(C_DOUBLE) :: absorb_wgt ! weight absorbed for survival biasing
|
||||
|
||||
! What event last took place
|
||||
logical :: fission ! did the particle cause implicit fission
|
||||
integer :: event ! scatter, absorption
|
||||
integer :: event_nuclide ! index in nuclides array
|
||||
integer :: event_MT ! reaction MT
|
||||
integer :: delayed_group ! delayed group
|
||||
logical(C_BOOL) :: fission ! did the particle cause implicit fission
|
||||
integer(C_INT) :: event ! scatter, absorption
|
||||
integer(C_INT) :: event_nuclide ! index in nuclides array
|
||||
integer(C_INT) :: event_MT ! reaction MT
|
||||
integer(C_INT) :: delayed_group ! delayed group
|
||||
|
||||
! Post-collision physical data
|
||||
integer :: n_bank ! number of fission sites banked
|
||||
real(8) :: wgt_bank ! weight of fission sites banked
|
||||
integer :: n_delayed_bank(MAX_DELAYED_GROUPS) ! number of delayed fission
|
||||
integer(C_INT) :: n_bank ! number of fission sites banked
|
||||
real(C_DOUBLE) :: wgt_bank ! weight of fission sites banked
|
||||
integer(C_INT) :: n_delayed_bank(MAX_DELAYED_GROUPS) ! number of delayed fission
|
||||
! sites banked
|
||||
|
||||
! Indices for various arrays
|
||||
integer :: surface ! index for surface particle is on
|
||||
integer :: cell_born ! index for cell particle was born in
|
||||
integer :: material ! index for current material
|
||||
integer :: last_material ! index for last material
|
||||
integer(C_INT) :: surface ! index for surface particle is on
|
||||
integer(C_INT) :: cell_born ! index for cell particle was born in
|
||||
integer(C_INT) :: material ! index for current material
|
||||
integer(C_INT) :: last_material ! index for last material
|
||||
|
||||
! Temperature of the current cell
|
||||
real(8) :: sqrtkT ! sqrt(k_Boltzmann * temperature) in eV
|
||||
real(8) :: last_sqrtKT ! last temperature
|
||||
real(C_DOUBLE) :: sqrtkT ! sqrt(k_Boltzmann * temperature) in eV
|
||||
real(C_DOUBLE) :: last_sqrtKT ! last temperature
|
||||
|
||||
! Statistical data
|
||||
integer :: n_collision ! # of collisions
|
||||
integer(C_INT) :: n_collision ! # of collisions
|
||||
|
||||
! Track output
|
||||
logical :: write_track = .false.
|
||||
logical(C_BOOL) :: write_track = .false.
|
||||
|
||||
! Secondary particles created
|
||||
integer(8) :: n_secondary = 0
|
||||
type(Bank) :: secondary_bank(MAX_SECONDARY)
|
||||
|
||||
contains
|
||||
procedure :: clear
|
||||
procedure :: create_secondary
|
||||
procedure :: initialize
|
||||
procedure :: initialize_from_source
|
||||
procedure :: mark_as_lost
|
||||
procedure :: write_restart
|
||||
integer(C_INT64_T) :: n_secondary = 0
|
||||
type(Bank) :: secondary_bank(MAX_SECONDARY)
|
||||
end type Particle
|
||||
|
||||
interface
|
||||
subroutine reset_coord(c) bind(C)
|
||||
import LocalCoord
|
||||
type(LocalCoord), intent(inout) :: c
|
||||
end subroutine reset_coord
|
||||
|
||||
subroutine particle_clear(p) bind(C)
|
||||
import Particle
|
||||
type(Particle), intent(inout) :: p
|
||||
end subroutine particle_clear
|
||||
|
||||
subroutine particle_create_secondary(p, uvw, E, type, run_CE) bind(C)
|
||||
import Particle, C_DOUBLE, C_INT, C_BOOL
|
||||
type(Particle), intent(inout) :: p
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
real(C_DOUBLE), value :: E
|
||||
integer(C_INT), value :: type
|
||||
logical(C_BOOL), value :: run_CE
|
||||
end subroutine particle_create_secondary
|
||||
|
||||
subroutine particle_initialize(p) bind(C)
|
||||
import Particle
|
||||
type(Particle), intent(inout) :: p
|
||||
end subroutine particle_initialize
|
||||
|
||||
subroutine particle_from_source(p, src, run_CE, energy_bin_avg) bind(C)
|
||||
import Particle, Bank, C_BOOL, C_DOUBLE
|
||||
type(Particle), intent(inout) :: p
|
||||
type(Bank), intent(in) :: src
|
||||
logical(C_BOOL), value :: run_CE
|
||||
real(C_DOUBLE), intent(in) :: energy_bin_avg(*)
|
||||
end subroutine particle_from_source
|
||||
|
||||
subroutine particle_mark_as_lost_c(p, message) bind(C, name='particle_mark_as_lost')
|
||||
import Particle, C_CHAR
|
||||
type(Particle), intent(in) :: p
|
||||
character(kind=C_CHAR), intent(in) :: message(*)
|
||||
end subroutine particle_mark_as_lost_c
|
||||
|
||||
subroutine particle_write_restart(p) bind(C)
|
||||
import Particle
|
||||
type(Particle), intent(in) :: p
|
||||
end subroutine particle_write_restart
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! RESET_COORD clears data from a single coordinate level
|
||||
!===============================================================================
|
||||
subroutine particle_mark_as_lost(this, message)
|
||||
type(Particle), intent(inout) :: this
|
||||
character(*) :: message
|
||||
|
||||
elemental subroutine reset_coord(this)
|
||||
class(LocalCoord), intent(inout) :: this
|
||||
|
||||
this % cell = NONE
|
||||
this % universe = NONE
|
||||
this % lattice = NONE
|
||||
this % lattice_x = NONE
|
||||
this % lattice_y = NONE
|
||||
this % lattice_z = NONE
|
||||
this % rotated = .false.
|
||||
|
||||
end subroutine reset_coord
|
||||
|
||||
!===============================================================================
|
||||
! CLEAR_PARTICLE resets all coordinate levels for the particle
|
||||
!===============================================================================
|
||||
|
||||
subroutine clear(this)
|
||||
class(Particle) :: this
|
||||
|
||||
integer :: i
|
||||
|
||||
! remove any coordinate levels
|
||||
do i = 1, MAX_COORD
|
||||
call this % coord(i) % reset()
|
||||
end do
|
||||
end subroutine clear
|
||||
|
||||
!===============================================================================
|
||||
! CREATE_SECONDARY stores the current phase space attributes of the particle in
|
||||
! the secondary bank and increments the number of sites in the secondary bank.
|
||||
!===============================================================================
|
||||
|
||||
subroutine create_secondary(this, uvw, type, run_CE)
|
||||
class(Particle), intent(inout) :: this
|
||||
real(8), intent(in) :: uvw(3)
|
||||
integer, intent(in) :: type
|
||||
logical, intent(in) :: run_CE
|
||||
|
||||
integer(8) :: n
|
||||
|
||||
! Check to make sure that the hard-limit on secondary particles is not
|
||||
! exceeded.
|
||||
if (this % n_secondary == MAX_SECONDARY) then
|
||||
call fatal_error("Too many secondary particles created.")
|
||||
end if
|
||||
|
||||
n = this % n_secondary + 1
|
||||
this % secondary_bank(n) % wgt = this % wgt
|
||||
this % secondary_bank(n) % xyz(:) = this % coord(1) % xyz
|
||||
this % secondary_bank(n) % uvw(:) = uvw
|
||||
this % n_secondary = n
|
||||
this % secondary_bank(this % n_secondary) % E = this % E
|
||||
if (.not. run_CE) then
|
||||
this % secondary_bank(this % n_secondary) % E = real(this % g, 8)
|
||||
end if
|
||||
|
||||
end subroutine create_secondary
|
||||
|
||||
!===============================================================================
|
||||
! INITIALIZE sets default attributes for a particle from the source bank
|
||||
!===============================================================================
|
||||
|
||||
subroutine initialize(this)
|
||||
|
||||
class(Particle) :: this
|
||||
|
||||
! Clear coordinate lists
|
||||
call this % clear()
|
||||
|
||||
! Set particle to neutron that's alive
|
||||
this % type = NEUTRON
|
||||
this % alive = .true.
|
||||
|
||||
! clear attributes
|
||||
this % surface = NONE
|
||||
this % cell_born = NONE
|
||||
this % material = NONE
|
||||
this % last_material = NONE
|
||||
this % last_sqrtkT = NONE
|
||||
this % wgt = ONE
|
||||
this % last_wgt = ONE
|
||||
this % absorb_wgt = ZERO
|
||||
this % n_bank = 0
|
||||
this % wgt_bank = ZERO
|
||||
this % sqrtkT = ERROR_REAL
|
||||
this % n_collision = 0
|
||||
this % fission = .false.
|
||||
this % delayed_group = 0
|
||||
this % n_delayed_bank(:) = 0
|
||||
this % g = NONE
|
||||
|
||||
! Set up base level coordinates
|
||||
this % coord(1) % universe = root_universe
|
||||
this % n_coord = 1
|
||||
this % last_n_coord = 1
|
||||
|
||||
end subroutine initialize
|
||||
|
||||
!===============================================================================
|
||||
! INITIALIZE_FROM_SOURCE initializes a particle from data stored in a source
|
||||
! site. The source site may have been produced from an external source, from
|
||||
! fission, or simply as a secondary particle.
|
||||
!===============================================================================
|
||||
|
||||
subroutine initialize_from_source(this, src, run_CE, energy_bin_avg)
|
||||
class(Particle), intent(inout) :: this
|
||||
type(Bank), intent(in) :: src
|
||||
logical, intent(in) :: run_CE
|
||||
real(8), allocatable, intent(in) :: energy_bin_avg(:)
|
||||
|
||||
! set defaults
|
||||
call this % initialize()
|
||||
|
||||
! copy attributes from source bank site
|
||||
this % wgt = src % wgt
|
||||
this % last_wgt = src % wgt
|
||||
this % coord(1) % xyz = src % xyz
|
||||
this % coord(1) % uvw = src % uvw
|
||||
this % last_xyz_current = src % xyz
|
||||
this % last_xyz = src % xyz
|
||||
this % last_uvw = src % uvw
|
||||
if (run_CE) then
|
||||
this % E = src % E
|
||||
this % g = NONE
|
||||
else
|
||||
this % g = int(src % E)
|
||||
this % last_g = int(src % E)
|
||||
this % E = energy_bin_avg(this % g)
|
||||
end if
|
||||
this % last_E = this % E
|
||||
|
||||
end subroutine initialize_from_source
|
||||
|
||||
!===============================================================================
|
||||
! MARK_AS_LOST
|
||||
!===============================================================================
|
||||
|
||||
subroutine mark_as_lost(this, message)
|
||||
class(Particle), intent(inout) :: this
|
||||
character(*) :: message
|
||||
|
||||
integer(8) :: tot_n_particles
|
||||
|
||||
! Print warning and write lost particle file
|
||||
call warning(message)
|
||||
call this % write_restart()
|
||||
|
||||
! Increment number of lost particles
|
||||
this % alive = .false.
|
||||
!$omp atomic
|
||||
n_lost_particles = n_lost_particles + 1
|
||||
|
||||
! Count the total number of simulated particles (on this processor)
|
||||
tot_n_particles = current_batch * gen_per_batch * work
|
||||
|
||||
! Abort the simulation if the maximum number of lost particles has been
|
||||
! reached
|
||||
if (n_lost_particles >= MAX_LOST_PARTICLES .and. &
|
||||
n_lost_particles >= REL_MAX_LOST_PARTICLES * tot_n_particles) then
|
||||
call fatal_error("Maximum number of lost particles has been reached.")
|
||||
end if
|
||||
|
||||
end subroutine mark_as_lost
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_RESTART creates a particle restart file
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_restart(this)
|
||||
class(Particle), intent(in) :: this
|
||||
|
||||
integer(HID_T) :: file_id
|
||||
character(MAX_FILE_LEN) :: filename
|
||||
|
||||
! Dont write another restart file if in particle restart mode
|
||||
if (run_mode == MODE_PARTICLE) return
|
||||
|
||||
! Set up file name
|
||||
filename = trim(path_output) // 'particle_' // trim(to_str(current_batch)) &
|
||||
// '_' // trim(to_str(this % id)) // '.h5'
|
||||
|
||||
!$omp critical (WriteParticleRestart)
|
||||
! Create file
|
||||
file_id = file_open(filename, 'w')
|
||||
|
||||
associate (src => source_bank(current_work))
|
||||
! Write filetype and version info
|
||||
call write_attribute(file_id, 'filetype', 'particle restart')
|
||||
call write_attribute(file_id, 'version', VERSION_PARTICLE_RESTART)
|
||||
call write_attribute(file_id, "openmc_version", VERSION)
|
||||
#ifdef GIT_SHA1
|
||||
call write_attribute(file_id, "git_sha1", GIT_SHA1)
|
||||
#endif
|
||||
|
||||
! Write data to file
|
||||
call write_dataset(file_id, 'current_batch', current_batch)
|
||||
call write_dataset(file_id, 'generations_per_batch', gen_per_batch)
|
||||
call write_dataset(file_id, 'current_generation', current_gen)
|
||||
call write_dataset(file_id, 'n_particles', n_particles)
|
||||
select case(run_mode)
|
||||
case (MODE_FIXEDSOURCE)
|
||||
call write_dataset(file_id, 'run_mode', 'fixed source')
|
||||
case (MODE_EIGENVALUE)
|
||||
call write_dataset(file_id, 'run_mode', 'eigenvalue')
|
||||
case (MODE_PARTICLE)
|
||||
call write_dataset(file_id, 'run_mode', 'particle restart')
|
||||
end select
|
||||
call write_dataset(file_id, 'id', this % id)
|
||||
call write_dataset(file_id, 'weight', src % wgt)
|
||||
call write_dataset(file_id, 'energy', src % E)
|
||||
call write_dataset(file_id, 'xyz', src % xyz)
|
||||
call write_dataset(file_id, 'uvw', src % uvw)
|
||||
end associate
|
||||
|
||||
! Close file
|
||||
call file_close(file_id)
|
||||
!$omp end critical (WriteParticleRestart)
|
||||
|
||||
end subroutine write_restart
|
||||
call particle_mark_as_lost_c(this, to_c_string(message))
|
||||
end subroutine particle_mark_as_lost
|
||||
|
||||
end module particle_header
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module particle_restart
|
|||
use mgxs_interface, only: energy_bin_avg
|
||||
use nuclide_header, only: micro_xs, n_nuclides
|
||||
use output, only: print_particle
|
||||
use particle_header, only: Particle
|
||||
use particle_header
|
||||
use random_lcg, only: set_particle_seed
|
||||
use settings
|
||||
use simulation_header
|
||||
|
|
@ -41,7 +41,7 @@ contains
|
|||
allocate(micro_xs(n_nuclides))
|
||||
|
||||
! Initialize the particle to be tracked
|
||||
call p % initialize()
|
||||
call particle_initialize(p)
|
||||
|
||||
! Read in the restart information
|
||||
call read_particle_restart(p, previous_run_mode)
|
||||
|
|
@ -100,6 +100,7 @@ contains
|
|||
previous_run_mode = MODE_FIXEDSOURCE
|
||||
end select
|
||||
call read_dataset(p % id, file_id, 'id')
|
||||
call read_dataset(p % type, file_id, 'type')
|
||||
call read_dataset(p % wgt, file_id, 'weight')
|
||||
call read_dataset(p % E, file_id, 'energy')
|
||||
call read_dataset(p % coord(1) % xyz, file_id, 'xyz')
|
||||
|
|
|
|||
506
src/photon_header.F90
Normal file
506
src/photon_header.F90
Normal file
|
|
@ -0,0 +1,506 @@
|
|||
module photon_header
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants
|
||||
use dict_header, only: DictIntInt, DictCharInt
|
||||
use endf_header, only: Tabulated1D
|
||||
use hdf5_interface
|
||||
use nuclide_header, only: nuclides
|
||||
use settings
|
||||
|
||||
real(8), allocatable :: compton_profile_pz(:)
|
||||
real(8), allocatable :: ttb_e_grid(:) ! energy T of incident electron
|
||||
real(8), allocatable :: ttb_k_grid(:) ! reduced energy W/T of emitted photon
|
||||
|
||||
type ElectronSubshell
|
||||
integer :: index_subshell ! index in SUBSHELLS
|
||||
integer :: threshold
|
||||
real(8) :: n_electrons
|
||||
real(8) :: binding_energy
|
||||
real(8), allocatable :: cross_section(:)
|
||||
|
||||
! Transition data
|
||||
integer :: n_transitions
|
||||
integer, allocatable :: transition_subshells(:,:)
|
||||
real(8), allocatable :: transition_energy(:)
|
||||
real(8), allocatable :: transition_probability(:)
|
||||
end type ElectronSubshell
|
||||
|
||||
type PhotonInteraction
|
||||
character(3) :: name ! atomic symbol, e.g. 'Zr'
|
||||
integer :: Z ! atomic number
|
||||
|
||||
! Microscopic cross sections
|
||||
real(8), allocatable :: energy(:)
|
||||
real(8), allocatable :: coherent(:)
|
||||
real(8), allocatable :: incoherent(:)
|
||||
real(8), allocatable :: photoelectric_total(:)
|
||||
real(8), allocatable :: pair_production_total(:)
|
||||
real(8), allocatable :: pair_production_electron(:)
|
||||
real(8), allocatable :: pair_production_nuclear(:)
|
||||
|
||||
! Form factors
|
||||
type(Tabulated1D) :: incoherent_form_factor
|
||||
type(Tabulated1D) :: coherent_int_form_factor
|
||||
type(Tabulated1D) :: coherent_anomalous_real
|
||||
type(Tabulated1D) :: coherent_anomalous_imag
|
||||
|
||||
! Photoionization and atomic relaxation data
|
||||
type(DictIntInt) :: shell_dict ! Given a shell designator, e.g. 3, this
|
||||
! dictionary gives an index in shells(:)
|
||||
type(ElectronSubshell), allocatable :: shells(:)
|
||||
|
||||
! Compton profile data
|
||||
real(8), allocatable :: profile_pdf(:,:)
|
||||
real(8), allocatable :: profile_cdf(:,:)
|
||||
real(8), allocatable :: binding_energy(:)
|
||||
real(8), allocatable :: electron_pdf(:)
|
||||
|
||||
! Stopping power data
|
||||
real(8) :: I ! mean excitation energy
|
||||
real(8), allocatable :: stopping_power_collision(:)
|
||||
real(8), allocatable :: stopping_power_radiative(:)
|
||||
|
||||
! Bremsstrahlung scaled DCS
|
||||
real(8), allocatable :: dcs(:,:)
|
||||
|
||||
contains
|
||||
procedure :: from_hdf5 => photon_from_hdf5
|
||||
procedure :: calculate_xs => photon_calculate_xs
|
||||
end type PhotonInteraction
|
||||
|
||||
type BremsstrahlungData
|
||||
real(8), allocatable :: pdf(:,:) ! Bremsstrahlung energy PDF
|
||||
real(8), allocatable :: cdf(:,:) ! Bremsstrahlung energy CDF
|
||||
real(8), allocatable :: yield(:) ! Photon number yield
|
||||
end type BremsstrahlungData
|
||||
|
||||
type Bremsstrahlung
|
||||
type(BremsstrahlungData) :: electron
|
||||
type(BremsstrahlungData) :: positron
|
||||
end type Bremsstrahlung
|
||||
|
||||
type(PhotonInteraction), allocatable, target :: elements(:) ! Photon cross sections
|
||||
integer :: n_elements ! Number of photon cross section tables
|
||||
|
||||
type(DictCharInt) :: element_dict
|
||||
|
||||
type(Bremsstrahlung), allocatable, target :: ttb(:) ! Bremsstrahlung data
|
||||
|
||||
!===============================================================================
|
||||
! ELEMENTMICROXS contains cached microscopic photon cross sections for a
|
||||
! particular element at the current energy
|
||||
!===============================================================================
|
||||
|
||||
type ElementMicroXS
|
||||
integer :: index_grid ! index on element energy grid
|
||||
real(8) :: last_E = ZERO ! last evaluated energy
|
||||
real(8) :: interp_factor ! interpolation factor on energy grid
|
||||
real(8) :: total ! microscropic total photon xs
|
||||
real(8) :: coherent ! microscopic coherent xs
|
||||
real(8) :: incoherent ! microscopic incoherent xs
|
||||
real(8) :: photoelectric ! microscopic photoelectric xs
|
||||
real(8) :: pair_production ! microscopic pair production xs
|
||||
end type ElementMicroXS
|
||||
|
||||
type(ElementMicroXS), allocatable :: micro_photon_xs(:) ! Cache for each element
|
||||
!$omp threadprivate(micro_photon_xs)
|
||||
|
||||
contains
|
||||
|
||||
subroutine photon_from_hdf5(this, group_id)
|
||||
class(PhotonInteraction), intent(inout) :: this
|
||||
integer(HID_T), intent(in) :: group_id
|
||||
|
||||
integer :: i, j
|
||||
integer(HID_T) :: rgroup, tgroup
|
||||
integer(HID_T) :: dset_id
|
||||
integer(HSIZE_T) :: dims(1), dims2(2)
|
||||
integer :: n_energy
|
||||
integer :: n_shell
|
||||
integer :: n_profile
|
||||
integer :: n_transition
|
||||
integer :: n_k
|
||||
integer :: n_e
|
||||
character(3), allocatable :: designators(:)
|
||||
real(8) :: c
|
||||
real(8) :: f
|
||||
real(8) :: y
|
||||
real(8), allocatable :: electron_energy(:)
|
||||
real(8), allocatable :: matrix(:,:)
|
||||
real(8), allocatable :: dcs(:,:)
|
||||
|
||||
! Get name of nuclide from group
|
||||
this % name = get_name(group_id)
|
||||
|
||||
! Get rid of leading '/'
|
||||
this % name = trim(this % name(2:))
|
||||
|
||||
! Get atomic number
|
||||
call read_attribute(this % Z, group_id, 'Z')
|
||||
|
||||
! Determine number of energies and read energy grid
|
||||
dset_id = open_dataset(group_id, 'energy')
|
||||
call get_shape(dset_id, dims)
|
||||
n_energy = int(dims(1), 4)
|
||||
allocate(this % energy(dims(1)))
|
||||
call read_dataset(this % energy, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Allocate arrays
|
||||
allocate(this % coherent(n_energy))
|
||||
allocate(this % incoherent(n_energy))
|
||||
allocate(this % pair_production_total(n_energy))
|
||||
allocate(this % pair_production_nuclear(n_energy))
|
||||
allocate(this % pair_production_electron(n_energy))
|
||||
allocate(this % photoelectric_total(n_energy))
|
||||
|
||||
! Read coherent scattering
|
||||
rgroup = open_group(group_id, 'coherent')
|
||||
call read_dataset(this % coherent, rgroup, 'xs')
|
||||
|
||||
dset_id = open_dataset(rgroup, 'integrated_scattering_factor')
|
||||
call this % coherent_int_form_factor % from_hdf5(dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
dset_id = open_dataset(rgroup, 'anomalous_real')
|
||||
call this % coherent_anomalous_real % from_hdf5(dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
dset_id = open_dataset(rgroup, 'anomalous_imag')
|
||||
call this % coherent_anomalous_imag % from_hdf5(dset_id)
|
||||
call close_dataset(dset_id)
|
||||
call close_group(rgroup)
|
||||
|
||||
! Read incoherent scattering
|
||||
rgroup = open_group(group_id, 'incoherent')
|
||||
call read_dataset(this % incoherent, rgroup, 'xs')
|
||||
dset_id = open_dataset(rgroup, 'scattering_factor')
|
||||
call this % incoherent_form_factor % from_hdf5(dset_id)
|
||||
call close_dataset(dset_id)
|
||||
call close_group(rgroup)
|
||||
|
||||
! Read pair production
|
||||
rgroup = open_group(group_id, 'pair_production_electron')
|
||||
call read_dataset(this % pair_production_electron, rgroup, 'xs')
|
||||
call close_group(rgroup)
|
||||
|
||||
! Read pair production
|
||||
if (object_exists(group_id, 'pair_production_nuclear')) then
|
||||
rgroup = open_group(group_id, 'pair_production_nuclear')
|
||||
call read_dataset(this % pair_production_nuclear, rgroup, 'xs')
|
||||
call close_group(rgroup)
|
||||
else
|
||||
this % pair_production_nuclear(:) = ZERO
|
||||
end if
|
||||
|
||||
! Read photoelectric
|
||||
rgroup = open_group(group_id, 'photoelectric')
|
||||
call read_dataset(this % photoelectric_total, rgroup, 'xs')
|
||||
call close_group(rgroup)
|
||||
|
||||
! Read subshell photoionization cross section and atomic relaxation data
|
||||
rgroup = open_group(group_id, 'subshells')
|
||||
call read_attribute(designators, rgroup, 'designators')
|
||||
n_shell = size(designators)
|
||||
allocate(this % shells(n_shell))
|
||||
do i = 1, n_shell
|
||||
! Create mapping from designator to index
|
||||
do j = 1, size(SUBSHELLS)
|
||||
if (designators(i) == SUBSHELLS(j)) then
|
||||
call this % shell_dict % set(j, i)
|
||||
this % shells(i) % index_subshell = j
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
|
||||
! Read binding energy and number of electrons
|
||||
tgroup = open_group(rgroup, trim(designators(i)))
|
||||
call read_attribute(this % shells(i) % binding_energy, tgroup, &
|
||||
'binding_energy')
|
||||
call read_attribute(this % shells(i) % n_electrons, tgroup, &
|
||||
'num_electrons')
|
||||
|
||||
! Read subshell cross section
|
||||
dset_id = open_dataset(tgroup, 'xs')
|
||||
call read_attribute(j, dset_id, 'threshold_idx')
|
||||
this % shells(i) % threshold = j
|
||||
allocate(this % shells(i) % cross_section(n_energy - j))
|
||||
call read_dataset(this % shells(i) % cross_section, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
where (this % shells(i) % cross_section > ZERO)
|
||||
this % shells(i) % cross_section = log(this % shells(i) % cross_section)
|
||||
elsewhere
|
||||
this % shells(i) % cross_section = -500.0_8
|
||||
end where
|
||||
|
||||
if (object_exists(tgroup, 'transitions')) then
|
||||
dset_id = open_dataset(tgroup, 'transitions')
|
||||
call get_shape(dset_id, dims2)
|
||||
n_transition = int(dims2(2), 4)
|
||||
this % shells(i) % n_transitions = n_transition
|
||||
if (n_transition > 0) then
|
||||
allocate(this % shells(i) % transition_subshells(2, n_transition))
|
||||
allocate(this % shells(i) % transition_energy(n_transition))
|
||||
allocate(this % shells(i) % transition_probability(n_transition))
|
||||
|
||||
allocate(matrix(dims2(1), dims2(2)))
|
||||
call read_dataset(matrix, dset_id)
|
||||
|
||||
this % shells(i) % transition_subshells(:,:) = int(matrix(1:2, :), 4)
|
||||
this % shells(i) % transition_energy(:) = matrix(3, :)
|
||||
this % shells(i) % transition_probability(:) = matrix(4, :) &
|
||||
/ sum(matrix(4, :))
|
||||
deallocate(matrix)
|
||||
end if
|
||||
call close_dataset(dset_id)
|
||||
else
|
||||
this % shells(i) % n_transitions = 0
|
||||
end if
|
||||
call close_group(tgroup)
|
||||
end do
|
||||
call close_group(rgroup)
|
||||
deallocate(designators)
|
||||
|
||||
! Determine number of electron shells
|
||||
rgroup = open_group(group_id, 'compton_profiles')
|
||||
|
||||
! Determine number of shells
|
||||
dset_id = open_dataset(rgroup, 'num_electrons')
|
||||
call get_shape(dset_id, dims)
|
||||
n_shell = int(dims(1), 4)
|
||||
|
||||
! Read electron shell PDF and binding energies
|
||||
allocate(this % electron_pdf(n_shell), this % binding_energy(n_shell))
|
||||
call read_dataset(this % electron_pdf, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
call read_dataset(this % binding_energy, rgroup, 'binding_energy')
|
||||
this % electron_pdf(:) = this % electron_pdf / sum(this % electron_pdf)
|
||||
|
||||
! Read Compton profiles
|
||||
dset_id = open_dataset(rgroup, 'J')
|
||||
call get_shape(dset_id, dims2)
|
||||
n_profile = int(dims2(1), 4)
|
||||
allocate(this % profile_pdf(n_profile, n_shell))
|
||||
call read_dataset(this % profile_pdf, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Get Compton profile momentum grid
|
||||
if (.not. allocated(compton_profile_pz)) then
|
||||
allocate(compton_profile_pz(n_profile))
|
||||
call read_dataset(compton_profile_pz, rgroup, 'pz')
|
||||
end if
|
||||
call close_group(rgroup)
|
||||
|
||||
! Create Compton profile CDF
|
||||
allocate(this % profile_cdf(n_profile, n_shell))
|
||||
do i = 1, n_shell
|
||||
c = ZERO
|
||||
this % profile_cdf(1,i) = ZERO
|
||||
do j = 1, n_profile - 1
|
||||
c = c + HALF*(compton_profile_pz(j+1) - compton_profile_pz(j)) * &
|
||||
(this%profile_pdf(j,i) + this%profile_pdf(j+1,i))
|
||||
this % profile_cdf(j+1,i) = c
|
||||
end do
|
||||
end do
|
||||
|
||||
! Calculate total pair production
|
||||
this % pair_production_total(:) = this % pair_production_nuclear + &
|
||||
this % pair_production_electron
|
||||
|
||||
if (electron_treatment == ELECTRON_TTB) then
|
||||
! Read bremsstrahlung scaled DCS
|
||||
rgroup = open_group(group_id, 'bremsstrahlung')
|
||||
dset_id = open_dataset(rgroup, 'dcs')
|
||||
call get_shape(dset_id, dims2)
|
||||
n_k = int(dims2(1), 4)
|
||||
n_e = int(dims2(2), 4)
|
||||
allocate(this % dcs(n_k, n_e))
|
||||
call read_dataset(this % dcs, dset_id)
|
||||
call close_dataset(dset_id)
|
||||
|
||||
! Get energy grids used for bremsstrahlung DCS and for stopping powers
|
||||
allocate(electron_energy(n_e))
|
||||
call read_dataset(electron_energy, rgroup, 'electron_energy')
|
||||
if (.not. allocated(ttb_k_grid)) then
|
||||
allocate(ttb_k_grid(n_k))
|
||||
call read_dataset(ttb_k_grid, rgroup, 'photon_energy')
|
||||
end if
|
||||
call close_group(rgroup)
|
||||
|
||||
! Read stopping power data
|
||||
if (this % Z < 99) then
|
||||
rgroup = open_group(group_id, 'stopping_powers')
|
||||
allocate(this % stopping_power_collision(n_e))
|
||||
allocate(this % stopping_power_radiative(n_e))
|
||||
call read_dataset(this % stopping_power_collision, rgroup, 's_collision')
|
||||
call read_dataset(this % stopping_power_radiative, rgroup, 's_radiative')
|
||||
call read_attribute(this % I, rgroup, 'I')
|
||||
call close_group(rgroup)
|
||||
end if
|
||||
|
||||
! Truncate the bremsstrahlung data at the cutoff energy
|
||||
if (energy_cutoff(PHOTON) > electron_energy(1)) then
|
||||
i_grid = binary_search(electron_energy, n_e, energy_cutoff(PHOTON))
|
||||
|
||||
! calculate interpolation factor
|
||||
f = (log(energy_cutoff(PHOTON)) - log(electron_energy(i_grid))) / &
|
||||
(log(electron_energy(i_grid+1)) - log(electron_energy(i_grid)))
|
||||
|
||||
! Interpolate collision stopping power at the cutoff energy and
|
||||
! truncate
|
||||
y = exp(log(this % stopping_power_collision(i_grid)) + &
|
||||
f*(log(this % stopping_power_collision(i_grid+1)) - &
|
||||
log(this % stopping_power_collision(i_grid))))
|
||||
this % stopping_power_collision = &
|
||||
[y, this % stopping_power_collision(i_grid+1:n_e)]
|
||||
|
||||
! Interpolate radiative stopping power at the cutoff energy and
|
||||
! truncate
|
||||
y = exp(log(this % stopping_power_radiative(i_grid)) + &
|
||||
f*(log(this % stopping_power_radiative(i_grid+1)) - &
|
||||
log(this % stopping_power_radiative(i_grid))))
|
||||
this % stopping_power_radiative = &
|
||||
[y, this % stopping_power_radiative(i_grid+1:n_e)]
|
||||
|
||||
! Interpolate bremsstrahlung DCS at the cutoff energy and truncate
|
||||
allocate(dcs(n_k, n_e-i_grid+1))
|
||||
do i = 1, n_k
|
||||
y = exp(log(this % dcs(i,i_grid)) + &
|
||||
f*(log(this % dcs(i,i_grid+1)) - log(this % dcs(i,i_grid))))
|
||||
dcs(i,:) = [y, this % dcs(i,i_grid+1:n_e)]
|
||||
end do
|
||||
call move_alloc(dcs, this % dcs)
|
||||
|
||||
electron_energy = [energy_cutoff(PHOTON), electron_energy(i_grid+1:n_e)]
|
||||
end if
|
||||
|
||||
! Set incident particle energy grid
|
||||
if (.not. allocated(ttb_e_grid)) then
|
||||
call move_alloc(electron_energy, ttb_e_grid)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Take logarithm of energies and cross sections since they are log-log
|
||||
! interpolated
|
||||
this % energy = log(this % energy)
|
||||
|
||||
where (this % coherent > ZERO)
|
||||
this % coherent = log(this % coherent)
|
||||
elsewhere
|
||||
this % coherent = -500.0_8
|
||||
end where
|
||||
|
||||
where (this % incoherent > ZERO)
|
||||
this % incoherent = log(this % incoherent)
|
||||
elsewhere
|
||||
this % incoherent = -500.0_8
|
||||
end where
|
||||
|
||||
where (this % photoelectric_total > ZERO)
|
||||
this % photoelectric_total = log(this % photoelectric_total)
|
||||
elsewhere
|
||||
this % photoelectric_total = -500.0_8
|
||||
end where
|
||||
|
||||
where (this % pair_production_total > ZERO)
|
||||
this % pair_production_total = log(this % pair_production_total)
|
||||
elsewhere
|
||||
this % pair_production_total = -500.0_8
|
||||
end where
|
||||
|
||||
end subroutine photon_from_hdf5
|
||||
|
||||
!===============================================================================
|
||||
! CALCULATE_ELEMENT_XS determines microscopic photon cross sections for an
|
||||
! element of a given index in the elements array at the energy of the given
|
||||
! particle
|
||||
!===============================================================================
|
||||
|
||||
subroutine photon_calculate_xs(this, E, xs)
|
||||
class(PhotonInteraction), intent(in) :: this ! index into elements array
|
||||
real(8), intent(in) :: E ! energy
|
||||
type(ElementMicroXS), intent(inout) :: xs
|
||||
|
||||
integer :: i_grid ! index on element energy grid
|
||||
integer :: i_shell ! index in subshells
|
||||
integer :: i_start ! threshold index
|
||||
integer :: n_grid ! number of grid points
|
||||
real(8) :: f ! interp factor on element energy grid
|
||||
real(8) :: log_E ! logarithm of the energy
|
||||
|
||||
! Perform binary search on the element energy grid in order to determine
|
||||
! which points to interpolate between
|
||||
n_grid = size(this % energy)
|
||||
log_E = log(E)
|
||||
if (log_E <= this % energy(1)) then
|
||||
i_grid = 1
|
||||
elseif (log_E > this % energy(n_grid)) then
|
||||
i_grid = n_grid - 1
|
||||
else
|
||||
i_grid = binary_search(this % energy, n_grid, log_E)
|
||||
end if
|
||||
|
||||
! check for case where two energy points are the same
|
||||
if (this % energy(i_grid) == this % energy(i_grid+1)) i_grid = i_grid + 1
|
||||
|
||||
! calculate interpolation factor
|
||||
f = (log_E - this % energy(i_grid)) / &
|
||||
(this % energy(i_grid+1) - this % energy(i_grid))
|
||||
|
||||
xs % index_grid = i_grid
|
||||
xs % interp_factor = f
|
||||
|
||||
! Calculate microscopic coherent cross section
|
||||
xs % coherent = exp(this % coherent(i_grid) + f * &
|
||||
(this % coherent(i_grid+1) - this % coherent(i_grid)))
|
||||
|
||||
! Calculate microscopic incoherent cross section
|
||||
xs % incoherent = exp(this % incoherent(i_grid) + &
|
||||
f*(this % incoherent(i_grid+1) - this % incoherent(i_grid)))
|
||||
|
||||
! Calculate microscopic photoelectric cross section
|
||||
xs % photoelectric = ZERO
|
||||
do i_shell = 1, size(this % shells)
|
||||
! Check threshold of reaction
|
||||
i_start = this % shells(i_shell) % threshold
|
||||
if (i_grid <= i_start) cycle
|
||||
|
||||
! Evaluation subshell photoionization cross section
|
||||
xs % photoelectric = xs % photoelectric + &
|
||||
exp(this % shells(i_shell) % cross_section(i_grid-i_start) + &
|
||||
f*(this % shells(i_shell) % cross_section(i_grid+1-i_start) - &
|
||||
this % shells(i_shell) % cross_section(i_grid-i_start)))
|
||||
end do
|
||||
|
||||
! Calculate microscopic pair production cross section
|
||||
xs % pair_production = exp(&
|
||||
this % pair_production_total(i_grid) + f*(&
|
||||
this % pair_production_total(i_grid+1) - &
|
||||
this % pair_production_total(i_grid)))
|
||||
|
||||
! Calculate microscopic total cross section
|
||||
xs % total = xs % coherent + xs % incoherent + xs % photoelectric + &
|
||||
xs % pair_production
|
||||
|
||||
xs % last_E = E
|
||||
|
||||
end subroutine photon_calculate_xs
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_PHOTON deallocates/resets global variables in this module
|
||||
!===============================================================================
|
||||
|
||||
subroutine free_memory_photon()
|
||||
! Deallocate photon cross section data
|
||||
if (allocated(elements)) deallocate(elements)
|
||||
if (allocated(compton_profile_pz)) deallocate(compton_profile_pz)
|
||||
n_elements = 0
|
||||
call element_dict % clear()
|
||||
|
||||
! Clear TTB-related arrays
|
||||
if (allocated(ttb_e_grid)) deallocate(ttb_e_grid)
|
||||
if (allocated(ttb)) deallocate(ttb)
|
||||
end subroutine free_memory_photon
|
||||
|
||||
end module photon_header
|
||||
641
src/photon_physics.F90
Normal file
641
src/photon_physics.F90
Normal file
|
|
@ -0,0 +1,641 @@
|
|||
module photon_physics
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants
|
||||
use particle_header
|
||||
use photon_header, only: PhotonInteraction, BremsstrahlungData, &
|
||||
compton_profile_pz, ttb_e_grid, ttb
|
||||
use random_lcg, only: prn
|
||||
use settings
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! KLEIN_NISHINA
|
||||
!===============================================================================
|
||||
|
||||
subroutine klein_nishina(alpha, alpha_out, mu)
|
||||
real(8), intent(in) :: alpha
|
||||
real(8), intent(out) :: alpha_out
|
||||
real(8), intent(out) :: mu
|
||||
|
||||
real(8) :: beta ! 1 + 2a
|
||||
real(8) :: t ! (1 + 2a)/(9 + 2a)
|
||||
real(8) :: r, s, x
|
||||
real(8) :: gamma
|
||||
|
||||
beta = ONE + TWO*alpha
|
||||
if (alpha < THREE) then
|
||||
! Kahn's rejection method
|
||||
t = beta/(beta + 8.0_8)
|
||||
do
|
||||
if (prn() < t) then
|
||||
! Left branch of flow chart
|
||||
r = TWO*prn()
|
||||
x = ONE + alpha*r
|
||||
if (prn() < FOUR/x*(ONE - ONE/x)) then
|
||||
mu = 1 - r
|
||||
exit
|
||||
end if
|
||||
else
|
||||
! Right branch of flow chart
|
||||
x = beta/(ONE + TWO*alpha*prn())
|
||||
mu = ONE + (ONE - x)/alpha
|
||||
if (prn() < HALF*(mu**2 + ONE/x)) exit
|
||||
end if
|
||||
end do
|
||||
alpha_out = alpha/x
|
||||
|
||||
else
|
||||
! Koblinger's direct method
|
||||
gamma = ONE - beta**(-2)
|
||||
s = prn()*(FOUR/alpha + HALF*gamma + &
|
||||
(ONE - (ONE + beta)/alpha**2)*log(beta))
|
||||
if (s <= 2./alpha) then
|
||||
! For first term, x = 1 + 2ar
|
||||
! Therefore, a' = a/(1 + 2ar)
|
||||
alpha_out = alpha/(ONE + TWO*alpha*prn())
|
||||
elseif (s <= FOUR/alpha) then
|
||||
! For third term, x = beta/(1 + 2ar)
|
||||
! Therefore, a' = a(1 + 2ar)/beta
|
||||
alpha_out = alpha*(ONE + TWO*alpha*prn())/beta
|
||||
elseif (s <= FOUR/alpha + HALF*gamma) then
|
||||
! For fourth term, x = 1/sqrt(1 - gamma*r)
|
||||
! Therefore, a' = a*sqrt(1 - gamma*r)
|
||||
alpha_out = alpha*sqrt(ONE - gamma*prn())
|
||||
else
|
||||
! For third term, x = beta^r
|
||||
! Therefore, a' = a/beta^r
|
||||
alpha_out = alpha/beta**prn()
|
||||
end if
|
||||
|
||||
! Calculate cosine of scattering angle based on basic relation
|
||||
mu = ONE + ONE/alpha - ONE/alpha_out
|
||||
end if
|
||||
|
||||
end subroutine klein_nishina
|
||||
|
||||
!===============================================================================
|
||||
! COMPTON_SCATTER
|
||||
!===============================================================================
|
||||
|
||||
subroutine compton_scatter(el, alpha, alpha_out, mu, i_shell, use_doppler)
|
||||
type(PhotonInteraction), intent(in) :: el
|
||||
real(8), intent(in) :: alpha
|
||||
real(8), intent(out) :: alpha_out
|
||||
real(8), intent(out) :: mu
|
||||
integer, intent(out) :: i_shell
|
||||
logical, intent(in), optional :: use_doppler
|
||||
|
||||
real(8) :: x
|
||||
real(8) :: form_factor_xmax
|
||||
real(8) :: form_factor_x
|
||||
real(8) :: e_out
|
||||
logical :: use_doppler_
|
||||
|
||||
if (present(use_doppler)) then
|
||||
use_doppler_ = use_doppler
|
||||
else
|
||||
use_doppler_ = .false.
|
||||
end if
|
||||
|
||||
form_factor_xmax = ZERO
|
||||
do
|
||||
! Sample Klein-Nishina distribution for trial energy and angle
|
||||
call klein_nishina(alpha, alpha_out, mu)
|
||||
|
||||
! Note that the parameter used here does not correspond exactly to the
|
||||
! momentum transfer q in ENDF-102 Eq. (27.2). Rather, this is the
|
||||
! parameter as defined by Hubbell, where the actual data comes from
|
||||
x = MASS_ELECTRON_EV/PLANCK_C*alpha*sqrt(HALF*(ONE - mu))
|
||||
|
||||
! Calculate S(x, Z) and S(x_max, Z)
|
||||
form_factor_x = el % incoherent_form_factor % evaluate(x)
|
||||
if (form_factor_xmax == ZERO) then
|
||||
form_factor_xmax = el % incoherent_form_factor % evaluate(&
|
||||
MASS_ELECTRON_EV/PLANCK_C*alpha)
|
||||
end if
|
||||
|
||||
! Perform rejection on form factor
|
||||
if (prn() < form_factor_x / form_factor_xmax) then
|
||||
if (use_doppler_) then
|
||||
call compton_doppler(el, alpha, mu, e_out, i_shell)
|
||||
alpha_out = e_out/MASS_ELECTRON_EV
|
||||
else
|
||||
i_shell = 0
|
||||
end if
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
|
||||
end subroutine compton_scatter
|
||||
|
||||
!===============================================================================
|
||||
! COMPTON_DOPPLER
|
||||
!===============================================================================
|
||||
|
||||
subroutine compton_doppler(el, alpha, mu, e_out, i_shell)
|
||||
type(PhotonInteraction), intent(in) :: el
|
||||
real(8), intent(in) :: alpha
|
||||
real(8), intent(in) :: mu
|
||||
real(8), intent(out) :: e_out
|
||||
integer, intent(out) :: i_shell
|
||||
|
||||
integer :: i
|
||||
integer :: n
|
||||
real(8) :: rn, m
|
||||
real(8) :: c, c_l, c_max
|
||||
real(8) :: pz_l, pz_r, pz, pz_max
|
||||
real(8) :: p_l, p_r
|
||||
real(8) :: e, e_b
|
||||
real(8) :: e_out1, e_out2
|
||||
real(8) :: a, b, quad
|
||||
real(8) :: f
|
||||
real(8) :: momentum_sq
|
||||
|
||||
n = size(compton_profile_pz)
|
||||
|
||||
do
|
||||
! Sample electron shell
|
||||
rn = prn()
|
||||
c = ZERO
|
||||
do i_shell = 1, size(el % electron_pdf)
|
||||
c = c + el % electron_pdf(i_shell)
|
||||
if (rn < c) exit
|
||||
end do
|
||||
|
||||
! Determine binding energy of shell
|
||||
e_b = el % binding_energy(i_shell)
|
||||
|
||||
! Determine p_z,max
|
||||
e = alpha*MASS_ELECTRON_EV
|
||||
if (e < e_b) then
|
||||
e_out = alpha/(1 + alpha*(1 - mu))*MASS_ELECTRON_EV
|
||||
exit
|
||||
end if
|
||||
|
||||
pz_max = -FINE_STRUCTURE*(e_b - (e - e_b)*alpha*(ONE - mu)) / &
|
||||
sqrt(TWO*e*(e - e_b)*(ONE - mu) + e_b**2)
|
||||
if (pz_max < ZERO) then
|
||||
e_out = alpha/(1 + alpha*(1 - mu))*MASS_ELECTRON_EV
|
||||
exit
|
||||
end if
|
||||
|
||||
! Determine profile cdf value corresponding to p_z,max
|
||||
if (pz_max > compton_profile_pz(n)) then
|
||||
c_max = el % profile_cdf(n, i_shell)
|
||||
else
|
||||
i = binary_search(compton_profile_pz, n, pz_max)
|
||||
pz_l = compton_profile_pz(i)
|
||||
pz_r = compton_profile_pz(i + 1)
|
||||
p_l = el % profile_pdf(i, i_shell)
|
||||
p_r = el % profile_pdf(i + 1, i_shell)
|
||||
c_l = el % profile_cdf(i, i_shell)
|
||||
if (pz_l == pz_r) then
|
||||
c_max = c_l
|
||||
elseif (p_l == p_r) then
|
||||
c_max = c_l + (pz_max - pz_l)*p_l
|
||||
else
|
||||
m = (p_l - p_r)/(pz_l - pz_r)
|
||||
c_max = c_l + ((m*(pz_max - pz_l) + p_l)**2 - p_l**2)/(TWO*m)
|
||||
end if
|
||||
end if
|
||||
|
||||
! Sample value on bounded cdf
|
||||
c = prn()*c_max
|
||||
|
||||
! Determine pz corresponding to sampled cdf value
|
||||
i = binary_search(el % profile_cdf(:, i_shell), n, c)
|
||||
pz_l = compton_profile_pz(i)
|
||||
pz_r = compton_profile_pz(i + 1)
|
||||
p_l = el % profile_pdf(i, i_shell)
|
||||
p_r = el % profile_pdf(i + 1, i_shell)
|
||||
c_l = el % profile_cdf(i, i_shell)
|
||||
if (pz_l == pz_r) then
|
||||
pz = pz_l
|
||||
elseif (p_l == p_r) then
|
||||
pz = pz_l + (c - c_l)/p_l
|
||||
else
|
||||
m = (p_l - p_r)/(pz_l - pz_r)
|
||||
pz = pz_l + (sqrt(p_l**2 + TWO*m*(c - c_l)) - p_l)/m
|
||||
end if
|
||||
|
||||
! Determine outgoing photon energy corresponding to electron momentum
|
||||
! (solve Eq. 39 in LA-UR-04-0487 for E')
|
||||
momentum_sq = (pz/FINE_STRUCTURE)**2
|
||||
f = ONE + alpha*(ONE - mu)
|
||||
a = momentum_sq - f*f
|
||||
b = TWO*e*(f - momentum_sq*mu)
|
||||
c = e**2*(momentum_sq - ONE)
|
||||
|
||||
quad = b**2 - FOUR*a*c
|
||||
if (quad < 0) then
|
||||
e_out = alpha/(1 + alpha*(1 - mu))*MASS_ELECTRON_EV
|
||||
exit
|
||||
end if
|
||||
quad = sqrt(quad)
|
||||
e_out1 = -(b + quad)/(TWO*a)
|
||||
e_out2 = -(b - quad)/(TWO*a)
|
||||
|
||||
! Determine solution to quadratic equation that is positive
|
||||
if (e_out1 > ZERO) then
|
||||
if (e_out2 > ZERO) then
|
||||
! If both are positive, pick one at random
|
||||
if (prn() < HALF) then
|
||||
e_out = e_out1
|
||||
else
|
||||
e_out = e_out2
|
||||
end if
|
||||
else
|
||||
e_out = e_out1
|
||||
end if
|
||||
else
|
||||
if (e_out2 > ZERO) then
|
||||
e_out = e_out2
|
||||
else
|
||||
! No positive solution -- resample
|
||||
cycle
|
||||
end if
|
||||
end if
|
||||
if (e_out < e - e_b) exit
|
||||
end do
|
||||
|
||||
end subroutine compton_doppler
|
||||
|
||||
!===============================================================================
|
||||
! RAYLEIGH_SCATTER
|
||||
!===============================================================================
|
||||
|
||||
subroutine rayleigh_scatter(el, alpha, mu)
|
||||
type(PhotonInteraction), intent(in) :: el
|
||||
real(8), intent(in) :: alpha
|
||||
real(8), intent(out) :: mu
|
||||
|
||||
integer :: i
|
||||
real(8) :: F
|
||||
real(8) :: F_max
|
||||
real(8) :: x2
|
||||
real(8) :: x2_max
|
||||
real(8) :: r
|
||||
|
||||
do
|
||||
! Determine maximum value of x^2
|
||||
x2_max = (MASS_ELECTRON_EV/PLANCK_C*alpha)**2
|
||||
|
||||
! Determine F(x^2_max, Z)
|
||||
F_max = el % coherent_int_form_factor % evaluate(x2_max)
|
||||
|
||||
! Sample cumulative distribution
|
||||
F = prn()*F_max
|
||||
|
||||
! Determine x^2 corresponding to F
|
||||
i = binary_search(el%coherent_int_form_factor%y, &
|
||||
size(el%coherent_int_form_factor%y), F)
|
||||
r = (F - el%coherent_int_form_factor%y(i)) / &
|
||||
(el%coherent_int_form_factor%y(i+1) - el%coherent_int_form_factor%y(i))
|
||||
x2 = el%coherent_int_form_factor%x(i) + r*(el%coherent_int_form_factor%x(i+1) - &
|
||||
el%coherent_int_form_factor%x(i))
|
||||
|
||||
! Calculate mu
|
||||
mu = ONE - TWO*x2/x2_max
|
||||
|
||||
if (prn() < HALF*(ONE + mu**2)) exit
|
||||
end do
|
||||
|
||||
end subroutine rayleigh_scatter
|
||||
|
||||
!===============================================================================
|
||||
! ATOMIC_RELAXATION
|
||||
!===============================================================================
|
||||
|
||||
recursive subroutine atomic_relaxation(p, elm, i_shell)
|
||||
type(Particle), intent(inout) :: p
|
||||
type(PhotonInteraction), intent(in) :: elm
|
||||
integer, intent(in) :: i_shell
|
||||
|
||||
integer :: i_hole
|
||||
integer :: i_transition
|
||||
integer :: primary
|
||||
integer :: secondary
|
||||
real(8) :: c
|
||||
real(8) :: rn
|
||||
real(8) :: E
|
||||
real(8) :: mu
|
||||
real(8) :: phi
|
||||
real(8) :: uvw(3)
|
||||
|
||||
! If no transitions, assume fluorescent photon from captured free electron
|
||||
if (elm % shells(i_shell) % n_transitions == 0) then
|
||||
mu = TWO*prn() - ONE
|
||||
phi = TWO*PI*prn()
|
||||
uvw(1) = mu
|
||||
uvw(2) = sqrt(ONE - mu*mu)*cos(phi)
|
||||
uvw(3) = sqrt(ONE - mu*mu)*sin(phi)
|
||||
E = elm % shells(i_shell) % binding_energy
|
||||
call particle_create_secondary(p, uvw, E, PHOTON, run_ce=.true._C_BOOL)
|
||||
return
|
||||
end if
|
||||
|
||||
! Sample transition
|
||||
rn = prn()
|
||||
c = ZERO
|
||||
do i_transition = 1, elm % shells(i_shell) % n_transitions
|
||||
c = c + elm % shells(i_shell) % &
|
||||
transition_probability(i_transition)
|
||||
if (rn < c) exit
|
||||
end do
|
||||
|
||||
! Get primary and secondary subshell designators
|
||||
primary = elm % shells(i_shell) % transition_subshells(1, i_transition)
|
||||
secondary = elm % shells(i_shell) % transition_subshells(2, i_transition)
|
||||
|
||||
! Sample angle isotropically
|
||||
mu = TWO*prn() - ONE
|
||||
phi = TWO*PI*prn()
|
||||
uvw(1) = mu
|
||||
uvw(2) = sqrt(ONE - mu*mu)*cos(phi)
|
||||
uvw(3) = sqrt(ONE - mu*mu)*sin(phi)
|
||||
|
||||
! Get the transition energy
|
||||
E = elm % shells(i_shell) % transition_energy(i_transition)
|
||||
|
||||
if (secondary /= 0) then
|
||||
! Non-radiative transition -- Auger/Coster-Kronig effect
|
||||
|
||||
! Create auger electron
|
||||
call particle_create_secondary(p, uvw, E, ELECTRON, run_ce=.true._C_BOOL)
|
||||
|
||||
! Fill hole left by emitted auger electron
|
||||
i_hole = elm % shell_dict % get(secondary)
|
||||
call atomic_relaxation(p, elm, i_hole)
|
||||
else
|
||||
! Radiative transition -- get X-ray energy
|
||||
|
||||
! Create fluorescent photon
|
||||
call particle_create_secondary(p, uvw, E, PHOTON, run_ce=.true._C_BOOL)
|
||||
|
||||
end if
|
||||
|
||||
! Fill hole created by electron transitioning to the photoelectron hole
|
||||
i_hole = elm % shell_dict % get(primary)
|
||||
call atomic_relaxation(p, elm, i_hole)
|
||||
|
||||
end subroutine atomic_relaxation
|
||||
|
||||
!===============================================================================
|
||||
! PAIR_PRODUCTION samples the kinetic energy and direction of the electron and
|
||||
! positron created when a photon is absorbed near an atomic nucleus. The
|
||||
! simulation procedure follows the semiempirical model outlined in F. Salvat, J.
|
||||
! M. Fernández-Varea, and J. Sempau, "PENELOPE-2011: A Code System for Monte
|
||||
! Carlo Simulation of Electron and Photon Transport," OECD-NEA,
|
||||
! Issy-les-Moulineaux, France (2011).
|
||||
!===============================================================================
|
||||
|
||||
subroutine pair_production(elm, alpha, E_electron, E_positron, mu_electron, &
|
||||
mu_positron)
|
||||
type(PhotonInteraction), intent(in) :: elm
|
||||
real(8), intent(in) :: alpha
|
||||
real(8), intent(out) :: E_electron
|
||||
real(8), intent(out) :: E_positron
|
||||
real(8), intent(out) :: mu_electron
|
||||
real(8), intent(out) :: mu_positron
|
||||
|
||||
integer :: i
|
||||
real(8) :: f
|
||||
real(8) :: c
|
||||
real(8) :: a
|
||||
real(8) :: b
|
||||
real(8) :: q
|
||||
real(8) :: rn
|
||||
real(8) :: beta
|
||||
real(8) :: e, e_min, e_max
|
||||
real(8) :: t1, t2, t3, t4
|
||||
real(8) :: u1, u2
|
||||
real(8) :: phi1, phi2
|
||||
real(8) :: phi1_max, phi2_max
|
||||
real(8), parameter :: r(99) = (/ &
|
||||
122.81_8, 73.167_8, 69.228_8, 67.301_8, 64.696_8, 61.228_8, &
|
||||
57.524_8, 54.033_8, 50.787_8, 47.851_8, 46.373_8, 45.401_8, &
|
||||
44.503_8, 43.815_8, 43.074_8, 42.321_8, 41.586_8, 40.953_8, &
|
||||
40.524_8, 40.256_8, 39.756_8, 39.144_8, 38.462_8, 37.778_8, &
|
||||
37.174_8, 36.663_8, 35.986_8, 35.317_8, 34.688_8, 34.197_8, &
|
||||
33.786_8, 33.422_8, 33.068_8, 32.740_8, 32.438_8, 32.143_8, &
|
||||
31.884_8, 31.622_8, 31.438_8, 31.142_8, 30.950_8, 30.758_8, &
|
||||
30.561_8, 30.285_8, 30.097_8, 29.832_8, 29.581_8, 29.411_8, &
|
||||
29.247_8, 29.085_8, 28.930_8, 28.721_8, 28.580_8, 28.442_8, &
|
||||
28.312_8, 28.139_8, 27.973_8, 27.819_8, 27.675_8, 27.496_8, &
|
||||
27.285_8, 27.093_8, 26.911_8, 26.705_8, 26.516_8, 26.304_8, &
|
||||
26.108_8, 25.929_8, 25.730_8, 25.577_8, 25.403_8, 25.245_8, &
|
||||
25.100_8, 24.941_8, 24.790_8, 24.655_8, 24.506_8, 24.391_8, &
|
||||
24.262_8, 24.145_8, 24.039_8, 23.922_8, 23.813_8, 23.712_8, &
|
||||
23.621_8, 23.523_8, 23.430_8, 23.331_8, 23.238_8, 23.139_8, &
|
||||
23.048_8, 22.967_8, 22.833_8, 22.694_8, 22.624_8, 22.545_8, &
|
||||
22.446_8, 22.358_8, 22.264_8 /)
|
||||
|
||||
! The reduced screening radius r is the ratio of the screening radius to
|
||||
! the Compton wavelength of the electron, where the screening radius is
|
||||
! obtained under the assumption that the Coulomb field of the nucleus is
|
||||
! exponentially screened by atomic electrons. This allows us to use a
|
||||
! simplified atomic form factor and analytical approximations of the
|
||||
! screening functions in the pair production DCS instead of computing the
|
||||
! screening functions numerically. The reduced screening radii above for
|
||||
! Z = 1-99 come from F. Salvat, J. M. Fernández-Varea, and J. Sempau,
|
||||
! "PENELOPE-2011: A Code System for Monte Carlo Simulation of Electron and
|
||||
! Photon Transport," OECD-NEA, Issy-les-Moulineaux, France (2011).
|
||||
|
||||
! Compute the minimum and maximum values of the electron reduced energy,
|
||||
! i.e. the fraction of the photon energy that is given to the electron
|
||||
e_min = ONE/alpha
|
||||
e_max = ONE - ONE/alpha
|
||||
|
||||
! Compute the high-energy Coulomb correction
|
||||
a = elm % Z / FINE_STRUCTURE
|
||||
c = a**2*(ONE/(ONE + a**2) + 0.202059_8 - 0.03693_8*a**2 + 0.00835_8*a**4 &
|
||||
- 0.00201_8*a**6 + 0.00049_8*a**8 - 0.00012_8*a**10 + 0.00003_8*a**12)
|
||||
|
||||
! The analytical approximation of the DCS underestimates the cross section
|
||||
! at low energies. The correction factor f compensates for this.
|
||||
q = sqrt(TWO/alpha)
|
||||
f = q*(-0.1774_8 - 12.10_8*a + 11.18_8*a**2) &
|
||||
+ q**2*(8.523_8 + 73.26_8*a - 44.41_8*a**2) &
|
||||
+ q**3*(-13.52_8 - 121.1_8*a + 96.41_8*a**2) &
|
||||
+ q**4*(8.946_8 + 62.05_8*a - 63.41_8*a**2)
|
||||
|
||||
! Calculate phi_1(1/2) and phi_2(1/2). The unnormalized PDF for the reduced
|
||||
! energy is given by p = 2*(1/2 - e)^2*phi_1(e) + phi_2(e), where phi_1 and
|
||||
! phi_2 are non-negative and maximum at e = 1/2.
|
||||
b = TWO*r(elm % Z)/alpha
|
||||
t1 = TWO*log(ONE + b**2)
|
||||
t2 = b*atan(ONE/b)
|
||||
t3 = b**2*(FOUR - FOUR*t2 - THREE*log(ONE + ONE/b**2))
|
||||
t4 = FOUR*log(r(elm % Z)) - FOUR*c + f
|
||||
phi1_max = 7.0_8/THREE - t1 - 6.0_8*t2 - t3 + t4
|
||||
phi2_max = 11.0_8/6.0_8 - t1 - THREE*t2 + HALF*t3 + t4
|
||||
|
||||
! To aid sampling, the unnormalized PDF can be expressed as
|
||||
! p = u_1*U_1(e)*pi_1(e) + u_2*U_2(e)*pi_2(e), where pi_1 and pi_2 are
|
||||
! normalized PDFs on the interval (e_min, e_max) from which values of e can
|
||||
! be sampled using the inverse transform method, and
|
||||
! U_1 = phi_1(e)/phi_1(1/2) and U_2 = phi_2(e)/phi_2(1/2) are valid
|
||||
! rejection functions. The reduced energy can now be sampled using a
|
||||
! combination of the composition and rejection methods.
|
||||
u1 = TWO/THREE*(HALF - ONE/alpha)**2*phi1_max
|
||||
u2 = phi2_max
|
||||
do
|
||||
rn = prn()
|
||||
|
||||
! Sample the index i in (1, 2) using the point probabilities
|
||||
! p(1) = u_1/(u_1 + u_2) and p(2) = u_2/(u_1 + u_2)
|
||||
if (prn() < u1/(u1 + u2)) then
|
||||
i = 1
|
||||
|
||||
! Sample e from pi_1 using the inverse transform method
|
||||
if (rn >= HALF) then
|
||||
e = HALF + (HALF - ONE/alpha)*(TWO*rn - ONE)**(ONE/THREE)
|
||||
else
|
||||
e = HALF - (HALF - ONE/alpha)*(ONE - TWO*rn)**(ONE/THREE)
|
||||
end if
|
||||
else
|
||||
i = 2
|
||||
|
||||
! Sample e from pi_2 using the inverse transform method
|
||||
e = ONE/alpha + (HALF - ONE/alpha)*TWO*rn
|
||||
end if
|
||||
|
||||
! Calculate phi_i(e) and deliver e if rn <= U_i(e)
|
||||
b = r(elm % Z)/(TWO*alpha*e*(ONE - e))
|
||||
t1 = TWO*log(ONE + b**2)
|
||||
t2 = b*atan(ONE/b)
|
||||
t3 = b**2*(FOUR - FOUR*t2 - THREE*log(ONE + ONE/b**2))
|
||||
if (i == 1) then
|
||||
phi1 = 7.0_8/THREE - t1 - 6.0_8*t2 - t3 + t4
|
||||
if (prn() <= phi1/phi1_max) exit
|
||||
else
|
||||
phi2 = 11.0_8/6.0_8 - t1 - THREE*t2 + HALF*t3 + t4
|
||||
if (prn() <= phi2/phi2_max) exit
|
||||
end if
|
||||
end do
|
||||
|
||||
! Compute the kinetic energy of the electron and the positron
|
||||
E_electron = (alpha*e - ONE)*MASS_ELECTRON_EV
|
||||
E_positron = (alpha*(ONE - e) - ONE)*MASS_ELECTRON_EV
|
||||
|
||||
! Sample the scattering angle of the electron. The cosine of the polar
|
||||
! angle of the direction relative to the incident photon is sampled from
|
||||
! p(mu) = C/(1 - beta*mu)^2 using the inverse transform method.
|
||||
beta = sqrt(E_electron*(E_electron + TWO*MASS_ELECTRON_EV)) &
|
||||
/ (E_electron + MASS_ELECTRON_EV)
|
||||
rn = TWO*prn() - ONE
|
||||
mu_electron = (rn + beta)/(rn*beta + ONE)
|
||||
|
||||
! Sample the scattering angle of the positron
|
||||
beta = sqrt(E_positron*(E_positron + TWO*MASS_ELECTRON_EV)) &
|
||||
/ (E_positron + MASS_ELECTRON_EV)
|
||||
rn = TWO*prn() - ONE
|
||||
mu_positron = (rn + beta)/(rn*beta + ONE)
|
||||
|
||||
end subroutine pair_production
|
||||
|
||||
!===============================================================================
|
||||
! THICK_TARGET_BREMSSTRAHLUNG
|
||||
!===============================================================================
|
||||
|
||||
subroutine thick_target_bremsstrahlung(p, E_lost)
|
||||
type(Particle), intent(inout) :: p
|
||||
real(8), intent(inout) :: E_lost
|
||||
|
||||
integer :: i, j
|
||||
integer :: i_e, i_w
|
||||
integer :: n
|
||||
integer :: n_e
|
||||
real(8) :: a
|
||||
real(8) :: f
|
||||
real(8) :: e, e_l, e_r
|
||||
real(8) :: y, y_l, y_r
|
||||
real(8) :: w, w_l, w_r
|
||||
real(8) :: p_l, p_r
|
||||
real(8) :: c, c_l, c_max
|
||||
type(BremsstrahlungData), pointer :: mat
|
||||
|
||||
if (p % E < energy_cutoff(PHOTON)) return
|
||||
|
||||
! Get bremsstrahlung data for this material and particle type
|
||||
if (p % type == POSITRON) then
|
||||
mat => ttb(p % material) % positron
|
||||
else
|
||||
mat => ttb(p % material) % electron
|
||||
end if
|
||||
|
||||
e = log(p % E)
|
||||
n_e = size(ttb_e_grid)
|
||||
|
||||
! Find the lower bounding index of the incident electron energy
|
||||
j = binary_search(ttb_e_grid, n_e, e)
|
||||
if (j == n_e) j = j - 1
|
||||
|
||||
! Get the interpolation bounds
|
||||
e_l = ttb_e_grid(j)
|
||||
e_r = ttb_e_grid(j+1)
|
||||
y_l = mat % yield(j)
|
||||
y_r = mat % yield(j+1)
|
||||
|
||||
! Calculate the interpolation weight w_j+1 of the bremsstrahlung energy PDF
|
||||
! interpolated in log energy, which can be interpreted as the probability
|
||||
! of index j+1
|
||||
f = (e - e_l)/(e_r - e_l)
|
||||
|
||||
! Get the photon number yield for the given energy using linear
|
||||
! interpolation on a log-log scale
|
||||
y = exp(y_l + (y_r - y_l)*f)
|
||||
|
||||
! Sample number of secondary bremsstrahlung photons
|
||||
n = int(y + prn())
|
||||
|
||||
E_lost = ZERO
|
||||
if (n == 0) return
|
||||
|
||||
! Sample index of the tabulated PDF in the energy grid, j or j+1
|
||||
if (prn() <= f .or. j == 1) then
|
||||
i_e = j + 1
|
||||
|
||||
! Interpolate the maximum value of the CDF at the incoming particle
|
||||
! energy on a log-log scale
|
||||
p_l = mat % pdf(i_e-1, i_e)
|
||||
p_r = mat % pdf(i_e, i_e)
|
||||
c_l = mat % cdf(i_e-1, i_e)
|
||||
a = log(p_r/p_l)/(e_r - e_l) + ONE
|
||||
c_max = c_l + exp(e_l)*p_l/a*(exp(a*(e - e_l)) - ONE)
|
||||
else
|
||||
i_e = j
|
||||
|
||||
! Maximum value of the CDF
|
||||
c_max = mat % cdf(i_e, i_e)
|
||||
end if
|
||||
|
||||
! Sample the energies of the emitted photons
|
||||
do i = 1, n
|
||||
! Generate a random number r and determine the index i for which
|
||||
! cdf(i) <= r*cdf,max <= cdf(i+1)
|
||||
c = prn()*c_max
|
||||
i_w = binary_search(mat % cdf(:i_e,i_e), i_e, c)
|
||||
|
||||
! Sample the photon energy
|
||||
w_l = ttb_e_grid(i_w)
|
||||
w_r = ttb_e_grid(i_w+1)
|
||||
p_l = mat % pdf(i_w, i_e)
|
||||
p_r = mat % pdf(i_w+1, i_e)
|
||||
c_l = mat % cdf(i_w, i_e)
|
||||
a = log(p_r/p_l)/(w_r - w_l) + ONE
|
||||
w = exp(w_l)*(a*(c - c_l)/(exp(w_l)*p_l) + ONE)**(ONE/a)
|
||||
|
||||
if (w > energy_cutoff(PHOTON)) then
|
||||
! Create secondary photon
|
||||
call particle_create_secondary(p, p % coord(1) % uvw, w, PHOTON, &
|
||||
run_ce=.true._C_BOOL)
|
||||
E_lost = E_lost + w
|
||||
end if
|
||||
end do
|
||||
|
||||
end subroutine thick_target_bremsstrahlung
|
||||
|
||||
end module photon_physics
|
||||
487
src/physics.F90
487
src/physics.F90
|
|
@ -9,7 +9,11 @@ module physics
|
|||
use mesh_header, only: meshes
|
||||
use message_passing
|
||||
use nuclide_header
|
||||
use particle_header, only: Particle
|
||||
use particle_header
|
||||
use photon_header
|
||||
use photon_physics, only: rayleigh_scatter, compton_scatter, &
|
||||
atomic_relaxation, pair_production, &
|
||||
thick_target_bremsstrahlung
|
||||
use physics_common
|
||||
use random_lcg, only: prn, advance_prn_seed, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
|
|
@ -36,40 +40,48 @@ contains
|
|||
! Add to collision counter for particle
|
||||
p % n_collision = p % n_collision + 1
|
||||
|
||||
! Sample nuclide/reaction for the material the particle is in
|
||||
call sample_reaction(p)
|
||||
! Sample reaction for the material the particle is in
|
||||
if (p % type == NEUTRON) then
|
||||
call sample_neutron_reaction(p)
|
||||
else if (p % type == PHOTON) then
|
||||
call sample_photon_reaction(p)
|
||||
else if (p % type == ELECTRON) then
|
||||
call sample_electron_reaction(p)
|
||||
else if (p % type == POSITRON) then
|
||||
call sample_positron_reaction(p)
|
||||
end if
|
||||
|
||||
! Kill particle if energy falls below cutoff
|
||||
if (p % E < energy_cutoff(p % type)) then
|
||||
p % alive = .false.
|
||||
p % wgt = ZERO
|
||||
p % last_wgt = ZERO
|
||||
end if
|
||||
|
||||
! Display information about collision
|
||||
if (verbosity >= 10 .or. trace) then
|
||||
call write_message(" " // trim(reaction_name(p % event_MT)) &
|
||||
&// " with " // trim(adjustl(nuclides(p % event_nuclide) % name)) &
|
||||
&// ". Energy = " // trim(to_str(p % E)) // " eV.")
|
||||
if (p % type == NEUTRON) then
|
||||
call write_message(" " // trim(reaction_name(p % event_MT)) &
|
||||
&// " with " // trim(adjustl(nuclides(p % event_nuclide) % name)) &
|
||||
&// ". Energy = " // trim(to_str(p % E)) // " eV.")
|
||||
else
|
||||
call write_message(" " // trim(reaction_name(p % event_MT)) &
|
||||
&// " with " // trim(adjustl(elements(p % event_nuclide) % name)) &
|
||||
&// ". Energy = " // trim(to_str(p % E)) // " eV.")
|
||||
end if
|
||||
end if
|
||||
|
||||
! check for very low energy
|
||||
if (p % E < 1.0e-100_8) then
|
||||
p % alive = .false.
|
||||
if (master) call warning("Killing neutron with extremely low energy")
|
||||
end if
|
||||
|
||||
! Advance URR seed stream 'N' times after energy changes
|
||||
if (p % E /= p % last_E) then
|
||||
call prn_set_stream(STREAM_URR_PTABLE)
|
||||
call advance_prn_seed(size(nuclides, kind=8))
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
endif
|
||||
|
||||
end subroutine collision
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_REACTION samples a nuclide based on the macroscopic cross sections for
|
||||
! each nuclide within a material and then samples a reaction for that nuclide
|
||||
! and calls the appropriate routine to process the physics. Note that there is
|
||||
! special logic when suvival biasing is turned on since fission and
|
||||
! disappearance are treated implicitly.
|
||||
! SAMPLE_NEUTRON_REACTION samples a nuclide based on the macroscopic cross
|
||||
! sections for each nuclide within a material and then samples a reaction for
|
||||
! that nuclide and calls the appropriate routine to process the physics. Note
|
||||
! that there is special logic when suvival biasing is turned on since fission
|
||||
! and disappearance are treated implicitly.
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_reaction(p)
|
||||
subroutine sample_neutron_reaction(p)
|
||||
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
|
|
@ -102,6 +114,13 @@ contains
|
|||
end if
|
||||
end if
|
||||
|
||||
! Create secondary photons
|
||||
if (photon_transport) then
|
||||
call prn_set_stream(STREAM_PHOTON)
|
||||
call sample_secondary_photons(p, i_nuclide)
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
end if
|
||||
|
||||
! If survival biasing is being used, the following subroutine adjusts the
|
||||
! weight of the particle. Otherwise, it checks to see if absorption occurs
|
||||
|
||||
|
|
@ -116,21 +135,259 @@ contains
|
|||
! exiting neutron
|
||||
call scatter(p, i_nuclide, i_nuc_mat)
|
||||
|
||||
! Play russian roulette if survival biasing is turned on
|
||||
! Advance URR seed stream 'N' times after energy changes
|
||||
if (p % E /= p % last_E) then
|
||||
call prn_set_stream(STREAM_URR_PTABLE)
|
||||
call advance_prn_seed(size(nuclides, kind=8))
|
||||
call prn_set_stream(STREAM_TRACKING)
|
||||
end if
|
||||
|
||||
! Play russian roulette if survival biasing is turned on
|
||||
if (survival_biasing) then
|
||||
call russian_roulette(p)
|
||||
if (.not. p % alive) return
|
||||
end if
|
||||
|
||||
! Kill neutron under certain energy
|
||||
if (p % E < energy_cutoff) then
|
||||
end subroutine sample_neutron_reaction
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_PHOTON_REACTION samples an element based on the macroscopic cross
|
||||
! sections for each nuclide within a material and then samples a reaction for
|
||||
! that element and calls the appropriate routine to process the physics.
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_photon_reaction(p)
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
integer :: i_shell ! index in subshells
|
||||
integer :: i_grid ! index on energy grid
|
||||
integer :: i_element ! index in nuclides array
|
||||
integer :: i_start ! threshold index
|
||||
real(8) :: prob ! cumulative probability
|
||||
real(8) :: cutoff ! sampled total cross section
|
||||
real(8) :: f ! interpolation factor
|
||||
real(8) :: xs ! photoionization cross section
|
||||
real(8) :: r ! random number
|
||||
real(8) :: prob_after
|
||||
real(8) :: alpha ! photon energy divided by electron rest mass
|
||||
real(8) :: alpha_out ! outgoing photon energy over electron rest mass
|
||||
real(8) :: mu ! scattering cosine
|
||||
real(8) :: mu_electron ! electron scattering cosine
|
||||
real(8) :: mu_positron ! positron scattering cosine
|
||||
real(8) :: phi ! azimuthal angle
|
||||
real(8) :: uvw(3) ! new direction
|
||||
real(8) :: rel_vel ! relative velocity of electron
|
||||
real(8) :: e_b ! binding energy of electron
|
||||
real(8) :: E_electron ! electron energy
|
||||
real(8) :: E_positron ! positron energy
|
||||
|
||||
! Kill photon if below energy cutoff -- an extra check is made here because
|
||||
! photons with energy below the cutoff may have been produced by neutrons
|
||||
! reactions or atomic relaxation
|
||||
if (p % E < energy_cutoff(PHOTON)) then
|
||||
p % E = ZERO
|
||||
p % alive = .false.
|
||||
p % wgt = ZERO
|
||||
p % last_wgt = ZERO
|
||||
return
|
||||
end if
|
||||
|
||||
end subroutine sample_reaction
|
||||
! Sample element within material
|
||||
i_element = sample_element(p)
|
||||
p % event_nuclide = i_element
|
||||
|
||||
! Calculate photon energy over electron rest mass equivalent
|
||||
alpha = p % E/MASS_ELECTRON_EV
|
||||
|
||||
! For tallying purposes, this routine might be called directly. In that
|
||||
! case, we need to sample a reaction via the cutoff variable
|
||||
prob = ZERO
|
||||
cutoff = prn() * micro_photon_xs(i_element) % total
|
||||
|
||||
associate (elm => elements(i_element))
|
||||
! Coherent (Rayleigh) scattering
|
||||
prob = prob + micro_photon_xs(i_element) % coherent
|
||||
if (prob > cutoff) then
|
||||
call rayleigh_scatter(elm, alpha, mu)
|
||||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
p % event_MT = COHERENT
|
||||
return
|
||||
end if
|
||||
|
||||
! Incoherent (Compton) scattering
|
||||
prob = prob + micro_photon_xs(i_element) % incoherent
|
||||
if (prob > cutoff) then
|
||||
call compton_scatter(elm, alpha, alpha_out, mu, i_shell, .true.)
|
||||
|
||||
! Determine binding energy of shell. The binding energy is zero if
|
||||
! doppler broadening is not used.
|
||||
if (i_shell == 0) then
|
||||
e_b = ZERO
|
||||
else
|
||||
e_b = elm % binding_energy(i_shell)
|
||||
end if
|
||||
|
||||
! Create Compton electron
|
||||
E_electron = (alpha - alpha_out)*MASS_ELECTRON_EV - e_b
|
||||
mu_electron = (alpha - alpha_out*mu) &
|
||||
/ sqrt(alpha**2 + alpha_out**2 - TWO*alpha*alpha_out*mu)
|
||||
phi = TWO*PI*prn()
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu_electron, phi)
|
||||
call particle_create_secondary(p, uvw, E_electron, ELECTRON, .true._C_BOOL)
|
||||
|
||||
! TODO: Compton subshell data does not match atomic relaxation data
|
||||
! Allow electrons to fill orbital and produce auger electrons
|
||||
! and fluorescent photons
|
||||
if (i_shell > 0) then
|
||||
call atomic_relaxation(p, elm, i_shell)
|
||||
end if
|
||||
|
||||
phi = phi + PI
|
||||
p % E = alpha_out*MASS_ELECTRON_EV
|
||||
p % coord(1) % uvw = rotate_angle(p % coord(1) % uvw, mu, phi)
|
||||
p % event_MT = INCOHERENT
|
||||
return
|
||||
end if
|
||||
|
||||
! Photoelectric effect
|
||||
prob_after = prob + micro_photon_xs(i_element) % photoelectric
|
||||
if (prob_after > cutoff) then
|
||||
do i_shell = 1, size(elm % shells)
|
||||
! Get grid index and interpolation factor
|
||||
i_grid = micro_photon_xs(i_element) % index_grid
|
||||
f = micro_photon_xs(i_element) % interp_factor
|
||||
|
||||
! Check threshold of reaction
|
||||
i_start = elm % shells(i_shell) % threshold
|
||||
if (i_grid <= i_start) cycle
|
||||
|
||||
! Evaluation subshell photoionization cross section
|
||||
xs = exp(elm % shells(i_shell) % cross_section(i_grid - i_start) + &
|
||||
f*(elm % shells(i_shell) % cross_section(i_grid + 1 - i_start) - &
|
||||
elm % shells(i_shell) % cross_section(i_grid - i_start)))
|
||||
|
||||
prob = prob + xs
|
||||
if (prob > cutoff) then
|
||||
E_electron = p % E - elm % shells(i_shell) % binding_energy
|
||||
|
||||
! Sample mu using non-relativistic Sauter distribution.
|
||||
! See Eqns 3.19 and 3.20 in "Implementing a photon physics
|
||||
! model in Serpent 2" by Toni Kaltiaisenaho
|
||||
SAMPLE_MU: do
|
||||
r = prn()
|
||||
if (FOUR * (ONE - r) * r >= prn()) then
|
||||
rel_vel = sqrt(E_electron * (E_electron + TWO * MASS_ELECTRON_EV))&
|
||||
/ (E_electron + MASS_ELECTRON_EV)
|
||||
mu = (TWO * r + rel_vel - ONE) / &
|
||||
(TWO * rel_vel * r - rel_vel + ONE)
|
||||
exit SAMPLE_MU
|
||||
end if
|
||||
end do SAMPLE_MU
|
||||
|
||||
phi = TWO*PI*prn()
|
||||
uvw(1) = mu
|
||||
uvw(2) = sqrt(ONE - mu*mu)*cos(phi)
|
||||
uvw(3) = sqrt(ONE - mu*mu)*sin(phi)
|
||||
|
||||
! Create secondary electron
|
||||
call particle_create_secondary(p, uvw, E_electron, ELECTRON, &
|
||||
run_CE=.true._C_BOOL)
|
||||
|
||||
! Allow electrons to fill orbital and produce auger electrons
|
||||
! and fluorescent photons
|
||||
call atomic_relaxation(p, elm, i_shell)
|
||||
p % event_MT = 533 + elm % shells(i_shell) % index_subshell
|
||||
p % alive = .false.
|
||||
p % E = ZERO
|
||||
|
||||
return
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
prob = prob_after
|
||||
|
||||
! Pair production
|
||||
prob = prob + micro_photon_xs(i_element) % pair_production
|
||||
if (prob > cutoff) then
|
||||
call pair_production(elm, alpha, E_electron, E_positron, mu_electron, &
|
||||
mu_positron)
|
||||
|
||||
! Create secondary electron
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu_electron)
|
||||
call particle_create_secondary(p, uvw, E_electron, ELECTRON, .true._C_BOOL)
|
||||
|
||||
! Create secondary positron
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu_positron)
|
||||
call particle_create_secondary(p, uvw, E_positron, POSITRON, .true._C_BOOL)
|
||||
|
||||
p % event_MT = PAIR_PROD
|
||||
p % alive = .false.
|
||||
p % E = ZERO
|
||||
end if
|
||||
|
||||
end associate
|
||||
|
||||
end subroutine sample_photon_reaction
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_ELECTRON_REACTION terminates the particle and either deposits all
|
||||
! energy locally (electron_treatment = ELECTRON_LED) or creates secondary
|
||||
! bremsstrahlung photons from electron deflections with charged particles
|
||||
! (electron_treatment = ELECTRON_TTB).
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_electron_reaction(p)
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
real(8) :: E_lost ! energy lost to bremsstrahlung photons
|
||||
|
||||
! TODO: create reaction types
|
||||
|
||||
if (electron_treatment == ELECTRON_TTB) then
|
||||
call thick_target_bremsstrahlung(p, E_lost)
|
||||
end if
|
||||
|
||||
p % E = ZERO
|
||||
p % alive = .false.
|
||||
|
||||
end subroutine sample_electron_reaction
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_POSITRON_REACTION terminates the particle and either deposits all
|
||||
! energy locally (electron_treatment = ELECTRON_LED) or creates secondary
|
||||
! bremsstrahlung photons from electron deflections with charged particles
|
||||
! (electron_treatment = ELECTRON_TTB). Two annihilation photons of energy
|
||||
! MASS_ELECTRON_EV (0.511 MeV) are created and travel in opposite directions.
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_positron_reaction(p)
|
||||
type(Particle), intent(inout) :: p
|
||||
|
||||
real(8) :: mu ! scattering cosine
|
||||
real(8) :: phi ! azimuthal angle
|
||||
real(8) :: uvw(3) ! new direction
|
||||
|
||||
real(8) :: E_lost ! energy lost to bremsstrahlung photons
|
||||
|
||||
! TODO: create reaction types
|
||||
|
||||
if (electron_treatment == ELECTRON_TTB) then
|
||||
call thick_target_bremsstrahlung(p, E_lost)
|
||||
end if
|
||||
|
||||
! Sample angle isotropically
|
||||
mu = TWO*prn() - ONE
|
||||
phi = TWO*PI*prn()
|
||||
uvw(1) = mu
|
||||
uvw(2) = sqrt(ONE - mu*mu)*cos(phi)
|
||||
uvw(3) = sqrt(ONE - mu*mu)*sin(phi)
|
||||
|
||||
! Create annihilation photon pair traveling in opposite directions
|
||||
call particle_create_secondary(p, uvw, MASS_ELECTRON_EV, PHOTON, .true._C_BOOL)
|
||||
call particle_create_secondary(p, -uvw, MASS_ELECTRON_EV, PHOTON, .true._C_BOOL)
|
||||
|
||||
p % E = ZERO
|
||||
p % alive = .false.
|
||||
|
||||
end subroutine sample_positron_reaction
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_NUCLIDE
|
||||
|
|
@ -169,7 +426,7 @@ contains
|
|||
|
||||
! Check to make sure that a nuclide was sampled
|
||||
if (i_nuc_mat > mat % n_nuclides) then
|
||||
call p % write_restart()
|
||||
call particle_write_restart(p)
|
||||
call fatal_error("Did not sample any nuclide during collision.")
|
||||
end if
|
||||
|
||||
|
|
@ -194,6 +451,49 @@ contains
|
|||
|
||||
end subroutine sample_nuclide
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_ELEMENT
|
||||
!===============================================================================
|
||||
|
||||
function sample_element(p) result(i_element)
|
||||
type(Particle), intent(in) :: p
|
||||
integer :: i_element
|
||||
|
||||
integer :: i
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
real(8) :: atom_density ! atom density of nuclide in atom/b-cm
|
||||
real(8) :: sigma ! microscopic total xs for nuclide
|
||||
|
||||
associate (mat => materials(p % material))
|
||||
! Sample cumulative distribution function
|
||||
cutoff = prn() * material_xs % total
|
||||
|
||||
i = 0
|
||||
prob = ZERO
|
||||
do while (prob < cutoff)
|
||||
i = i + 1
|
||||
|
||||
! Check to make sure that a nuclide was sampled
|
||||
if (i > mat % n_nuclides) then
|
||||
call particle_write_restart(p)
|
||||
call fatal_error("Did not sample any element during collision.")
|
||||
end if
|
||||
|
||||
! Find atom density
|
||||
i_element = mat % element(i)
|
||||
atom_density = mat % atom_density(i)
|
||||
|
||||
! Determine microscopic cross section
|
||||
sigma = atom_density * micro_photon_xs(i_element) % total
|
||||
|
||||
! Increment probability to compare to cutoff
|
||||
prob = prob + sigma
|
||||
end do
|
||||
end associate
|
||||
|
||||
end function sample_element
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_FISSION
|
||||
!===============================================================================
|
||||
|
|
@ -260,6 +560,65 @@ contains
|
|||
|
||||
end subroutine sample_fission
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_PHOTON_PRODUCT
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_photon_product(i_nuclide, E, i_reaction, i_product)
|
||||
integer, intent(in) :: i_nuclide ! index in nuclides array
|
||||
real(8), intent(in) :: E ! energy of neutron
|
||||
integer, intent(out) :: i_reaction ! index in nuc % reactions array
|
||||
integer, intent(out) :: i_product ! index in reaction % products array
|
||||
|
||||
integer :: i_grid
|
||||
integer :: i_temp
|
||||
integer :: threshold
|
||||
integer :: last_valid_reaction
|
||||
integer :: last_valid_product
|
||||
real(8) :: f
|
||||
real(8) :: prob
|
||||
real(8) :: cutoff
|
||||
real(8) :: yield
|
||||
|
||||
! Get pointer to nuclide
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
||||
! Get grid index and interpolation factor and sample photon production cdf
|
||||
i_temp = micro_xs(i_nuclide) % index_temp
|
||||
i_grid = micro_xs(i_nuclide) % index_grid
|
||||
f = micro_xs(i_nuclide) % interp_factor
|
||||
cutoff = prn() * micro_xs(i_nuclide) % photon_prod
|
||||
prob = ZERO
|
||||
|
||||
! 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
|
||||
|
||||
! 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
|
||||
! 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
|
||||
|
||||
if (prob > cutoff) return
|
||||
last_valid_reaction = i_reaction
|
||||
last_valid_product = i_product
|
||||
end if
|
||||
end do
|
||||
end associate
|
||||
end do REACTION_LOOP
|
||||
end associate
|
||||
|
||||
i_reaction = last_valid_reaction
|
||||
i_product = last_valid_product
|
||||
|
||||
end subroutine sample_photon_product
|
||||
|
||||
!===============================================================================
|
||||
! ABSORPTION
|
||||
!===============================================================================
|
||||
|
|
@ -386,7 +745,7 @@ contains
|
|||
|
||||
! Check to make sure inelastic scattering reaction sampled
|
||||
if (i > size(nuc % reactions)) then
|
||||
call p % write_restart()
|
||||
call particle_write_restart(p)
|
||||
call fatal_error("Did not sample any reaction for nuclide " &
|
||||
&// trim(nuc % name))
|
||||
end if
|
||||
|
|
@ -1093,7 +1452,7 @@ contains
|
|||
! Determine indices on ufs mesh for current location
|
||||
call m % get_bin(p % coord(1) % xyz, mesh_bin)
|
||||
if (mesh_bin == NO_BIN_FOUND) then
|
||||
call p % write_restart()
|
||||
call particle_write_restart(p)
|
||||
call fatal_error("Source site outside UFS mesh!")
|
||||
end if
|
||||
|
||||
|
|
@ -1145,6 +1504,9 @@ contains
|
|||
! Bank source neutrons by copying particle data
|
||||
bank_array(i) % xyz = p % coord(1) % xyz
|
||||
|
||||
! Set particle as neutron
|
||||
bank_array(i) % particle = NEUTRON
|
||||
|
||||
! Set weight of fission bank site
|
||||
bank_array(i) % wgt = ONE/weight
|
||||
|
||||
|
|
@ -1241,12 +1603,12 @@ contains
|
|||
call rxn % products(1 + group) % sample(E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max_neutron) exit
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
||||
! check for large number of resamples
|
||||
n_sample = n_sample + 1
|
||||
if (n_sample == MAX_SAMPLE) then
|
||||
! call p % write_restart()
|
||||
! call particle_write_restart(p)
|
||||
call fatal_error("Resampled energy distribution maximum number of " &
|
||||
// "times for nuclide " // nuc % name)
|
||||
end if
|
||||
|
|
@ -1265,12 +1627,12 @@ contains
|
|||
call rxn % products(1) % sample(E_in, site % E, mu)
|
||||
|
||||
! resample if energy is greater than maximum neutron energy
|
||||
if (site % E < energy_max_neutron) exit
|
||||
if (site % E < energy_max(NEUTRON)) exit
|
||||
|
||||
! check for large number of resamples
|
||||
n_sample = n_sample + 1
|
||||
if (n_sample == MAX_SAMPLE) then
|
||||
! call p % write_restart()
|
||||
! call particle_write_restart(p)
|
||||
call fatal_error("Resampled energy distribution maximum number of " &
|
||||
// "times for nuclide " // nuc % name)
|
||||
end if
|
||||
|
|
@ -1334,7 +1696,8 @@ contains
|
|||
if (mod(yield, ONE) == ZERO) then
|
||||
! If yield is integral, create exactly that many secondary particles
|
||||
do i = 1, nint(yield) - 1
|
||||
call p % create_secondary(p % coord(1) % uvw, NEUTRON, run_CE=.true.)
|
||||
call particle_create_secondary(p, p % coord(1) % uvw, p % E, &
|
||||
NEUTRON, run_CE=.true._C_BOOL)
|
||||
end do
|
||||
else
|
||||
! Otherwise, change weight of particle based on yield
|
||||
|
|
@ -1343,4 +1706,50 @@ contains
|
|||
|
||||
end subroutine inelastic_scatter
|
||||
|
||||
!===============================================================================
|
||||
! SAMPLE_SECONDARY_PHOTONS
|
||||
!===============================================================================
|
||||
|
||||
subroutine sample_secondary_photons(p, i_nuclide)
|
||||
type(Particle), intent(inout) :: p
|
||||
integer, intent(in) :: i_nuclide
|
||||
|
||||
integer :: i_reaction ! index in nuc % reactions array
|
||||
integer :: i_product ! index in nuc % reactions % products array
|
||||
|
||||
real(8) :: nu_t
|
||||
real(8) :: mu
|
||||
real(8) :: E
|
||||
real(8) :: uvw(3)
|
||||
integer :: nu
|
||||
integer :: i
|
||||
|
||||
! Sample the number of photons produced
|
||||
nu_t = p % wgt * micro_xs(i_nuclide) % photon_prod / &
|
||||
micro_xs(i_nuclide) % total
|
||||
if (prn() > nu_t - int(nu_t)) then
|
||||
nu = int(nu_t)
|
||||
else
|
||||
nu = int(nu_t) + 1
|
||||
end if
|
||||
|
||||
! Sample each secondary photon
|
||||
do i = 1, nu
|
||||
|
||||
! Sample the reaction and product
|
||||
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)
|
||||
|
||||
! Sample the new direction
|
||||
uvw = rotate_angle(p % coord(1) % uvw, mu)
|
||||
|
||||
! Create the secondary photon
|
||||
call particle_create_secondary(p, uvw, E, PHOTON, run_CE=.true._C_BOOL)
|
||||
end do
|
||||
|
||||
end subroutine sample_secondary_photons
|
||||
|
||||
end module physics
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module physics_mg
|
|||
use mgxs_interface
|
||||
use message_passing
|
||||
use nuclide_header, only: material_xs
|
||||
use particle_header, only: Particle
|
||||
use particle_header
|
||||
use physics_common
|
||||
use random_lcg, only: prn
|
||||
use settings
|
||||
|
|
@ -185,7 +185,7 @@ contains
|
|||
call m % get_bin(p % coord(1) % xyz, mesh_bin)
|
||||
|
||||
if (mesh_bin == NO_BIN_FOUND) then
|
||||
call p % write_restart()
|
||||
call particle_write_restart(p)
|
||||
call fatal_error("Source site outside UFS mesh!")
|
||||
end if
|
||||
|
||||
|
|
@ -237,6 +237,9 @@ contains
|
|||
! Bank source neutrons by copying particle data
|
||||
bank_array(i) % xyz = p % coord(1) % xyz
|
||||
|
||||
! Set particle as neutron
|
||||
bank_array(i) % particle = NEUTRON
|
||||
|
||||
! Set weight of fission bank site
|
||||
bank_array(i) % wgt = ONE/weight
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module plot
|
|||
use hdf5_interface
|
||||
use output, only: time_stamp
|
||||
use material_header, only: materials
|
||||
use particle_header, only: LocalCoord, Particle
|
||||
use particle_header
|
||||
use plot_header
|
||||
use progress_header, only: ProgressBar
|
||||
use settings, only: check_overlaps
|
||||
|
|
@ -161,7 +161,7 @@ contains
|
|||
end if
|
||||
|
||||
! allocate and initialize particle
|
||||
call p % initialize()
|
||||
call particle_initialize(p)
|
||||
p % coord(1) % xyz = xyz
|
||||
p % coord(1) % uvw = [ HALF, HALF, HALF ]
|
||||
p % coord(1) % universe = root_universe
|
||||
|
|
@ -388,7 +388,7 @@ contains
|
|||
ll = pl % origin - pl % width / TWO
|
||||
|
||||
! allocate and initialize particle
|
||||
call p % initialize()
|
||||
call particle_initialize(p)
|
||||
p % coord(1) % xyz = ll
|
||||
p % coord(1) % uvw = [ HALF, HALF, HALF ]
|
||||
p % coord(1) % universe = root_universe
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ namespace openmc {
|
|||
|
||||
|
||||
// Constants
|
||||
extern "C" const int N_STREAMS {5};
|
||||
extern "C" const int N_STREAMS {6};
|
||||
extern "C" const int STREAM_TRACKING {0};
|
||||
extern "C" const int STREAM_TALLIES {1};
|
||||
extern "C" const int STREAM_SOURCE {2};
|
||||
extern "C" const int STREAM_URR_PTABLE {3};
|
||||
extern "C" const int STREAM_VOLUME {4};
|
||||
extern "C" const int STREAM_PHOTON {5};
|
||||
|
||||
// Starting seed
|
||||
int64_t seed {1};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ extern "C" const int STREAM_TALLIES;
|
|||
extern "C" const int STREAM_SOURCE;
|
||||
extern "C" const int STREAM_URR_PTABLE;
|
||||
extern "C" const int STREAM_VOLUME;
|
||||
extern "C" const int STREAM_PHOTON;
|
||||
|
||||
//==============================================================================
|
||||
//! Generate a pseudo-random number using a linear congruential generator.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ element settings {
|
|||
element source {
|
||||
grammar {
|
||||
start =
|
||||
(element particle { xsd:string } | attribute particle { xsd:string })? &
|
||||
(element strength { xsd:double } | attribute strength { xsd:double })? &
|
||||
(element file { xsd:string } | attribute file { xsd:string })? &
|
||||
element space {
|
||||
|
|
|
|||
|
|
@ -274,6 +274,16 @@
|
|||
<grammar>
|
||||
<start>
|
||||
<interleave>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="particle">
|
||||
<data type="string"/>
|
||||
</element>
|
||||
<attribute name="particle">
|
||||
<data type="string"/>
|
||||
</attribute>
|
||||
</choice>
|
||||
</optional>
|
||||
<optional>
|
||||
<choice>
|
||||
<element name="strength">
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ module settings
|
|||
|
||||
! ============================================================================
|
||||
! ENERGY TREATMENT RELATED VARIABLES
|
||||
logical :: run_CE = .true. ! Run in CE mode?
|
||||
logical(C_BOOL) :: run_CE = .true. ! Run in CE mode?
|
||||
|
||||
! ============================================================================
|
||||
! CONTINUOUS-ENERGY CROSS SECTION RELATED VARIABLES
|
||||
|
|
@ -26,6 +26,9 @@ module settings
|
|||
|
||||
integer :: n_log_bins ! number of bins for logarithmic grid
|
||||
|
||||
logical :: photon_transport = .false.
|
||||
integer :: electron_treatment = ELECTRON_TTB
|
||||
|
||||
! ============================================================================
|
||||
! MULTI-GROUP CROSS SECTION RELATED VARIABLES
|
||||
|
||||
|
|
@ -71,7 +74,7 @@ module settings
|
|||
! Variance reduction settins
|
||||
logical :: survival_biasing = .false.
|
||||
real(8) :: weight_cutoff = 0.25_8
|
||||
real(8) :: energy_cutoff = ZERO
|
||||
real(8) :: energy_cutoff(4) = [ZERO, 1000.0_8, ZERO, ZERO]
|
||||
real(8) :: weight_survive = ONE
|
||||
|
||||
! Mode to run in (fixed source, eigenvalue, plotting, etc)
|
||||
|
|
|
|||
70
src/settings.cpp
Normal file
70
src/settings.cpp
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#include "settings.h"
|
||||
|
||||
#include "error.h"
|
||||
#include "openmc.h"
|
||||
#include "string_utils.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
char* openmc_path_input;
|
||||
char* openmc_path_statepoint;
|
||||
char* openmc_path_sourcepoint;
|
||||
char* openmc_path_particle_restart;
|
||||
std::string path_cross_sections;
|
||||
std::string path_multipole;
|
||||
std::string path_output;
|
||||
std::string path_source;
|
||||
|
||||
//==============================================================================
|
||||
// Functions
|
||||
//==============================================================================
|
||||
|
||||
void read_settings(pugi::xml_node* root)
|
||||
{
|
||||
// Look for deprecated cross_sections.xml file in settings.xml
|
||||
if (check_for_node(*root, "cross_sections")) {
|
||||
warning("Setting cross_sections in settings.xml has been deprecated."
|
||||
" The cross_sections are now set in materials.xml and the "
|
||||
"cross_sections input to materials.xml and the OPENMC_CROSS_SECTIONS"
|
||||
" environment variable will take precendent over setting "
|
||||
"cross_sections in settings.xml.");
|
||||
path_cross_sections = get_node_value(*root, "cross_sections");
|
||||
}
|
||||
|
||||
// Look for deprecated windowed_multipole file in settings.xml
|
||||
if (openmc_run_mode != RUN_MODE_PLOTTING) {
|
||||
if (check_for_node(*root, "multipole_library")) {
|
||||
warning("Setting multipole_library in settings.xml has been "
|
||||
"deprecated. The multipole_library is now set in materials.xml and"
|
||||
" the multipole_library input to materials.xml and the "
|
||||
"OPENMC_MULTIPOLE_LIBRARY environment variable will take "
|
||||
"precendent over setting multipole_library in settings.xml.");
|
||||
path_multipole = get_node_value(*root, "multipole_library");
|
||||
}
|
||||
if (!ends_with(path_multipole, "/")) {
|
||||
path_multipole += "/";
|
||||
}
|
||||
}
|
||||
|
||||
// Check for output options
|
||||
if (check_for_node(*root, "output")) {
|
||||
|
||||
// Get pointer to output node
|
||||
pugi::xml_node node_output = root->child("output");
|
||||
|
||||
// Set output directory if a path has been specified
|
||||
if (check_for_node(node_output, "path")) {
|
||||
path_output = get_node_value(node_output, "path");
|
||||
if (!ends_with(path_output, "/")) {
|
||||
path_output += "/";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
43
src/settings.h
Normal file
43
src/settings.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef OPENMC_SETTINGS_H
|
||||
#define OPENMC_SETTINGS_H
|
||||
|
||||
//! \file settings.h
|
||||
//! \brief Settings for OpenMC
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variable declarations
|
||||
//==============================================================================
|
||||
|
||||
// Defined on Fortran side
|
||||
extern "C" bool openmc_check_overlaps;
|
||||
extern "C" bool openmc_particle_restart_run;
|
||||
extern "C" bool openmc_restart_run;
|
||||
extern "C" bool openmc_write_all_tracks;
|
||||
|
||||
// Defined in .cpp
|
||||
// TODO: Make strings instead of char* once Fortran is gone
|
||||
extern "C" char* openmc_path_input;
|
||||
extern "C" char* openmc_path_statepoint;
|
||||
extern "C" char* openmc_path_sourcepoint;
|
||||
extern "C" char* openmc_path_particle_restart;
|
||||
extern std::string path_cross_sections;
|
||||
extern std::string path_multipole;
|
||||
extern std::string path_output;
|
||||
extern std::string path_source;
|
||||
|
||||
//==============================================================================
|
||||
//! Read settings from XML file
|
||||
//! \param[in] root XML node for <settings>
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void read_settings(pugi::xml_node* root);
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_SETTINGS_H
|
||||
|
|
@ -25,7 +25,8 @@ module simulation
|
|||
use output, only: header, print_columns, &
|
||||
print_batch_keff, print_generation, print_runtime, &
|
||||
print_results, print_overlap_check, write_tallies
|
||||
use particle_header, only: Particle
|
||||
use particle_header
|
||||
use photon_header, only: micro_photon_xs, n_elements
|
||||
use random_lcg, only: set_particle_seed
|
||||
use settings
|
||||
use simulation_header
|
||||
|
|
@ -141,7 +142,7 @@ contains
|
|||
integer :: i
|
||||
|
||||
! set defaults
|
||||
call p % initialize_from_source(source_bank(index_source), run_CE, &
|
||||
call particle_from_source(p, source_bank(index_source), run_CE, &
|
||||
energy_bin_avg)
|
||||
|
||||
! set identifier for particle
|
||||
|
|
@ -428,6 +429,7 @@ contains
|
|||
!$omp parallel
|
||||
! Allocate array for microscopic cross section cache
|
||||
allocate(micro_xs(n_nuclides))
|
||||
allocate(micro_photon_xs(n_elements))
|
||||
|
||||
! Allocate array for matching filter bins
|
||||
allocate(filter_matches(n_filters))
|
||||
|
|
@ -499,7 +501,7 @@ contains
|
|||
deallocate(materials(i) % mat_nuclide_index)
|
||||
end do
|
||||
!$omp parallel
|
||||
deallocate(micro_xs, filter_matches)
|
||||
deallocate(micro_xs, micro_photon_xs, filter_matches)
|
||||
!$omp end parallel
|
||||
|
||||
! Increment total number of generations
|
||||
|
|
|
|||
13
src/simulation.h
Normal file
13
src/simulation.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SIMULATION_H
|
||||
#define SIMULATION_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" int openmc_current_batch;
|
||||
extern "C" int openmc_current_gen;
|
||||
extern "C" int64_t openmc_current_work;
|
||||
extern "C" int openmc_n_lost_particles;
|
||||
|
||||
#pragma omp threadprivate(openmc_current_work)
|
||||
|
||||
#endif // SIMULATION_H
|
||||
|
|
@ -13,15 +13,15 @@ module simulation_header
|
|||
! GEOMETRY-RELATED VARIABLES
|
||||
|
||||
! Number of lost particles
|
||||
integer :: n_lost_particles = 0
|
||||
integer(C_INT), bind(C, name='openmc_n_lost_particles') :: n_lost_particles = 0
|
||||
|
||||
real(8) :: log_spacing ! spacing on logarithmic grid
|
||||
|
||||
! ============================================================================
|
||||
! SIMULATION VARIABLES
|
||||
|
||||
integer :: current_batch ! current batch
|
||||
integer :: current_gen ! current generation within a batch
|
||||
integer(C_INT), bind(C, name='openmc_current_batch') :: current_batch ! current batch
|
||||
integer(C_INT), bind(C, name='openmc_current_gen') :: current_gen ! current generation within a batch
|
||||
integer :: total_gen = 0 ! total number of generations simulated
|
||||
logical(C_BOOL), bind(C, name='openmc_simulation_initialized') :: &
|
||||
simulation_initialized = .false.
|
||||
|
|
@ -34,7 +34,7 @@ module simulation_header
|
|||
|
||||
integer(C_INT64_T), bind(C, name='openmc_work') :: work ! number of particles per processor
|
||||
integer(C_INT64_T), allocatable :: work_index(:) ! starting index in source bank for each process
|
||||
integer(8) :: current_work ! index in source bank of current history simulated
|
||||
integer(C_INT64_T), bind(C, name='openmc_current_work') :: current_work ! index in source bank of current history simulated
|
||||
|
||||
! ============================================================================
|
||||
! K-EIGENVALUE SIMULATION VARIABLES
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ module source_header
|
|||
use error
|
||||
use geometry, only: find_cell
|
||||
use material_header, only: materials
|
||||
use nuclide_header, only: energy_min_neutron, energy_max_neutron
|
||||
use particle_header, only: Particle
|
||||
use nuclide_header, only: energy_min, energy_max
|
||||
use particle_header
|
||||
use settings, only: photon_transport
|
||||
use string, only: to_lower
|
||||
use xml_interface
|
||||
|
||||
|
|
@ -29,6 +30,7 @@ module source_header
|
|||
!===============================================================================
|
||||
|
||||
type, public :: SourceDistribution
|
||||
integer :: particle ! particle type
|
||||
real(8) :: strength = ONE ! source strength
|
||||
class(SpatialDistribution), allocatable :: space ! spatial distribution
|
||||
class(UnitSphereDistribution), allocatable :: angle ! angle distribution
|
||||
|
|
@ -53,11 +55,27 @@ contains
|
|||
|
||||
integer :: n
|
||||
logical :: file_exists
|
||||
character(MAX_WORD_LEN) :: type
|
||||
character(MAX_WORD_LEN) :: type, temp_str
|
||||
type(XMLNode) :: node_space
|
||||
type(XMLNode) :: node_angle
|
||||
type(XMLNode) :: node_dist
|
||||
|
||||
! Check for particle type
|
||||
if (check_for_node(node, "particle")) then
|
||||
call get_node_value(node, "particle", temp_str)
|
||||
select case (to_lower(temp_str))
|
||||
case ('neutron')
|
||||
this % particle = NEUTRON
|
||||
case ('photon')
|
||||
this % particle = PHOTON
|
||||
photon_transport = .true.
|
||||
case default
|
||||
call fatal_error('Unknown source particle type: ' // trim(temp_str))
|
||||
end select
|
||||
else
|
||||
this % particle = NEUTRON
|
||||
end if
|
||||
|
||||
! Check for source strength
|
||||
if (check_for_node(node, "strength")) then
|
||||
call get_node_value(node, "strength", this % strength)
|
||||
|
|
@ -229,7 +247,10 @@ contains
|
|||
found = .false.
|
||||
do while (.not. found)
|
||||
! Set particle defaults
|
||||
call p % initialize()
|
||||
call particle_initialize(p)
|
||||
|
||||
! Set particle type
|
||||
site % particle = this % particle
|
||||
|
||||
! Sample spatial distribution
|
||||
site % xyz(:) = this % space % sample()
|
||||
|
|
@ -269,18 +290,18 @@ contains
|
|||
! Increment number of accepted samples
|
||||
n_accept = n_accept + 1
|
||||
|
||||
call p % clear()
|
||||
call particle_clear(p)
|
||||
|
||||
! Sample angle
|
||||
site % uvw(:) = this % angle % sample()
|
||||
|
||||
! Check for monoenergetic source above maximum neutron energy
|
||||
! Check for monoenergetic source above maximum particle energy
|
||||
select type (energy => this % energy)
|
||||
type is (Discrete)
|
||||
if (any(energy % x > energy_max_neutron)) then
|
||||
if (any(energy % x > energy_max(this % particle))) then
|
||||
call fatal_error("Source energy above range of energies of at least &
|
||||
&one cross section table")
|
||||
else if (any(energy % x < energy_min_neutron)) then
|
||||
else if (any(energy % x < energy_min(this % particle))) then
|
||||
call fatal_error("Source energy below range of energies of at least &
|
||||
&one cross section table")
|
||||
end if
|
||||
|
|
@ -290,8 +311,9 @@ contains
|
|||
! Sample energy spectrum
|
||||
site % E = this % energy % sample()
|
||||
|
||||
! Resample if energy falls outside minimum or maximum neutron energy
|
||||
if (site % E < energy_max_neutron .and. site % E > energy_min_neutron) exit
|
||||
! Resample if energy falls outside minimum or maximum particle energy
|
||||
if (site % E < energy_max(this % particle) .and. &
|
||||
site % E > energy_min(this % particle)) exit
|
||||
end do
|
||||
|
||||
! Set delayed group
|
||||
|
|
|
|||
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