mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-29 06:35:48 -04:00
Merge remote-tracking branch 'upstream/develop' into cpp_tallies
This commit is contained in:
commit
2d0ea1154b
109 changed files with 4104 additions and 3744 deletions
|
|
@ -318,14 +318,11 @@ add_library(libopenmc SHARED
|
|||
src/hdf5_interface.F90
|
||||
src/initialize.F90
|
||||
src/input_xml.F90
|
||||
src/list_header.F90
|
||||
src/material_header.F90
|
||||
src/math.F90
|
||||
src/mesh_header.F90
|
||||
src/message_passing.F90
|
||||
src/mgxs_data.F90
|
||||
src/mgxs_interface.F90
|
||||
src/multipole_header.F90
|
||||
src/nuclide_header.F90
|
||||
src/output.F90
|
||||
src/particle_header.F90
|
||||
|
|
@ -335,15 +332,12 @@ add_library(libopenmc SHARED
|
|||
src/random_lcg.F90
|
||||
src/reaction_header.F90
|
||||
src/relaxng
|
||||
src/sab_header.F90
|
||||
src/set_header.F90
|
||||
src/settings.F90
|
||||
src/simulation_header.F90
|
||||
src/simulation.F90
|
||||
src/state_point.F90
|
||||
src/stl_vector.F90
|
||||
src/string.F90
|
||||
src/summary.F90
|
||||
src/surface_header.F90
|
||||
src/timer_header.F90
|
||||
src/tracking.F90
|
||||
|
|
|
|||
|
|
@ -88,6 +88,14 @@ you care. This element has the following attributes/sub-elements:
|
|||
|
||||
*Default*: 0.0
|
||||
|
||||
--------------------------------
|
||||
``<dagmc>`` Element
|
||||
--------------------------------
|
||||
|
||||
When the DAGMC mode is enabled, the OpenMC geometry will be read from the file
|
||||
``dagmc.h5m``. If a :ref:`geometry.xml <io_geometry>` file is present with
|
||||
``dagmc`` set to ``true``, it will be ignored.
|
||||
|
||||
--------------------------------
|
||||
``<electron_treatment>`` Element
|
||||
--------------------------------
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ Coupling and Multi-physics
|
|||
<https://doi.org/10.1007/s41365-018-0539-1>`_," *Nucl. Sci. Tech.*, **30**
|
||||
(2019).
|
||||
|
||||
- April Novak, Paul Romano, Brycen Wendt, Ron Rahaman, Elia Merzari, Leslie
|
||||
Kerby, Cody Permann, Richard Martineau, and Rachel N. Slaybaugh, "Preliminary
|
||||
Coupling of OpenMC and Nek5000 within the MOOSE Framework," *Proc. PHYSOR*,
|
||||
Cancun, Mexico, Apr. 22-26 (2018).
|
||||
|
||||
- Jun Chen, Liangzhi Cao, Chuanqi Zhao, and Zhouyu Liu, "`Development of
|
||||
Subchannel Code SUBSC for high-fidelity multi-physics coupling application
|
||||
<https://doi.org/10.1016/j.egypro.2017.08.121>`_", *Energy Procedia*, **127**,
|
||||
|
|
@ -129,6 +134,10 @@ Geometry and Visualization
|
|||
Miscellaneous
|
||||
-------------
|
||||
|
||||
- Amanda L. Lund and Paul K. Romano, "`Implementation and Validation of Photon
|
||||
Transport in OpenMC <https://doi.org/10.2172/1490825>`_", Argonne National
|
||||
Laboratory, Technical Report ANL/MCS-TM-381 (2018).
|
||||
|
||||
- Bruno Merk, Dzianis Litskevich, R. Gregg, and A. R. Mount, "`Demand driven
|
||||
salt clean-up in a molten salt fast reactor -- Defining a priority list
|
||||
<https://doi.org/10.1371/journal.pone.0192020>`_", *PLOS One*, **13**,
|
||||
|
|
@ -191,6 +200,10 @@ Miscellaneous
|
|||
Multi-group Cross Section Generation
|
||||
------------------------------------
|
||||
|
||||
- Changho Lee and Yeon Sang Jung, "Verification of the Cross Section Library
|
||||
Generated Using OpenMC and MC\ :sup:`2`-3 for PROTEUS," *Proc. PHYSOR*, Cancun,
|
||||
Mexico, Apr. 22-26 (2018).
|
||||
|
||||
- Zhaoyuan Liu, Kord Smith, Benoit Forget, and Javier Ortensi, "`Cumulative
|
||||
migration method for computing rigorous diffusion coefficients and transport
|
||||
cross sections from Monte Carlo
|
||||
|
|
@ -454,3 +467,21 @@ Depletion
|
|||
- Kai Huang, Hongchun Wu, Yunzhao Li, and Liangzhi Cao, "Generalized depletion
|
||||
chain simplification based of significance analysis," *Proc. PHYSOR*, Sun
|
||||
Valley, Idaho, May 1-5, 2016.
|
||||
|
||||
--------------------
|
||||
Sensitivity Analysis
|
||||
--------------------
|
||||
|
||||
- Xingjie Peng, Jingang Liang, Benoit Forget, and Kord Smith, "`Calculation of
|
||||
adjoint-weighted reactor kinetics parameters in OpenMC
|
||||
<https://doi.org/10.1016/j.anucene.2019.01.007>`_", *Ann. Nucl. Energy*,
|
||||
**128**, 231-235 (2019).
|
||||
|
||||
- Zeyun Wu, Jingang Liang, Xingjie Peng, and Hany S. Abdel-Khalik, "`GPT-Free
|
||||
Sensitivity Analysis for Monte Carlo Models
|
||||
<https://doi.org/10.1080/00295450.2018.1556062>`_", *Nucl. Technol.* (2019).
|
||||
|
||||
- Xingjie Peng, Jingang Liang, Abdulla Alhajri, Benoit Forget, and Kord Smith,
|
||||
"`Development of continuous-energy sensitivity analysis capability in OpenMC
|
||||
<https://doi.org/10.1016/j.anucene.2017.06.061>`_", *Ann. Nucl. Energy*,
|
||||
**110**, 362-383 (2017).
|
||||
|
|
|
|||
|
|
@ -202,27 +202,20 @@ Coarse Mesh Finite Difference Acceleration
|
|||
------------------------------------------
|
||||
|
||||
CMFD is implemented in OpenMC and allows users to accelerate fission source
|
||||
convergence during inactive neutron batches. To run CMFD, the CMFDRun class
|
||||
should be used, and :mod:`from openmc import cmfd` should be included at the
|
||||
top of the Python input file. Additionally, this class has a dependence on the
|
||||
C API.
|
||||
convergence during inactive neutron batches. To use CMFD, the
|
||||
:class:`openmc.cmfd.CMFDRun` class executes OpenMC through the C API, solving
|
||||
the CMFD system between fission generations and modifying the source weights.
|
||||
Note that the :mod:`openmc.cmfd` module is not imported by default with the
|
||||
:mod:`openmc` namespace and needs to be imported explicitly.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myclass.rst
|
||||
|
||||
openmc.CMFDMesh
|
||||
openmc.CMFDRun
|
||||
|
||||
At the minimum, a CMFD mesh needs to be specified in order to run CMFD. Once
|
||||
these properties are set, an OpenMC simulation can be run with CMFD turned on
|
||||
with the function:
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
:nosignatures:
|
||||
:template: myfunction.rst
|
||||
|
||||
openmc.CMFDRun.run
|
||||
openmc.cmfd.CMFDMesh
|
||||
openmc.cmfd.CMFDRun
|
||||
|
||||
At the minimum, a CMFD mesh needs to be specified in order to run CMFD. Once the
|
||||
mesh and other optional properties are set, a simulation can be run with CMFD
|
||||
turned on using :meth:`openmc.cmfd.CMFDRun.run`.
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
.. module:: openmc.deplete
|
||||
|
||||
Two functions are provided that implement different time-integration algorithms
|
||||
for depletion calculations.
|
||||
Several functions are provided that implement different time-integration
|
||||
algorithms for depletion calculations, which are described in detail in Colin
|
||||
Josey's thesis, `Development and analysis of high order neutron
|
||||
transport-depletion coupling algorithms <http://hdl.handle.net/1721.1/113721>`_.
|
||||
|
||||
.. autosummary::
|
||||
:toctree: generated
|
||||
|
|
@ -16,6 +18,12 @@ for depletion calculations.
|
|||
|
||||
integrator.predictor
|
||||
integrator.cecm
|
||||
integrator.celi
|
||||
integrator.leqi
|
||||
integrator.cf4
|
||||
integrator.epc_rk4
|
||||
integrator.si_celi
|
||||
integrator.si_leqi
|
||||
|
||||
Each of these functions expects a "transport operator" to be passed. An operator
|
||||
specific to OpenMC is available using the following class:
|
||||
|
|
|
|||
|
|
@ -398,3 +398,32 @@ if needed, lattices, the last step is to create an instance of
|
|||
|
||||
.. _constructive solid geometry: http://en.wikipedia.org/wiki/Constructive_solid_geometry
|
||||
.. _quadratic surfaces: http://en.wikipedia.org/wiki/Quadric
|
||||
|
||||
--------------------------
|
||||
Using CAD-based Geometry
|
||||
--------------------------
|
||||
|
||||
OpenMC relies on the Direct Accelerated Geometry Monte Carlo toolkit (`DAGMC
|
||||
<https://svalinn.github.io/DAGMC/>`_) to represent CAD-based geometry in a
|
||||
surface mesh format. A DAGMC run can be enabled in OpenMC by setting the
|
||||
``dagmc`` property to ``True`` in the model Settings either via the Python
|
||||
:class:`openmc.settings` Python class::
|
||||
|
||||
settings = openmc.Settings()
|
||||
settings.dagmc = True
|
||||
|
||||
or in the :ref:`settings.xml <io_settings>` file::
|
||||
|
||||
<dagmc>true</dagmc>
|
||||
|
||||
With ``dagmc`` set to true, OpenMC will load the DAGMC model (from a local file
|
||||
named ``dagmc.h5m``) when initializing a simulation. If a `geometry.xml
|
||||
<../io_formats/geometry.html>`_ is present as well, it will be ignored.
|
||||
|
||||
**Note:** DAGMC geometries used in OpenMC are currently required to be clean,
|
||||
meaning that all surfaces have been `imprinted and merged
|
||||
<https://svalinn.github.io/DAGMC/usersguide/trelis_workflow.html>`_
|
||||
successfully and that the model is `watertight
|
||||
<https://svalinn.github.io/DAGMC/usersguide/tools.html#make-watertight>`_. Future
|
||||
implementations of DAGMC geometry will support small volume overlaps and
|
||||
un-merged surfaces.
|
||||
|
|
|
|||
|
|
@ -159,14 +159,25 @@ Prerequisites
|
|||
sudo apt install mpich libmpich-dev
|
||||
sudo apt install openmpi-bin libopenmpi-dev
|
||||
|
||||
* DAGMC_ toolkit for simulation using CAD-based geometries
|
||||
|
||||
OpenMC supports particle tracking in CAD-based geometries via the Direct
|
||||
Accelerated Geometry Monte Carlo (DAGMC) toolkit (`installation
|
||||
instructions
|
||||
<https://svalinn.github.io/DAGMC/install/dag_multiple.html>`_). For use in
|
||||
OpenMC, only the ``MOAB_DIR`` and ``BUILD_TALLY`` variables need to be
|
||||
specified in the CMake configuration step.
|
||||
|
|
||||
* git_ version control software for obtaining source code
|
||||
|
||||
|
||||
.. _gfortran: http://gcc.gnu.org/wiki/GFortran
|
||||
.. _gcc: https://gcc.gnu.org/
|
||||
.. _CMake: http://www.cmake.org
|
||||
.. _OpenMPI: http://www.open-mpi.org
|
||||
.. _MPICH: http://www.mpich.org
|
||||
.. _HDF5: https://www.hdfgroup.org/solutions/hdf5/
|
||||
.. _DAGMC: https://svalinn.github.io/DAGMC/index.html
|
||||
|
||||
Obtaining the Source
|
||||
--------------------
|
||||
|
|
@ -236,6 +247,12 @@ openmp
|
|||
Enables shared-memory parallelism using the OpenMP API. The Fortran compiler
|
||||
being used must support OpenMP. (Default: on)
|
||||
|
||||
dagmc
|
||||
Enables use of CAD-based DAGMC_ geometries. Please see the note about DAGMC in
|
||||
the optional dependencies list for more information on this feature. The
|
||||
installation directory for DAGMC should also be defined as `DAGMC_ROOT` in the
|
||||
CMake configuration command. (Default: off)
|
||||
|
||||
coverage
|
||||
Compile and link code instrumented for coverage analysis. This is typically
|
||||
used in conjunction with gcov_.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#ifdef __cplusplus
|
||||
#include "openmc/bank.h"
|
||||
|
||||
extern "C" {
|
||||
int openmc_fission_bank(openmc::Bank** ptr, int64_t* n);
|
||||
int openmc_source_bank(openmc::Bank** ptr, int64_t* n);
|
||||
|
|
@ -59,7 +60,7 @@ extern "C" {
|
|||
int openmc_init(int argc, char* argv[], const void* 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(const 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);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,15 @@ extern std::map<LibraryKey, std::size_t> library_map;
|
|||
|
||||
//! Read cross sections file (either XML or multigroup H5) and populate data
|
||||
//! libraries
|
||||
extern "C" void read_cross_sections_xml();
|
||||
void read_cross_sections_xml();
|
||||
|
||||
|
||||
//! Load nuclide and thermal scattering data from HDF5 files
|
||||
//
|
||||
//! \param[in] nuc_temps Temperatures for each nuclide in [K]
|
||||
//! \param[in] thermal_temps Temperatures for each thermal scattering table in [K]
|
||||
void read_ce_cross_sections(const std::vector<std::vector<double>>& nuc_temps,
|
||||
const std::vector<std::vector<double>>& thermal_temps);
|
||||
|
||||
//! Read cross_sections.xml and populate data libraries
|
||||
void read_ce_cross_sections_xml();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
#ifndef OPENMC_DAGMC_H
|
||||
#define OPENMC_DAGMC_H
|
||||
|
||||
namespace openmc {
|
||||
extern "C" const bool dagmc_enabled;
|
||||
}
|
||||
|
||||
#ifdef DAGMC
|
||||
|
||||
#include "DagMC.hpp"
|
||||
|
|
@ -10,10 +14,6 @@
|
|||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
namespace model {
|
||||
|
||||
extern moab::DagMC* DAG;
|
||||
|
|
@ -26,15 +26,11 @@ extern moab::DagMC* DAG;
|
|||
|
||||
extern "C" void load_dagmc_geometry();
|
||||
extern "C" void free_memory_dagmc();
|
||||
extern "C" pugi::xml_document* read_uwuw_materials();
|
||||
bool get_uwuw_materials_xml(std::string& s);
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // DAGMC
|
||||
|
||||
#endif // OPENMC_DAGMC_H
|
||||
|
||||
#ifdef DAGMC
|
||||
extern "C" constexpr bool dagmc_enabled = true;
|
||||
#else
|
||||
extern "C" constexpr bool dagmc_enabled = false;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -14,13 +14,38 @@ namespace openmc {
|
|||
//! Replace Universe, Lattice, and Material IDs with indices.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void adjust_indices();
|
||||
void adjust_indices();
|
||||
|
||||
//==============================================================================
|
||||
//! Assign defaults to cells with undefined temperatures.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void assign_temperatures();
|
||||
void assign_temperatures();
|
||||
|
||||
//==============================================================================
|
||||
//! \brief Obtain a list of temperatures that each nuclide/thermal scattering
|
||||
//! table appears at in the model. Later, this list is used to determine the
|
||||
//! actual temperatures to read (which may be different if interpolation is
|
||||
//! used)
|
||||
//!
|
||||
//! \param[out] nuc_temps Vector of temperatures for each nuclide
|
||||
//! \param[out] thermal_temps Vector of tempratures for each thermal scattering
|
||||
//! table
|
||||
//==============================================================================
|
||||
|
||||
void get_temperatures(std::vector<std::vector<double>>& nuc_temps,
|
||||
std::vector<std::vector<double>>& thermal_temps);
|
||||
|
||||
//==============================================================================
|
||||
//! \brief Perform final setup for geometry
|
||||
//!
|
||||
//! \param[out] nuc_temps Vector of temperatures for each nuclide
|
||||
//! \param[out] thermal_temps Vector of tempratures for each thermal scattering
|
||||
//! table
|
||||
//==============================================================================
|
||||
|
||||
void finalize_geometry(std::vector<std::vector<double>>& nuc_temps,
|
||||
std::vector<std::vector<double>>& thermal_temps);
|
||||
|
||||
//==============================================================================
|
||||
//! Figure out which Universe is the root universe.
|
||||
|
|
@ -36,7 +61,7 @@ extern "C" int32_t find_root_universe();
|
|||
//! Populate all data structures needed for distribcells.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void prepare_distribcell();
|
||||
void prepare_distribcell();
|
||||
|
||||
//==============================================================================
|
||||
//! Recursively search through the geometry and count cell instances.
|
||||
|
|
|
|||
|
|
@ -137,6 +137,13 @@ void read_attribute(hid_t obj_id, const char* name, T& buffer)
|
|||
read_attr(obj_id, name, H5TypeMap<T>::type_id, &buffer);
|
||||
}
|
||||
|
||||
// array version
|
||||
template<typename T, std::size_t N> inline void
|
||||
read_attribute(hid_t obj_id, const char* name, std::array<T, N>& buffer)
|
||||
{
|
||||
read_attr(obj_id, name, H5TypeMap<T>::type_id, buffer.data());
|
||||
}
|
||||
|
||||
// vector version
|
||||
template<typename T>
|
||||
void read_attribute(hid_t obj_id, const char* name, std::vector<T>& vec)
|
||||
|
|
@ -351,6 +358,12 @@ write_attribute(hid_t obj_id, const char* name, const char* buffer)
|
|||
write_attr_string(obj_id, name, buffer);
|
||||
}
|
||||
|
||||
inline void
|
||||
write_attribute(hid_t obj_id, const char* name, const std::string& buffer)
|
||||
{
|
||||
write_attr_string(obj_id, name, buffer.c_str());
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N> inline void
|
||||
write_attribute(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
||||
{
|
||||
|
|
@ -391,6 +404,29 @@ write_dataset(hid_t obj_id, const char* name, const std::array<T, N>& buffer)
|
|||
write_dataset(obj_id, 1, dims, name, H5TypeMap<T>::type_id, buffer.data(), false);
|
||||
}
|
||||
|
||||
inline void
|
||||
write_dataset(hid_t obj_id, const char* name, const std::vector<std::string>& buffer)
|
||||
{
|
||||
auto n {buffer.size()};
|
||||
hsize_t dims[] {n};
|
||||
|
||||
// Determine length of longest string, including \0
|
||||
size_t m = 1;
|
||||
for (const auto& s : buffer) {
|
||||
m = std::max(m, s.size() + 1);
|
||||
}
|
||||
|
||||
// Copy data into contiguous buffer
|
||||
char temp[n][m];
|
||||
std::fill(temp[0], temp[0] + n*m, '\0');
|
||||
for (int i = 0; i < n; ++i) {
|
||||
std::copy(buffer[i].begin(), buffer[i].end(), temp[i]);
|
||||
}
|
||||
|
||||
// Write 2D data
|
||||
write_string(obj_id, 1, dims, m, name, temp[0], false);
|
||||
}
|
||||
|
||||
template<typename T> inline void
|
||||
write_dataset(hid_t obj_id, const char* name, const std::vector<T>& buffer)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ int parse_command_line(int argc, char* argv[]);
|
|||
#ifdef OPENMC_MPI
|
||||
void initialize_mpi(MPI_Comm intracomm);
|
||||
#endif
|
||||
void read_input_xml();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
#define OPENMC_MATERIAL_H
|
||||
|
||||
#include <memory> // for unique_ptr
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <hdf5.h>
|
||||
#include "pugixml.hpp"
|
||||
#include "xtensor/xtensor.hpp"
|
||||
|
||||
#include "openmc/bremsstrahlung.h"
|
||||
#include "openmc/particle.h"
|
||||
|
|
@ -32,9 +35,57 @@ extern std::unordered_map<int32_t, int32_t> material_map;
|
|||
class Material
|
||||
{
|
||||
public:
|
||||
// Types
|
||||
struct ThermalTable {
|
||||
int index_table; //!< Index of table in data::thermal_scatt
|
||||
int index_nuclide; //!< Index in nuclide_
|
||||
double fraction; //!< How often to use table
|
||||
};
|
||||
|
||||
// Constructors
|
||||
Material() {};
|
||||
explicit Material(pugi::xml_node material_node);
|
||||
|
||||
// Methods
|
||||
void calculate_xs(const Particle& p) const;
|
||||
|
||||
//! Assign thermal scattering tables to specific nuclides within the material
|
||||
//! so the code knows when to apply bound thermal scattering data
|
||||
void init_thermal();
|
||||
|
||||
//! Set up mapping between global nuclides vector and indices in nuclide_
|
||||
void init_nuclide_index();
|
||||
|
||||
//! Finalize the material, assigning tables, normalize density, etc.
|
||||
void finalize();
|
||||
|
||||
//! Set total density of the material
|
||||
int set_density(double density, std::string units);
|
||||
|
||||
//! Write material data to HDF5
|
||||
void to_hdf5(hid_t group) const;
|
||||
|
||||
// Data
|
||||
int32_t id_; //!< Unique ID
|
||||
std::string name_; //!< Name of material
|
||||
std::vector<int> nuclide_; //!< Indices in nuclides vector
|
||||
std::vector<int> element_; //!< Indices in elements vector
|
||||
xt::xtensor<double, 1> atom_density_; //!< Nuclide atom density in [atom/b-cm]
|
||||
double density_; //!< Total atom density in [atom/b-cm]
|
||||
double density_gpcc_; //!< Total atom density in [g/cm^3]
|
||||
double volume_ {-1.0}; //!< Volume in [cm^3]
|
||||
bool fissionable {false}; //!< Does this material contain fissionable nuclides
|
||||
bool fissionable_ {false}; //!< Does this material contain fissionable nuclides
|
||||
bool depletable_ {false}; //!< Is the material depletable?
|
||||
std::vector<bool> p0_; //!< Indicate which nuclides are to be treated with iso-in-lab scattering
|
||||
|
||||
// To improve performance of tallying, we store an array (direct address
|
||||
// table) that indicates for each nuclide in data::nuclides the index of the
|
||||
// corresponding nuclide in the nuclide_ vector. If it is not present in the
|
||||
// material, the entry is set to -1.
|
||||
std::vector<int> mat_nuclide_index_;
|
||||
|
||||
// Thermal scattering tables
|
||||
std::vector<ThermalTable> thermal_tables_;
|
||||
|
||||
//! \brief Default temperature for cells containing this material.
|
||||
//!
|
||||
|
|
@ -43,12 +94,15 @@ public:
|
|||
|
||||
std::unique_ptr<Bremsstrahlung> ttb_;
|
||||
|
||||
Material() {};
|
||||
|
||||
explicit Material(pugi::xml_node material_node);
|
||||
|
||||
private:
|
||||
//! Initialize bremsstrahlung data
|
||||
void init_bremsstrahlung();
|
||||
|
||||
//! Normalize density
|
||||
void normalize_density();
|
||||
|
||||
void calculate_neutron_xs(const Particle& p) const;
|
||||
void calculate_photon_xs(const Particle& p) const;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -57,34 +57,25 @@ class Mgxs {
|
|||
//! @param in_fissionable Is this item fissionable or not.
|
||||
//! @param in_scatter_format Denotes whether Legendre, Tabular, or
|
||||
//! Histogram scattering is used.
|
||||
//! @param in_num_groups Number of energy groups.
|
||||
//! @param in_num_delayed_groups Number of delayed groups.
|
||||
//! @param in_is_isotropic Is this an isotropic or angular with respect to
|
||||
//! the incoming particle.
|
||||
//! @param in_polar Polar angle grid.
|
||||
//! @param in_azimuthal Azimuthal angle grid.
|
||||
void
|
||||
init(const std::string& in_name, double in_awr, const std::vector<double>& in_kTs,
|
||||
bool in_fissionable, int in_scatter_format, int in_num_groups,
|
||||
int in_num_delayed_groups, bool in_is_isotropic,
|
||||
bool in_fissionable, int in_scatter_format, bool in_is_isotropic,
|
||||
const std::vector<double>& in_polar, const std::vector<double>& in_azimuthal);
|
||||
|
||||
//! \brief Initializes the Mgxs object metadata from the HDF5 file
|
||||
//!
|
||||
//! @param xs_id HDF5 group id for the cross section data.
|
||||
//! @param in_num_groups Number of energy groups.
|
||||
//! @param in_num_delayed_groups Number of delayed groups.
|
||||
//! @param temperature Temperatures to read.
|
||||
//! @param tolerance Tolerance of temperature selection method.
|
||||
//! @param temps_to_read Resultant list of temperatures in the library
|
||||
//! to read which correspond to the requested temperatures.
|
||||
//! @param order_dim Resultant dimensionality of the scattering order.
|
||||
//! @param method Method of choosing nearest temperatures.
|
||||
void
|
||||
metadata_from_hdf5(hid_t xs_id, int in_num_groups,
|
||||
int in_num_delayed_groups, const std::vector<double>& temperature,
|
||||
double tolerance, std::vector<int>& temps_to_read, int& order_dim,
|
||||
int& method);
|
||||
metadata_from_hdf5(hid_t xs_id, const std::vector<double>& temperature,
|
||||
std::vector<int>& temps_to_read, int& order_dim);
|
||||
|
||||
//! \brief Performs the actual act of combining the microscopic data for a
|
||||
//! single temperature.
|
||||
|
|
@ -118,21 +109,8 @@ class Mgxs {
|
|||
//! \brief Constructor that loads the Mgxs object from the HDF5 file
|
||||
//!
|
||||
//! @param xs_id HDF5 group id for the cross section data.
|
||||
//! @param energy_groups Number of energy groups.
|
||||
//! @param delayed_groups Number of delayed groups.
|
||||
//! @param temperature Temperatures to read.
|
||||
//! @param tolerance Tolerance of temperature selection method.
|
||||
//! @param max_order Maximum order requested by the user;
|
||||
//! this is only used for Legendre scattering.
|
||||
//! @param legendre_to_tabular Flag to denote if any Legendre provided
|
||||
//! should be converted to a Tabular representation.
|
||||
//! @param legendre_to_tabular_points If a conversion is requested, this
|
||||
//! provides the number of points to use in the tabular representation.
|
||||
//! @param method Method of choosing nearest temperatures.
|
||||
Mgxs(hid_t xs_id, int energy_groups,
|
||||
int delayed_groups, const std::vector<double>& temperature, double tolerance,
|
||||
int max_order, bool legendre_to_tabular,
|
||||
int legendre_to_tabular_points, int& method);
|
||||
Mgxs(hid_t xs_id, const std::vector<double>& temperature);
|
||||
|
||||
//! \brief Constructor that initializes and populates all data to build a
|
||||
//! macroscopic cross section from microscopic cross section.
|
||||
|
|
@ -141,11 +119,8 @@ class Mgxs {
|
|||
//! @param mat_kTs temperatures (in units of eV) that data is needed.
|
||||
//! @param micros Microscopic objects to combine.
|
||||
//! @param atom_densities Atom densities of those microscopic quantities.
|
||||
//! @param tolerance Tolerance of temperature selection method.
|
||||
//! @param method Method of choosing nearest temperatures.
|
||||
Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
|
||||
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities,
|
||||
double tolerance, int& method);
|
||||
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities);
|
||||
|
||||
//! \brief Provides a cross section value given certain parameters
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace data {
|
|||
extern std::vector<Mgxs> nuclides_MG;
|
||||
extern std::vector<Mgxs> macro_xs;
|
||||
extern "C" int num_energy_groups;
|
||||
extern "C" int num_delayed_groups;
|
||||
extern std::vector<double> energy_bins;
|
||||
extern std::vector<double> energy_bin_avg;
|
||||
extern std::vector<double> rev_energy_bins;
|
||||
|
|
@ -30,19 +31,15 @@ extern std::vector<double> rev_energy_bins;
|
|||
// Mgxs data loading interface methods
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
add_mgxs_c(hid_t file_id, const char* name, int energy_groups,
|
||||
int delayed_groups, int n_temps, const double temps[], double tolerance,
|
||||
int max_order, bool legendre_to_tabular, int legendre_to_tabular_points,
|
||||
int& method);
|
||||
void read_mgxs();
|
||||
|
||||
extern "C" bool
|
||||
query_fissionable_c(int n_nuclides, const int i_nuclides[]);
|
||||
void
|
||||
add_mgxs(hid_t file_id, const std::string& name,
|
||||
const std::vector<double>& temperature);
|
||||
|
||||
extern "C" void
|
||||
create_macro_xs_c(const char* mat_name, int n_nuclides, const int i_nuclides[],
|
||||
int n_temps, const double temps[], const double atom_densities[],
|
||||
double tolerance, int& method);
|
||||
void create_macro_xs();
|
||||
|
||||
std::vector<std::vector<double>> get_mat_kTs();
|
||||
|
||||
extern "C" void read_mg_cross_sections_header_c(hid_t file_id);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <array>
|
||||
#include <memory> // for unique_ptr
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <hdf5.h>
|
||||
|
|
@ -96,7 +97,7 @@ public:
|
|||
};
|
||||
|
||||
// Constructors
|
||||
Nuclide(hid_t group, const double* temperature, int n, int i_nuclide);
|
||||
Nuclide(hid_t group, const std::vector<double>& temperature, int i_nuclide);
|
||||
|
||||
//! Initialize logarithmic grid for energy searches
|
||||
void init_grid();
|
||||
|
|
@ -168,6 +169,13 @@ private:
|
|||
static int XS_PHOTON_PROD;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Non-member functions
|
||||
//==============================================================================
|
||||
|
||||
//! Checks for the right version of nuclear data within HDF5 files
|
||||
void check_data_version(hid_t file_id);
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
|
@ -180,6 +188,7 @@ extern std::array<double, 2> energy_min;
|
|||
extern std::array<double, 2> energy_max;
|
||||
|
||||
extern std::vector<std::unique_ptr<Nuclide>> nuclides;
|
||||
extern std::unordered_map<std::string, int> nuclide_map;
|
||||
|
||||
} // namespace data
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ extern "C" void print_particle(Particle* p);
|
|||
//! Display plot information.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void print_plot();
|
||||
void print_plot();
|
||||
|
||||
//==============================================================================
|
||||
//! Display information regarding cell overlap checking.
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ constexpr double REL_MAX_LOST_PARTICLES {1.0e-6};
|
|||
|
||||
//! Particle types
|
||||
enum class ParticleType {
|
||||
neutron = 1, photon = 2, electron = 3, positron = 4
|
||||
neutron, photon, electron, positron
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ extern xt::xtensor<double, 1> compton_profile_pz; //! Compton profile momentum g
|
|||
|
||||
//! Photon interaction data for each element
|
||||
extern std::vector<PhotonInteraction> elements;
|
||||
extern std::unordered_map<std::string, int> element_map;
|
||||
|
||||
} // namespace data
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,12 @@ void sample_electron_reaction(Particle* p);
|
|||
//! MeV) are created and travel in opposite directions.
|
||||
void sample_positron_reaction(Particle* p);
|
||||
|
||||
void sample_nuclide(const Particle* p, int mt, int* i_nuclide, int* i_nuc_mat);
|
||||
//! Sample a nuclide based on their total cross sections and densities within
|
||||
//! the current material
|
||||
//!
|
||||
//! \param[in] p Particle
|
||||
//! \return Index in the data::nuclides vector
|
||||
int sample_nuclide(const Particle* p);
|
||||
|
||||
//! Determine the average total, prompt, and delayed neutrons produced from
|
||||
//! fission and creates appropriate bank sites.
|
||||
|
|
@ -52,7 +57,7 @@ void sample_photon_product(int i_nuclide, double E, int* i_rx, int* i_product);
|
|||
|
||||
void absorption(Particle* p, int i_nuclide);
|
||||
|
||||
void scatter(Particle*, int i_nuclide, int i_nuc_mat);
|
||||
void scatter(Particle*, int i_nuclide);
|
||||
|
||||
//! Treats the elastic scattering of a neutron with a target.
|
||||
void elastic_scatter(int i_nuclide, const Reaction* rx, double kT, double* E,
|
||||
|
|
|
|||
|
|
@ -140,8 +140,7 @@ class ScattDataLegendre: public ScattData {
|
|||
// Friend convert_legendre_to_tabular so it has access to protected
|
||||
// parameters
|
||||
friend void
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg,
|
||||
ScattDataTabular& tab, int n_mu);
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab);
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -223,8 +222,7 @@ class ScattDataTabular: public ScattData {
|
|||
// Friend convert_legendre_to_tabular so it has access to protected
|
||||
// parameters
|
||||
friend void
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg,
|
||||
ScattDataTabular& tab, int n_mu);
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab);
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ extern double weight_survive; //!< Survival weight after Russian roulette
|
|||
|
||||
//! Read settings from XML file
|
||||
//! \param[in] root XML node for <settings>
|
||||
extern "C" void read_settings_xml();
|
||||
void read_settings_xml();
|
||||
|
||||
extern "C" void read_settings_xml_f(pugi::xml_node_struct* root_ptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ extern "C" double keff_std; //!< standard deviation of average k
|
|||
extern "C" double k_col_abs; //!< sum over batches of k_collision * k_absorption
|
||||
extern "C" double k_col_tra; //!< sum over batches of k_collision * k_tracklength
|
||||
extern "C" double k_abs_tra; //!< sum over batches of k_absorption * k_tracklength
|
||||
extern "C" double log_spacing; //!< lethargy spacing for energy grid searches
|
||||
extern double log_spacing; //!< lethargy spacing for energy grid searches
|
||||
extern "C" int n_lost_particles; //!< cumulative number of lost particles
|
||||
extern "C" bool need_depletion_rx; //!< need to calculate depletion rx?
|
||||
extern "C" int restart_batch; //!< batch at which a restart job resumed
|
||||
|
|
|
|||
16
include/openmc/summary.h
Normal file
16
include/openmc/summary.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef OPENMC_SUMMARY_H
|
||||
#define OPENMC_SUMMARY_H
|
||||
|
||||
#include <hdf5.h>
|
||||
|
||||
namespace openmc {
|
||||
|
||||
void write_summary();
|
||||
void write_header(hid_t file);
|
||||
void write_nuclides(hid_t file);
|
||||
void write_geometry(hid_t file);
|
||||
void write_materials(hid_t file);
|
||||
|
||||
}
|
||||
|
||||
#endif // OPENMC_SUMMARY_H
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "xtensor/xtensor.hpp"
|
||||
|
|
@ -36,6 +37,7 @@ class ThermalScattering;
|
|||
|
||||
namespace data {
|
||||
extern std::vector<std::unique_ptr<ThermalScattering>> thermal_scatt;
|
||||
extern std::unordered_map<std::string, int> thermal_scatt_map;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ extern Timer time_bank_sendrecv;
|
|||
extern Timer time_finalize;
|
||||
extern Timer time_inactive;
|
||||
extern Timer time_initialize;
|
||||
extern Timer time_read_xs;
|
||||
extern Timer time_tallies;
|
||||
extern Timer time_total;
|
||||
extern Timer time_transport;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "hdf5.h"
|
||||
#include "xtensor/xtensor.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <complex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
|
@ -25,6 +26,9 @@ constexpr int FIT_S {0}; // Scattering
|
|||
constexpr int FIT_A {1}; // Absorption
|
||||
constexpr int FIT_F {2}; // Fission
|
||||
|
||||
// Multipole HDF5 file version
|
||||
constexpr std::array<int, 2> WMP_VERSION {1, 1};
|
||||
|
||||
//========================================================================
|
||||
// Windowed multipole data
|
||||
//========================================================================
|
||||
|
|
@ -68,6 +72,21 @@ public:
|
|||
xt::xtensor<bool, 1> broaden_poly_; //!< Whether to broaden curvefit
|
||||
};
|
||||
|
||||
//========================================================================
|
||||
// Non-member functions
|
||||
//========================================================================
|
||||
|
||||
//! Check to make sure WMP library data version matches
|
||||
//!
|
||||
//! \param[in] file HDF5 file object
|
||||
void check_wmp_version(hid_t file);
|
||||
|
||||
//! \brief Checks for the existence of a multipole library in the directory and
|
||||
//! loads it
|
||||
//!
|
||||
//! \param[in] i_nuclide Index in global nuclides array
|
||||
void read_multipole_data(int i_nuclide);
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // OPENMC_WMP_H
|
||||
|
|
|
|||
|
|
@ -24,50 +24,42 @@ class XsData {
|
|||
private:
|
||||
//! \brief Reads scattering data from the HDF5 file
|
||||
void
|
||||
scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
||||
int scatter_format, int final_scatter_format, int order_data,
|
||||
int max_order, int legendre_to_tabular_points);
|
||||
scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
int scatter_format, int final_scatter_format, int order_data);
|
||||
|
||||
//! \brief Reads fission data from the HDF5 file
|
||||
void
|
||||
fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
||||
size_t delayed_groups, bool is_isotropic);
|
||||
fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, bool is_isotropic);
|
||||
|
||||
//! \brief Reads fission data formatted as chi and nu-fission vectors from
|
||||
// the HDF5 file when beta is provided.
|
||||
void
|
||||
fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups, bool is_isotropic);
|
||||
fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, bool is_isotropic);
|
||||
|
||||
//! \brief Reads fission data formatted as chi and nu-fission vectors from
|
||||
// the HDF5 file when beta is not provided.
|
||||
void
|
||||
fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups);
|
||||
fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang);
|
||||
|
||||
//! \brief Reads fission data formatted as chi and nu-fission vectors from
|
||||
// the HDF5 file when no delayed data is provided.
|
||||
void
|
||||
fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups);
|
||||
fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang);
|
||||
|
||||
//! \brief Reads fission data formatted as a nu-fission matrix from
|
||||
// the HDF5 file when beta is provided.
|
||||
void
|
||||
fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups, bool is_isotropic);
|
||||
fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, bool is_isotropic);
|
||||
|
||||
//! \brief Reads fission data formatted as a nu-fission matrix from
|
||||
// the HDF5 file when beta is not provided.
|
||||
void
|
||||
fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups);
|
||||
fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang);
|
||||
|
||||
//! \brief Reads fission data formatted as a nu-fission matrix from
|
||||
// the HDF5 file when no delayed data is provided.
|
||||
void
|
||||
fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups);
|
||||
fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang);
|
||||
|
||||
public:
|
||||
|
||||
|
|
@ -106,8 +98,7 @@ class XsData {
|
|||
//! @param scatter_format The scattering representation of the file.
|
||||
//! @param n_pol Number of polar angles.
|
||||
//! @param n_azi Number of azimuthal angles.
|
||||
XsData(size_t num_groups, size_t num_delayed_groups, bool fissionable,
|
||||
int scatter_format, int n_pol, int n_azi);
|
||||
XsData(bool fissionable, int scatter_format, int n_pol, int n_azi);
|
||||
|
||||
//! \brief Loads the XsData object from the HDF5 file
|
||||
//!
|
||||
|
|
@ -118,20 +109,13 @@ class XsData {
|
|||
//! this is different from scatter_format if converting a Legendre to
|
||||
//! a tabular representation.
|
||||
//! @param order_data The dimensionality of the scattering data in the file.
|
||||
//! @param max_order Maximum order requested by the user;
|
||||
//! this is only used for Legendre scattering.
|
||||
//! @param legendre_to_tabular Flag to denote if any Legendre provided
|
||||
//! should be converted to a Tabular representation.
|
||||
//! @param legendre_to_tabular_points If a conversion is requested, this
|
||||
//! provides the number of points to use in the tabular representation.
|
||||
//! @param is_isotropic Is this an isotropic or angular with respect to
|
||||
//! the incoming particle.
|
||||
//! @param n_pol Number of polar angles.
|
||||
//! @param n_azi Number of azimuthal angles.
|
||||
void
|
||||
from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
|
||||
int final_scatter_format, int order_data, int max_order,
|
||||
int legendre_to_tabular_points, bool is_isotropic, int n_pol,
|
||||
int final_scatter_format, int order_data, bool is_isotropic, int n_pol,
|
||||
int n_azi);
|
||||
|
||||
//! \brief Combines the microscopic data to a macroscopic object.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from collections.abc import Mapping
|
||||
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER
|
||||
from ctypes import c_int, c_int32, c_double, c_char_p, POINTER, c_size_t
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
import numpy as np
|
||||
|
|
@ -48,6 +48,8 @@ _dll.openmc_material_set_id.errcheck = _error_handler
|
|||
_dll.openmc_material_set_volume.argtypes = [c_int32, c_double]
|
||||
_dll.openmc_material_set_volume.restype = c_int
|
||||
_dll.openmc_material_set_volume.errcheck = _error_handler
|
||||
_dll.n_materials.argtypes = []
|
||||
_dll.n_materials.restype = c_size_t
|
||||
|
||||
|
||||
class Material(_FortranObjectWithID):
|
||||
|
|
@ -161,7 +163,7 @@ class Material(_FortranObjectWithID):
|
|||
_dll.openmc_material_get_densities(self._index, nuclides, densities, n)
|
||||
|
||||
# Convert to appropriate types and return
|
||||
nuclide_list = [Nuclide(nuclides[i]).name for i in range(n.value)]
|
||||
nuclide_list = [Nuclide(nuclides[i] + 1).name for i in range(n.value)]
|
||||
density_array = as_array(densities, (n.value,))
|
||||
return nuclide_list, density_array
|
||||
|
||||
|
|
@ -228,7 +230,7 @@ class _MaterialMapping(Mapping):
|
|||
yield Material(index=i + 1).id
|
||||
|
||||
def __len__(self):
|
||||
return c_int32.in_dll(_dll, 'n_materials').value
|
||||
return _dll.n_materials()
|
||||
|
||||
def __repr__(self):
|
||||
return repr(dict(self))
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ _CURRENTS = {
|
|||
|
||||
|
||||
class CMFDMesh(object):
|
||||
""""A structured Cartesian mesh used for CMFD acceleration.
|
||||
"""A structured Cartesian mesh used for CMFD acceleration.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -199,14 +199,16 @@ class CMFDRun(object):
|
|||
display : dict
|
||||
Dictionary indicating which CMFD results to output. Note that CMFD
|
||||
k-effective will always be outputted. Acceptable keys are:
|
||||
* "balance" - Whether to output RMS [%] of the resdiual from the
|
||||
neutron balance equation on CMFD tallies (bool)
|
||||
* "dominance" - Whether to output the estimated dominance ratio from
|
||||
the CMFD iterations (bool)
|
||||
* "entropy" - Whether to output the *entropy* of the CMFD predicted
|
||||
fission source (bool)
|
||||
* "source" - Whether to ouput the RMS [%] between the OpenMC fission
|
||||
source and CMFD fission source (bool)
|
||||
|
||||
* "balance" - Whether to output RMS [%] of the resdiual from the
|
||||
neutron balance equation on CMFD tallies (bool)
|
||||
* "dominance" - Whether to output the estimated dominance ratio from
|
||||
the CMFD iterations (bool)
|
||||
* "entropy" - Whether to output the *entropy* of the CMFD predicted
|
||||
fission source (bool)
|
||||
* "source" - Whether to ouput the RMS [%] between the OpenMC fission
|
||||
source and CMFD fission source (bool)
|
||||
|
||||
downscatter : bool
|
||||
Indicate whether an effective downscatter cross section should be used
|
||||
when using 2-group CMFD.
|
||||
|
|
@ -215,7 +217,7 @@ class CMFDRun(object):
|
|||
of fission source neutrons on the next OpenMC batch. Defaults to False.
|
||||
cmfd_ktol : float
|
||||
Tolerance on the eigenvalue when performing CMFD power iteration
|
||||
mesh : openmc.CMFDMesh
|
||||
mesh : openmc.cmfd.CMFDMesh
|
||||
Structured mesh to be used for acceleration
|
||||
norm : float
|
||||
Normalization factor applied to the CMFD fission source distribution
|
||||
|
|
@ -244,16 +246,17 @@ class CMFDRun(object):
|
|||
``run_adjoint`` must be true for an adjoint calculation to be
|
||||
perfomed. Options are:
|
||||
|
||||
* "physical" - Create adjoint matrices from physical parameters of
|
||||
CMFD problem
|
||||
* "math" - Create adjoint matrices mathematically as the transpose of
|
||||
loss and production CMFD matrices
|
||||
* "physical" - Create adjoint matrices from physical parameters of
|
||||
CMFD problem
|
||||
* "math" - Create adjoint matrices mathematically as the transpose of
|
||||
loss and production CMFD matrices
|
||||
|
||||
indices : numpy.ndarray
|
||||
Stores spatial and group dimensions as [nx, ny, nz, ng]
|
||||
cmfd_src : numpy.ndarray
|
||||
CMFD source distribution calculated from solving CMFD equations
|
||||
entropy : list of floats
|
||||
"Shannon entropy" from cmfd fission source, stored for each generation
|
||||
"Shannon entropy" from CMFD fission source, stored for each generation
|
||||
that CMFD is invoked
|
||||
balance : list of floats
|
||||
RMS of neutron balance equations, stored for each generation that CMFD
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ Integrator
|
|||
The integrator subcomponents.
|
||||
"""
|
||||
|
||||
from .cf4 import *
|
||||
from .cecm import *
|
||||
from .celi import *
|
||||
from .cram import *
|
||||
from .epc_rk4 import *
|
||||
from .leqi import *
|
||||
from .predictor import *
|
||||
from .si_celi import *
|
||||
from .si_leqi import *
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ def cecm(operator, timesteps, power=None, power_density=None, print_out=True):
|
|||
r"""Deplete using the CE/CM algorithm.
|
||||
|
||||
Implements the second order `CE/CM predictor-corrector algorithm
|
||||
<https://doi.org/10.13182/NSE14-92>`_. This algorithm is mathematically
|
||||
defined as:
|
||||
<https://doi.org/10.13182/NSE14-92>`_.
|
||||
|
||||
"CE/CM" stands for constant extrapolation on predictor and constant
|
||||
midpoint on corrector. This algorithm is mathematically defined as:
|
||||
|
||||
.. math::
|
||||
y' &= A(y, t) y(t)
|
||||
|
|
@ -59,20 +61,16 @@ def cecm(operator, timesteps, power=None, power_density=None, print_out=True):
|
|||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
chain = operator.chain
|
||||
|
||||
# Initialize time
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
|
||||
# Initialize starting index for saving results
|
||||
if operator.prev_res is None:
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep concentrations and reaction rates
|
||||
# Avoid doing first transport run if already done in previous
|
||||
|
|
@ -95,10 +93,10 @@ def cecm(operator, timesteps, power=None, power_density=None, print_out=True):
|
|||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = p / power_res
|
||||
op_results[0].rates[0] *= ratio_power[0]
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
# Deplete for first half of timestep
|
||||
x_middle = deplete(chain, x[0], op_results[0], dt/2, print_out)
|
||||
x_middle = deplete(chain, x[0], op_results[0].rates, dt/2, print_out)
|
||||
|
||||
# Get middle-of-timestep reaction rates
|
||||
x.append(x_middle)
|
||||
|
|
@ -106,7 +104,7 @@ def cecm(operator, timesteps, power=None, power_density=None, print_out=True):
|
|||
|
||||
# Deplete for full timestep using beginning-of-step materials
|
||||
# and middle-of-timestep reaction rates
|
||||
x_end = deplete(chain, x[0], op_results[1], dt, print_out)
|
||||
x_end = deplete(chain, x[0], op_results[1].rates, dt, print_out)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
|
|
|
|||
166
openmc/deplete/integrator/celi.py
Normal file
166
openmc/deplete/integrator/celi.py
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
"""The CE/LI CFQ4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
|
||||
|
||||
# Functions to form the special matrix for depletion
|
||||
def _celi_f1(chain, rates):
|
||||
return 5/12 * chain.form_matrix(rates[0]) + \
|
||||
1/12 * chain.form_matrix(rates[1])
|
||||
|
||||
def _celi_f2(chain, rates):
|
||||
return 1/12 * chain.form_matrix(rates[0]) + \
|
||||
5/12 * chain.form_matrix(rates[1])
|
||||
|
||||
def celi(operator, timesteps, power=None, power_density=None,
|
||||
print_out=True):
|
||||
r"""Deplete using the CE/LI CFQ4 algorithm.
|
||||
|
||||
Implements the CE/LI Predictor-Corrector algorithm using the `fourth order
|
||||
commutator-free integrator <https://doi.org/10.1137/05063042>`_.
|
||||
|
||||
"CE/LI" stands for constant extrapolation on predictor and linear
|
||||
interpolation on corrector. This algorithm is mathematically defined as:
|
||||
|
||||
.. math::
|
||||
y' &= A(y, t) y(t)
|
||||
|
||||
A_0 &= A(y_n, t_n)
|
||||
|
||||
y_p &= \text{expm}(h A_0) y_n
|
||||
|
||||
A_1 &= A(y_p, t_n + h)
|
||||
|
||||
y_{n+1} &= \text{expm}(\frac{h}{12} A_0 + \frac{5h}{12} A1)
|
||||
\text{expm}(\frac{5h}{12} A_0 + \frac{h}{12} A1) y_n
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
vec, t, _ = celi_inner(operator, vec, p, i, i_res, t, dt,
|
||||
print_out)
|
||||
|
||||
# Perform one last simulation
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
||||
def celi_inner(operator, vec, p, i, i_res, t, dt, print_out):
|
||||
""" The inner loop of CE/LI CFQ4.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : Operator
|
||||
The operator object to simulate on.
|
||||
x : list of nuclide vector
|
||||
Nuclide vector, beginning of time.
|
||||
p : float
|
||||
Power of the reactor in [W]
|
||||
i : int
|
||||
Current iteration number.
|
||||
i_res : int
|
||||
Starting index, for restart calculation.
|
||||
t : float
|
||||
Time at start of step.
|
||||
dt : float
|
||||
Time step.
|
||||
print_out : bool
|
||||
Whether or not to print out time.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of numpy.array
|
||||
Nuclide vector, end of time.
|
||||
float
|
||||
Next time
|
||||
OperatorResult
|
||||
Operator result from beginning of step.
|
||||
"""
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
# Get beginning-of-timestep concentrations and reaction rates
|
||||
# Avoid doing first transport run if already done in previous
|
||||
# calculation
|
||||
if i > 0 or operator.prev_res is None:
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], p)]
|
||||
|
||||
else:
|
||||
# Get initial concentration
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Get rates
|
||||
op_results = [operator.prev_res[-1]]
|
||||
op_results[0].rates = op_results[0].rates[0]
|
||||
|
||||
# Set first stage value of keff
|
||||
op_results[0].k = op_results[0].k[0]
|
||||
|
||||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = p / power_res
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
# Deplete to end
|
||||
x_new = deplete(chain, x[0], op_results[0].rates, dt, print_out)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x[1], p))
|
||||
|
||||
# Deplete with two matrix exponentials
|
||||
rates = list(zip(op_results[0].rates, op_results[1].rates))
|
||||
x_end = deplete(chain, x[0], rates, dt, print_out,
|
||||
matrix_func=_celi_f1)
|
||||
x_end = deplete(chain, x_end, rates, dt, print_out,
|
||||
matrix_func=_celi_f2)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
|
||||
# return updated time and vectors
|
||||
return x_end, t + dt, op_results[0]
|
||||
161
openmc/deplete/integrator/cf4.py
Normal file
161
openmc/deplete/integrator/cf4.py
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
"""The CF4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
|
||||
|
||||
# Functions to form the special matrix for depletion
|
||||
def _cf4_f1(chain, rates):
|
||||
return 1/2 * chain.form_matrix(rates)
|
||||
|
||||
def _cf4_f2(chain, rates):
|
||||
return -1/2 * chain.form_matrix(rates[0]) + \
|
||||
chain.form_matrix(rates[1])
|
||||
|
||||
def _cf4_f3(chain, rates):
|
||||
return 1/4 * chain.form_matrix(rates[0]) + \
|
||||
1/6 * chain.form_matrix(rates[1]) + \
|
||||
1/6 * chain.form_matrix(rates[2]) + \
|
||||
-1/12 * chain.form_matrix(rates[3])
|
||||
|
||||
def _cf4_f4(chain, rates):
|
||||
return -1/12 * chain.form_matrix(rates[0]) + \
|
||||
1/6 * chain.form_matrix(rates[1]) + \
|
||||
1/6 * chain.form_matrix(rates[2]) + \
|
||||
1/4 * chain.form_matrix(rates[3])
|
||||
|
||||
def cf4(operator, timesteps, power=None, power_density=None, print_out=True):
|
||||
r"""Deplete using the CF4 algorithm.
|
||||
|
||||
Implements the fourth order `commutator-free Lie algorithm
|
||||
<https://doi.org/10.1016/S0167-739X(02)00161-9>`_.
|
||||
This algorithm is mathematically defined as:
|
||||
|
||||
.. math::
|
||||
F_1 &= h A(y_0)
|
||||
|
||||
y_1 &= \text{expm}(1/2 F_1) y_0
|
||||
|
||||
F_2 &= h A(y_1)
|
||||
|
||||
y_2 &= \text{expm}(1/2 F_2) y_0
|
||||
|
||||
F_3 &= h A(y_2)
|
||||
|
||||
y_3 &= \text{expm}(-1/2 F_1 + F_3) y_1
|
||||
|
||||
F_4 &= h A(y_3)
|
||||
|
||||
y_4 &= \text{expm}( 1/4 F_1 + 1/6 F_2 + 1/6 F_3 - 1/12 F_4)
|
||||
\text{expm}(-1/12 F_1 + 1/6 F_2 + 1/6 F_3 + 1/4 F_4) y_0
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep concentrations and reaction rates
|
||||
# Avoid doing first transport run if already done in previous
|
||||
# calculation
|
||||
if i > 0 or operator.prev_res is None:
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], p)]
|
||||
|
||||
else:
|
||||
# Get initial concentration
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Get rates
|
||||
op_results = [operator.prev_res[-1]]
|
||||
op_results[0].rates = op_results[0].rates[0]
|
||||
|
||||
# Set first stage value of keff
|
||||
op_results[0].k = op_results[0].k[0]
|
||||
|
||||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = p / power_res
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
# Step 1: deplete with matrix 1/2*A(y0)
|
||||
x_new = deplete(chain, x[0], op_results[0].rates, dt, print_out,
|
||||
matrix_func=_cf4_f1)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x_new, p))
|
||||
|
||||
# Step 2: deplete with matrix 1/2*A(y1)
|
||||
x_new = deplete(chain, x[0], op_results[1].rates, dt, print_out,
|
||||
matrix_func=_cf4_f1)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x_new, p))
|
||||
|
||||
# Step 3: deplete with matrix -1/2*A(y0)+A(y2)
|
||||
rates = list(zip(op_results[0].rates, op_results[2].rates))
|
||||
x_new = deplete(chain, x[1], rates, dt, print_out,
|
||||
matrix_func=_cf4_f2)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x_new, p))
|
||||
|
||||
# Step 4: deplete with two matrix exponentials
|
||||
rates = list(zip(op_results[0].rates, op_results[1].rates,
|
||||
op_results[2].rates, op_results[3].rates))
|
||||
x_end = deplete(chain, x[0], rates, dt, print_out,
|
||||
matrix_func=_cf4_f3)
|
||||
x_end = deplete(chain, x_end, rates, dt, print_out,
|
||||
matrix_func=_cf4_f4)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
|
||||
# Advance time, update vector
|
||||
t += dt
|
||||
vec = copy.deepcopy(x_end)
|
||||
|
||||
# Perform one last simulation
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
|
@ -14,7 +14,7 @@ import scipy.sparse.linalg as sla
|
|||
from .. import comm
|
||||
|
||||
|
||||
def deplete(chain, x, op_result, dt, print_out):
|
||||
def deplete(chain, x, rates, dt, print_out=True, matrix_func=None):
|
||||
"""Deplete materials using given reaction rates for a specified time
|
||||
|
||||
Parameters
|
||||
|
|
@ -23,12 +23,14 @@ def deplete(chain, x, op_result, dt, print_out):
|
|||
Depletion chain
|
||||
x : list of numpy.ndarray
|
||||
Atom number vectors for each material
|
||||
op_result : openmc.deplete.OperatorResult
|
||||
Result of applying transport operator (contains reaction rates)
|
||||
rates : openmc.deplete.ReactionRates
|
||||
Reaction rates (from transport operator)
|
||||
dt : float
|
||||
Time in [s] to deplete for
|
||||
print_out : bool
|
||||
print_out : bool, optional
|
||||
Whether to show elapsed time
|
||||
maxtrix_func : function, optional
|
||||
Function to form the depletion matrix
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -38,16 +40,9 @@ def deplete(chain, x, op_result, dt, print_out):
|
|||
"""
|
||||
t_start = time.time()
|
||||
|
||||
# Set up iterators
|
||||
n_mats = len(x)
|
||||
chains = repeat(chain, n_mats)
|
||||
vecs = (x[i] for i in range(n_mats))
|
||||
rates = (op_result.rates[i, :, :] for i in range(n_mats))
|
||||
dts = repeat(dt, n_mats)
|
||||
|
||||
# Use multiprocessing pool to distribute work
|
||||
with Pool() as pool:
|
||||
iters = zip(chains, vecs, rates, dts)
|
||||
iters = zip(repeat(chain), x, rates, repeat(dt), repeat(matrix_func))
|
||||
x_result = list(pool.starmap(_cram_wrapper, iters))
|
||||
|
||||
t_end = time.time()
|
||||
|
|
@ -58,12 +53,12 @@ def deplete(chain, x, op_result, dt, print_out):
|
|||
return x_result
|
||||
|
||||
|
||||
def _cram_wrapper(chain, n0, rates, dt):
|
||||
def _cram_wrapper(chain, n0, rates, dt, matrix_func=None):
|
||||
"""Wraps depletion matrix creation / CRAM solve for multiprocess execution
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chain : DepletionChain
|
||||
chain : openmc.deplete.Chain
|
||||
Depletion chain used to construct the burnup matrix
|
||||
n0 : numpy.array
|
||||
Vector to operate a matrix exponent on.
|
||||
|
|
@ -71,13 +66,19 @@ def _cram_wrapper(chain, n0, rates, dt):
|
|||
2D array indexed by nuclide then by cell.
|
||||
dt : float
|
||||
Time to integrate to.
|
||||
maxtrix_func : function, optional
|
||||
Function to form the depletion matrix
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.array
|
||||
Results of the matrix exponent.
|
||||
"""
|
||||
A = chain.form_matrix(rates)
|
||||
|
||||
if matrix_func is None:
|
||||
A = chain.form_matrix(rates)
|
||||
else:
|
||||
A = matrix_func(chain, rates)
|
||||
return CRAM48(A, n0, dt)
|
||||
|
||||
|
||||
|
|
|
|||
147
openmc/deplete/integrator/epc_rk4.py
Normal file
147
openmc/deplete/integrator/epc_rk4.py
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
"""The EPC-RK4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
|
||||
|
||||
# Functions to form the special matrix for depletion
|
||||
def _rk4_f1(chain, rates):
|
||||
return 1/2 * chain.form_matrix(rates)
|
||||
|
||||
def _rk4_f4(chain, rates):
|
||||
return 1/6 * chain.form_matrix(rates[0]) + \
|
||||
1/3 * chain.form_matrix(rates[1]) + \
|
||||
1/3 * chain.form_matrix(rates[2]) + \
|
||||
1/6 * chain.form_matrix(rates[3])
|
||||
|
||||
def epc_rk4(operator, timesteps, power=None, power_density=None, print_out=True):
|
||||
r"""Deplete using the EPC-RK4 algorithm.
|
||||
|
||||
Implements an extended predictor-corrector algorithm with traditional
|
||||
Runge-Kutta 4 method.
|
||||
This algorithm is mathematically defined as:
|
||||
|
||||
.. math::
|
||||
F_1 &= h A(y_0)
|
||||
|
||||
y_1 &= \text{expm}(1/2 F_1) y_0
|
||||
|
||||
F_2 &= h A(y_1)
|
||||
|
||||
y_2 &= \text{expm}(1/2 F_2) y_0
|
||||
|
||||
F_3 &= h A(y_2)
|
||||
|
||||
y_3 &= \text{expm}(F_3) y_0
|
||||
|
||||
F_4 &= h A(y_3)
|
||||
|
||||
y_4 &= \text{expm}(1/6 F_1 + 1/3 F_2 + 1/3 F_3 + 1/6 F_4) y_0
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep concentrations and reaction rates
|
||||
# Avoid doing first transport run if already done in previous
|
||||
# calculation
|
||||
if i > 0 or operator.prev_res is None:
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], p)]
|
||||
|
||||
else:
|
||||
# Get initial concentration
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Get rates
|
||||
op_results = [operator.prev_res[-1]]
|
||||
op_results[0].rates = op_results[0].rates[0]
|
||||
|
||||
# Set first stage value of keff
|
||||
op_results[0].k = op_results[0].k[0]
|
||||
|
||||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = p / power_res
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
# Step 1: deplete with matrix 1/2*A(y0)
|
||||
x_new = deplete(chain, x[0], op_results[0].rates, dt, print_out,
|
||||
matrix_func=_rk4_f1)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x[1], p))
|
||||
|
||||
# Step 2: deplete with matrix 1/2*A(y1)
|
||||
x_new = deplete(chain, x[0], op_results[1].rates, dt, print_out,
|
||||
matrix_func=_rk4_f1)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x[2], p))
|
||||
|
||||
# Step 3: deplete with matrix A(y2)
|
||||
x_new = deplete(chain, x[0], op_results[2].rates, dt, print_out)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x[3], p))
|
||||
|
||||
# Step 4: deplete with matrix 1/6*A(y0)+1/3*A(y1)+1/3*A(y2)+1/6*A(y3)
|
||||
rates = list(zip(op_results[0].rates, op_results[1].rates,
|
||||
op_results[2].rates, op_results[3].rates))
|
||||
x_end = deplete(chain, x[0], rates, dt, print_out,
|
||||
matrix_func=_rk4_f4)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
|
||||
# Advance time, update vector
|
||||
t += dt
|
||||
vec = copy.deepcopy(x_end)
|
||||
|
||||
# Perform one last simulation
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
170
openmc/deplete/integrator/leqi.py
Normal file
170
openmc/deplete/integrator/leqi.py
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
"""The LE/QI CFQ4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
from itertools import repeat
|
||||
|
||||
from .celi import celi_inner
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
|
||||
|
||||
# Functions to form the special matrix for depletion
|
||||
def _leqi_f1(chain, inputs):
|
||||
f1 = chain.form_matrix(inputs[0])
|
||||
f2 = chain.form_matrix(inputs[1])
|
||||
dt_l, dt = inputs[2], inputs[3]
|
||||
return -dt / (12 * dt_l) * f1 + (dt + 6 * dt_l) / (12 * dt_l) * f2
|
||||
|
||||
def _leqi_f2(chain, inputs):
|
||||
f1 = chain.form_matrix(inputs[0])
|
||||
f2 = chain.form_matrix(inputs[1])
|
||||
dt_l, dt = inputs[2], inputs[3]
|
||||
return -5 * dt / (12 * dt_l) * f1 + (5 * dt + 6 * dt_l) / (12 * dt_l) * f2
|
||||
|
||||
def _leqi_f3(chain, inputs):
|
||||
f1 = chain.form_matrix(inputs[0])
|
||||
f2 = chain.form_matrix(inputs[1])
|
||||
f3 = chain.form_matrix(inputs[2])
|
||||
dt_l, dt = inputs[3], inputs[4]
|
||||
return -dt**2 / (12 * dt_l * (dt + dt_l)) * f1 + \
|
||||
(dt**2 + 6*dt*dt_l + 5*dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + \
|
||||
dt_l / (12 * (dt + dt_l)) * f3
|
||||
|
||||
def _leqi_f4(chain, inputs):
|
||||
f1 = chain.form_matrix(inputs[0])
|
||||
f2 = chain.form_matrix(inputs[1])
|
||||
f3 = chain.form_matrix(inputs[2])
|
||||
dt_l, dt = inputs[3], inputs[4]
|
||||
return -dt**2 / (12 * dt_l * (dt + dt_l)) * f1 + \
|
||||
(dt**2 + 2*dt*dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + \
|
||||
(4 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f3
|
||||
|
||||
def leqi(operator, timesteps, power=None, power_density=None, print_out=True):
|
||||
r"""Deplete using the LE/QI CFQ4 algorithm.
|
||||
|
||||
Implements the LE/QI Predictor-Corrector algorithm using the `fourth order
|
||||
commutator-free integrator <https://doi.org/10.1137/05063042>`_.
|
||||
|
||||
"LE/QI" stands for linear extrapolation on predictor and quadratic
|
||||
interpolation on corrector. This algorithm is mathematically defined as:
|
||||
|
||||
.. math::
|
||||
y' &= A(y, t) y(t)
|
||||
|
||||
A_{last} &= A(y_{n-1}, t_n - h_1)
|
||||
|
||||
A_0 &= A(y_n, t_n)
|
||||
|
||||
F_1 &= \frac{-h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+h_2)}{12h_1} A_0
|
||||
|
||||
F_2 &= \frac{-5h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+5h_2)}{12h_1} A_0
|
||||
|
||||
y_p &= \text{expm}(F_2) \text{expm}(F_1) y_n
|
||||
|
||||
A_1 &= A(y_p, t_n + h_2)
|
||||
|
||||
F_3 &= \frac{-h_2^3}{12 h_1 (h_1 + h_2)} A_{last} +
|
||||
\frac{h_2 (5 h_1^2 + 6 h_2 h_1 + h_2^2)}{12 h_1 (h_1 + h_2)} A_0 +
|
||||
\frac{h_2 h_1)}{12 (h_1 + h_2)} A_1
|
||||
|
||||
F_4 &= \frac{-h_2^3}{12 h_1 (h_1 + h_2)} A_{last} +
|
||||
\frac{h_2 (h_1^2 + 2 h_2 h_1 + h_2^2)}{12 h_1 (h_1 + h_2)} A_0 +
|
||||
\frac{h_2 (5 h_1^2 + 4 h_2 h_1)}{12 h_1 (h_1 + h_2)} A_1
|
||||
|
||||
y_{n+1} &= \text{expm}(F_4) \text{expm}(F_3) y_n
|
||||
|
||||
It is initialized using the CE/LI algorithm.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# LE/QI needs the last step results to start
|
||||
# Perform CE/LI CFQ4 or restore results for the first step
|
||||
if i == 0:
|
||||
if i_res <= 1:
|
||||
dt_l = dt
|
||||
x_new, t, op_res_last = celi_inner(operator, vec, p, i,
|
||||
i_res, t, dt, print_out)
|
||||
continue
|
||||
else:
|
||||
dt_l = t - operator.prev_res[-2].time[0]
|
||||
op_res_last = operator.prev_res[-2]
|
||||
op_res_last.rates = op_res_last.rates[0]
|
||||
x_new = operator.prev_res[-1].data[0]
|
||||
|
||||
# Perform remaining LE/QI
|
||||
x = [copy.deepcopy(x_new)]
|
||||
op_results = [operator(x[0], p)]
|
||||
|
||||
inputs = list(zip(op_res_last.rates, op_results[0].rates,
|
||||
repeat(dt_l), repeat(dt)))
|
||||
x_new = deplete(chain, x[0], inputs, dt, print_out,
|
||||
matrix_func=_leqi_f1)
|
||||
x_new = deplete(chain, x_new, inputs, dt, print_out,
|
||||
matrix_func=_leqi_f2)
|
||||
x.append(x_new)
|
||||
op_results.append(operator(x[1], p))
|
||||
|
||||
inputs = list(zip(op_res_last.rates, op_results[0].rates,
|
||||
op_results[1].rates, repeat(dt_l), repeat(dt)))
|
||||
x_new = deplete(chain, x[0], inputs, dt, print_out,
|
||||
matrix_func=_leqi_f3)
|
||||
x_new = deplete(chain, x_new, inputs, dt, print_out,
|
||||
matrix_func=_leqi_f4)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t+dt], p, i_res+i)
|
||||
|
||||
# update results
|
||||
op_res_last = copy.deepcopy(op_results[0])
|
||||
t += dt
|
||||
dt_l = dt
|
||||
|
||||
# Perform one last simulation
|
||||
x = [copy.deepcopy(x_new)]
|
||||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
|
@ -55,20 +55,16 @@ def predictor(operator, timesteps, power=None, power_density=None,
|
|||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
chain = operator.chain
|
||||
|
||||
# Initialize time
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
|
||||
# Initialize starting index for saving results
|
||||
if operator.prev_res is None:
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res) - 1
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep concentrations and reaction rates
|
||||
# Avoid doing first transport run if already done in previous
|
||||
|
|
@ -90,10 +86,10 @@ def predictor(operator, timesteps, power=None, power_density=None,
|
|||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = p / power_res
|
||||
op_results[0].rates[0] *= ratio_power[0]
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
# Deplete for full timestep
|
||||
x_end = deplete(chain, x[0], op_results[0], dt, print_out)
|
||||
x_end = deplete(chain, x[0], op_results[0].rates, dt, print_out)
|
||||
|
||||
# Advance time, update vector
|
||||
t += dt
|
||||
|
|
|
|||
163
openmc/deplete/integrator/si_celi.py
Normal file
163
openmc/deplete/integrator/si_celi.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
"""The SI-CE/LI CFQ4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
from ..abc import OperatorResult
|
||||
from .celi import _celi_f1, _celi_f2
|
||||
|
||||
|
||||
def si_celi(operator, timesteps, power=None, power_density=None,
|
||||
print_out=True, m=10):
|
||||
r"""Deplete using the SI-CE/LI CFQ4 algorithm.
|
||||
|
||||
Implements the Stochastic Implicit CE/LI Predictor-Corrector algorithm using
|
||||
the `fourth order commutator-free integrator <https://doi.org/10.1137/05063042>`_.
|
||||
|
||||
Detailed algorithm can be found in Section 3.2 in `Colin Josey's thesis
|
||||
<http://hdl.handle.net/1721.1/113721>`_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
m : int, optional
|
||||
Number of stages.
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
# Get the concentrations and reaction rates for the first
|
||||
# beginning-of-timestep (BOS). Compute with m (stage number) times as
|
||||
# many neutrons as later simulations for statistics reasons if no
|
||||
# previous calculation results present
|
||||
if operator.prev_res is None:
|
||||
x = [copy.deepcopy(vec)]
|
||||
if hasattr(operator, "settings"):
|
||||
operator.settings.particles *= m
|
||||
op_results = [operator(x[0], power[0])]
|
||||
if hasattr(operator, "settings"):
|
||||
operator.settings.particles //= m
|
||||
else:
|
||||
# Get initial concentration
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Get rates
|
||||
op_results = [operator.prev_res[-1]]
|
||||
op_results[0].rates = op_results[0].rates[0]
|
||||
|
||||
# Set first stage value of keff
|
||||
op_results[0].k = op_results[0].k[0]
|
||||
|
||||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = power[0] / power_res
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
x, t, op_results = si_celi_inner(operator, x, op_results, p,
|
||||
i, i_res, t, dt, print_out, m)
|
||||
|
||||
# Create results for last point, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
||||
|
||||
def si_celi_inner(operator, x, op_results, p, i, i_res, t, dt, print_out, m=10):
|
||||
""" The inner loop of SI-CE/LI CFQ4.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : Operator
|
||||
The operator object to simulate on.
|
||||
x : list of nuclide vector
|
||||
Nuclide vector, beginning of time.
|
||||
op_results : list of OperatorResult
|
||||
Operator result at BOS.
|
||||
p : float
|
||||
Power of the reactor in [W]
|
||||
i : int
|
||||
Current iteration number.
|
||||
i_res : int
|
||||
Starting index, for restart calculation.
|
||||
t : float
|
||||
Time at start of step.
|
||||
dt : float
|
||||
Time step.
|
||||
print_out : bool
|
||||
Whether or not to print out time.
|
||||
m : int, optional
|
||||
Number of stages.
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of nuclide vector (numpy.array)
|
||||
Nuclide vector, end of time.
|
||||
float
|
||||
Next time
|
||||
list of OperatorResult
|
||||
Operator result at end of time.
|
||||
"""
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
# Deplete to end
|
||||
x_new = deplete(chain, x[0], op_results[0].rates, dt, print_out)
|
||||
x.append(x_new)
|
||||
|
||||
for j in range(m + 1):
|
||||
op_res = operator(x_new, p)
|
||||
|
||||
if j <= 1:
|
||||
op_res_bar = copy.deepcopy(op_res)
|
||||
else:
|
||||
rates = 1/j * op_res.rates + (1 - 1/j) * op_res_bar.rates
|
||||
k = 1/j * op_res.k + (1 - 1/j) * op_res_bar.k
|
||||
op_res_bar = OperatorResult(k, rates)
|
||||
|
||||
rates = list(zip(op_results[0].rates, op_res_bar.rates))
|
||||
x_new = deplete(chain, x[0], rates, dt, print_out,
|
||||
matrix_func=_celi_f1)
|
||||
x_new = deplete(chain, x_new, rates, dt, print_out,
|
||||
matrix_func=_celi_f2)
|
||||
|
||||
# Create results, write to disk
|
||||
op_results.append(op_res_bar)
|
||||
Results.save(operator, x, op_results, [t, t+dt], p, i_res+i)
|
||||
|
||||
# return updated time and vectors
|
||||
return [x_new], t + dt, [op_res_bar]
|
||||
151
openmc/deplete/integrator/si_leqi.py
Normal file
151
openmc/deplete/integrator/si_leqi.py
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
"""The SI-LE/QI CFQ4 integrator."""
|
||||
|
||||
import copy
|
||||
from collections.abc import Iterable
|
||||
from itertools import repeat
|
||||
|
||||
from .si_celi import si_celi_inner
|
||||
from .leqi import _leqi_f1, _leqi_f2, _leqi_f3, _leqi_f4
|
||||
from .cram import deplete
|
||||
from ..results import Results
|
||||
from ..abc import OperatorResult
|
||||
|
||||
|
||||
def si_leqi(operator, timesteps, power=None, power_density=None,
|
||||
print_out=True, m=10):
|
||||
r"""Deplete using the SI-LE/QI CFQ4 algorithm.
|
||||
|
||||
Implements the Stochastic Implicit LE/QI Predictor-Corrector algorithm using
|
||||
the `fourth order commutator-free integrator <https://doi.org/10.1137/05063042>`_.
|
||||
|
||||
Detailed algorithm can be found in Section 3.2 in `Colin Josey's thesis
|
||||
<http://hdl.handle.net/1721.1/113721>`_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
operator : openmc.deplete.TransportOperator
|
||||
The operator object to simulate on.
|
||||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not cumulative.
|
||||
power : float or iterable of float, optional
|
||||
Power of the reactor in [W]. A single value indicates that the power is
|
||||
constant over all timesteps. An iterable indicates potentially different
|
||||
power levels for each timestep. For a 2D problem, the power can be given
|
||||
in [W/cm] as long as the "volume" assigned to a depletion material is
|
||||
actually an area in [cm^2]. Either `power` or `power_density` must be
|
||||
specified.
|
||||
power_density : float or iterable of float, optional
|
||||
Power density of the reactor in [W/gHM]. It is multiplied by initial
|
||||
heavy metal inventory to get total power if `power` is not speficied.
|
||||
print_out : bool, optional
|
||||
Whether or not to print out time.
|
||||
m : int, optional
|
||||
Number of stages.
|
||||
"""
|
||||
if power is None:
|
||||
if power_density is None:
|
||||
raise ValueError(
|
||||
"Neither power nor power density was specified.")
|
||||
if not isinstance(power_density, Iterable):
|
||||
power = power_density*operator.heavy_metal
|
||||
else:
|
||||
power = [i*operator.heavy_metal for i in power_density]
|
||||
|
||||
if not isinstance(power, Iterable):
|
||||
power = [power]*len(timesteps)
|
||||
|
||||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
# Initialize time and starting index
|
||||
if operator.prev_res is None:
|
||||
t = 0.0
|
||||
i_res = 0
|
||||
else:
|
||||
t = operator.prev_res[-1].time[-1]
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
# Get the concentrations and reaction rates for the first
|
||||
# beginning-of-timestep (BOS). Compute with m (stage number) times as
|
||||
# many neutrons as later simulations for statistics reasons if no
|
||||
# previous calculation results present
|
||||
if operator.prev_res is None:
|
||||
x = [copy.deepcopy(vec)]
|
||||
if hasattr(operator, "settings"):
|
||||
operator.settings.particles *= m
|
||||
op_results = [operator(x[0], power[0])]
|
||||
if hasattr(operator, "settings"):
|
||||
operator.settings.particles //= m
|
||||
else:
|
||||
# Get initial concentration
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Get rates
|
||||
op_results = [operator.prev_res[-1]]
|
||||
op_results[0].rates = op_results[0].rates[0]
|
||||
|
||||
# Set first stage value of keff
|
||||
op_results[0].k = op_results[0].k[0]
|
||||
|
||||
# Scale reaction rates by ratio of powers
|
||||
power_res = operator.prev_res[-1].power
|
||||
ratio_power = power[0] / power_res
|
||||
op_results[0].rates *= ratio_power[0]
|
||||
|
||||
chain = operator.chain
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# LE/QI needs the last step results to start
|
||||
# Perform SI-CE/LI CFQ4 or restore results for the first step
|
||||
if i == 0:
|
||||
dt_l = dt
|
||||
if i_res <= 1:
|
||||
op_res_last = copy.deepcopy(op_results[0])
|
||||
x, t, op_results = si_celi_inner(operator, x, op_results, p,
|
||||
i, i_res, t, dt, print_out)
|
||||
continue
|
||||
else:
|
||||
dt_l = t - operator.prev_res[-2].time[0]
|
||||
op_res_last = operator.prev_res[-2]
|
||||
op_res_last.rates = op_res_last.rates[0]
|
||||
x = [operator.prev_res[-1].data[0]]
|
||||
|
||||
# Perform remaining LE/QI
|
||||
inputs = list(zip(op_res_last.rates, op_results[0].rates,
|
||||
repeat(dt_l), repeat(dt)))
|
||||
x_new = deplete(chain, x[0], inputs, dt, print_out,
|
||||
matrix_func=_leqi_f1)
|
||||
x_new = deplete(chain, x_new, inputs, dt, print_out,
|
||||
matrix_func=_leqi_f2)
|
||||
x.append(x_new)
|
||||
|
||||
# Loop on inner
|
||||
for j in range(m + 1):
|
||||
op_res = operator(x_new, p)
|
||||
|
||||
if j <= 1:
|
||||
op_res_bar = copy.deepcopy(op_res)
|
||||
else:
|
||||
rates = 1/j * op_res.rates + (1 - 1/j) * op_res_bar.rates
|
||||
k = 1/j * op_res.k + (1 - 1/j) * op_res_bar.k
|
||||
op_res_bar = OperatorResult(k, rates)
|
||||
|
||||
inputs = list(zip(op_res_last.rates, op_results[0].rates,
|
||||
op_res_bar.rates, repeat(dt_l), repeat(dt)))
|
||||
x_new = deplete(chain, x[0], inputs, dt, print_out,
|
||||
matrix_func=_leqi_f3)
|
||||
x_new = deplete(chain, x_new, inputs, dt, print_out,
|
||||
matrix_func=_leqi_f4)
|
||||
|
||||
# Create results, write to disk
|
||||
op_results.append(op_res_bar)
|
||||
Results.save(operator, x, op_results, [t, t+dt], p, i_res+i)
|
||||
|
||||
# update results
|
||||
x = [x_new]
|
||||
op_res_last = copy.deepcopy(op_results[0])
|
||||
op_results = [op_res_bar]
|
||||
t += dt
|
||||
dt_l = dt
|
||||
|
||||
# Create results for last point, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res+len(timesteps))
|
||||
|
|
@ -125,6 +125,10 @@ class Operator(TransportOperator):
|
|||
else:
|
||||
self.prev_res = None
|
||||
|
||||
# Differentiate burnable materials with multiple instances
|
||||
if self.diff_burnable_mats:
|
||||
self._differentiate_burnable_mats()
|
||||
|
||||
# Clear out OpenMC, create task lists, distribute
|
||||
openmc.reset_auto_ids()
|
||||
self.burnable_mats, volume, nuclides = self._get_burnable_mats()
|
||||
|
|
@ -227,10 +231,6 @@ class Operator(TransportOperator):
|
|||
|
||||
"""
|
||||
|
||||
if self.diff_burnable_mats:
|
||||
# Automatically distribute burnable materials
|
||||
self._differentiate_burnable_mats()
|
||||
|
||||
burnable_mats = set()
|
||||
model_nuclides = set()
|
||||
volume = OrderedDict()
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class Model(object):
|
|||
for plot in plots:
|
||||
self._plots.append(plot)
|
||||
|
||||
def deplete(self, timesteps, power, chain_file=None, method='cecm',
|
||||
def deplete(self, timesteps, chain_file=None, method='cecm',
|
||||
**kwargs):
|
||||
"""Deplete model using specified timesteps/power
|
||||
|
||||
|
|
@ -131,17 +131,11 @@ class Model(object):
|
|||
timesteps : iterable of float
|
||||
Array of timesteps in units of [s]. Note that values are not
|
||||
cumulative.
|
||||
power : float or iterable of float
|
||||
Power of the reactor in [W]. A single value indicates that the power
|
||||
is constant over all timesteps. An iterable indicates potentially
|
||||
different power levels for each timestep. For a 2D problem, the
|
||||
power can be given in [W/cm] as long as the "volume" assigned to a
|
||||
depletion material is actually an area in [cm^2].
|
||||
chain_file : str, optional
|
||||
Path to the depletion chain XML file. Defaults to the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
|
||||
method : {'cecm', 'predictor'}
|
||||
Integration method used for depletion
|
||||
method : str
|
||||
Integration method used for depletion (e.g., 'cecm', 'predictor')
|
||||
**kwargs
|
||||
Keyword arguments passed to integration function (e.g.,
|
||||
:func:`openmc.deplete.integrator.cecm`)
|
||||
|
|
@ -156,12 +150,9 @@ class Model(object):
|
|||
op = dep.Operator(self.geometry, self.settings, chain_file)
|
||||
|
||||
# Perform depletion
|
||||
if method == 'predictor':
|
||||
dep.integrator.predictor(op, timesteps, power, **kwargs)
|
||||
elif method == 'cecm':
|
||||
dep.integrator.cecm(op, timesteps, power, **kwargs)
|
||||
else:
|
||||
check_value('method', method, ('cecm', 'predictor'))
|
||||
check_value('method', method, ('cecm', 'predictor', 'cf4', 'epc_rk4',
|
||||
'si_celi', 'si_leqi', 'celi', 'leqi'))
|
||||
getattr(dep.integrator, method)(op, timesteps, **kwargs)
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Export model to XML files."""
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ contains
|
|||
use geometry_header
|
||||
use material_header
|
||||
use photon_header
|
||||
use sab_header
|
||||
use settings
|
||||
use simulation_header
|
||||
use surface_header
|
||||
|
|
@ -74,6 +73,9 @@ contains
|
|||
subroutine free_memory_source() bind(C)
|
||||
end subroutine
|
||||
|
||||
subroutine free_memory_material() bind(C)
|
||||
end subroutine
|
||||
|
||||
subroutine free_memory_mesh() bind(C)
|
||||
end subroutine free_memory_mesh
|
||||
|
||||
|
|
@ -85,6 +87,9 @@ contains
|
|||
|
||||
subroutine free_memory_cmfd() bind(C)
|
||||
end subroutine free_memory_cmfd
|
||||
|
||||
subroutine sab_clear() bind(C)
|
||||
end subroutine
|
||||
end interface
|
||||
|
||||
call free_memory_geometry()
|
||||
|
|
@ -95,7 +100,7 @@ contains
|
|||
call free_memory_nuclide()
|
||||
call free_memory_photon()
|
||||
call free_memory_settings()
|
||||
call free_memory_sab()
|
||||
call sab_clear()
|
||||
call free_memory_source()
|
||||
call free_memory_mesh()
|
||||
call free_memory_tally()
|
||||
|
|
|
|||
|
|
@ -30,12 +30,10 @@ void thick_target_bremsstrahlung(Particle& p, double* E_lost)
|
|||
{
|
||||
if (p.material == MATERIAL_VOID) return;
|
||||
|
||||
// TODO: off-by-one
|
||||
int photon = static_cast<int>(ParticleType::photon) - 1;
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
if (p.E < settings::energy_cutoff[photon]) return;
|
||||
|
||||
// Get bremsstrahlung data for this material and particle type
|
||||
// TODO: off-by-one
|
||||
BremsstrahlungData* mat;
|
||||
if (p.type == static_cast<int>(ParticleType::positron)) {
|
||||
mat = &model::materials[p.material -1]->ttb_->positron;
|
||||
|
|
|
|||
|
|
@ -122,10 +122,10 @@ module constants
|
|||
|
||||
! Particle type
|
||||
integer, parameter :: &
|
||||
NEUTRON = 1, &
|
||||
PHOTON = 2, &
|
||||
ELECTRON = 3, &
|
||||
POSITRON = 4
|
||||
NEUTRON = 0, &
|
||||
PHOTON = 1, &
|
||||
ELECTRON = 2, &
|
||||
POSITRON = 3
|
||||
|
||||
! Angular distribution type
|
||||
integer, parameter :: &
|
||||
|
|
|
|||
|
|
@ -2,15 +2,27 @@
|
|||
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/container_util.h"
|
||||
#ifdef DAGMC
|
||||
#include "openmc/dagmc.h"
|
||||
#endif
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/file_utils.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/material.h"
|
||||
#include "openmc/message_passing.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/photon.h"
|
||||
#include "openmc/settings.h"
|
||||
#include "openmc/simulation.h"
|
||||
#include "openmc/string_utils.h"
|
||||
#include "openmc/thermal.h"
|
||||
#include "openmc/xml_interface.h"
|
||||
#include "openmc/wmp.h"
|
||||
|
||||
#include "pugixml.hpp"
|
||||
|
||||
#include <cstdlib> // for getenv
|
||||
#include <unordered_set>
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -81,15 +93,30 @@ extern "C" void read_mg_cross_sections_header();
|
|||
|
||||
void read_cross_sections_xml()
|
||||
{
|
||||
// Check if materials.xml exists
|
||||
pugi::xml_document doc;
|
||||
std::string filename = settings::path_input + "materials.xml";
|
||||
#ifdef DAGMC
|
||||
std::string s;
|
||||
bool found_uwuw_mats = false;
|
||||
if (settings::dagmc) {
|
||||
found_uwuw_mats = get_uwuw_materials_xml(s);
|
||||
}
|
||||
|
||||
if (found_uwuw_mats) {
|
||||
// if we found uwuw materials, load those
|
||||
doc.load_file(s.c_str());
|
||||
} else {
|
||||
#endif
|
||||
// Check if materials.xml exists
|
||||
if (!file_exists(filename)) {
|
||||
fatal_error("Material XML file '" + filename + "' does not exist.");
|
||||
}
|
||||
|
||||
// Parse materials.xml file
|
||||
pugi::xml_document doc;
|
||||
doc.load_file(filename.c_str());
|
||||
#ifdef DAGMC
|
||||
}
|
||||
#endif
|
||||
|
||||
auto root = doc.document_element();
|
||||
|
||||
// Find cross_sections.xml file -- the first place to look is the
|
||||
|
|
@ -136,9 +163,7 @@ void read_cross_sections_xml()
|
|||
int i = 0;
|
||||
for (const auto& lib : data::libraries) {
|
||||
for (const auto& name : lib.materials_) {
|
||||
std::string lower_name = name;
|
||||
to_lower(lower_name);
|
||||
LibraryKey key {lib.type_, lower_name};
|
||||
LibraryKey key {lib.type_, name};
|
||||
data::library_map.insert({key, i});
|
||||
}
|
||||
++i;
|
||||
|
|
@ -146,9 +171,7 @@ void read_cross_sections_xml()
|
|||
|
||||
// Check that 0K nuclides are listed in the cross_sections.xml file
|
||||
for (const auto& name : settings::res_scat_nuclides) {
|
||||
std::string lower_name = name;
|
||||
to_lower(lower_name);
|
||||
LibraryKey key {Library::Type::neutron, lower_name};
|
||||
LibraryKey key {Library::Type::neutron, name};
|
||||
if (data::library_map.find(key) == data::library_map.end()) {
|
||||
fatal_error("Could not find resonant scatterer " +
|
||||
name + " in cross_sections.xml file!");
|
||||
|
|
@ -156,6 +179,211 @@ void read_cross_sections_xml()
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" void nuclide_from_hdf5(hid_t group, const Nuclide* ptr,
|
||||
const double* temps, int n, int n_nuclide);
|
||||
|
||||
void
|
||||
read_ce_cross_sections(const std::vector<std::vector<double>>& nuc_temps,
|
||||
const std::vector<std::vector<double>>& thermal_temps)
|
||||
{
|
||||
std::unordered_set<std::string> already_read;
|
||||
|
||||
// Construct a vector of nuclide names because we haven't loaded nuclide data
|
||||
// yet, but we need to know the name of the i-th nuclide
|
||||
std::vector<std::string> nuclide_names(data::nuclide_map.size());
|
||||
std::vector<std::string> thermal_names(data::thermal_scatt_map.size());
|
||||
for (const auto& kv : data::nuclide_map) {
|
||||
nuclide_names[kv.second] = kv.first;
|
||||
}
|
||||
for (const auto& kv : data::thermal_scatt_map) {
|
||||
thermal_names[kv.second] = kv.first;
|
||||
}
|
||||
|
||||
// Read cross sections
|
||||
for (const auto& mat : model::materials) {
|
||||
for (int i_nuc : mat->nuclide_) {
|
||||
// Find name of corresponding nuclide. Because we haven't actually loaded
|
||||
// data, we don't have the name available, so instead we search through
|
||||
// all key/value pairs in nuclide_map
|
||||
std::string& name = nuclide_names[i_nuc];
|
||||
|
||||
// If we've already read this nuclide, skip it
|
||||
if (already_read.find(name) != already_read.end()) continue;
|
||||
|
||||
LibraryKey key {Library::Type::neutron, name};
|
||||
int idx = data::library_map[key];
|
||||
std::string& filename = data::libraries[idx].path_;
|
||||
|
||||
write_message("Reading " + name + " from " + filename, 6);
|
||||
|
||||
// Open file and make sure version is sufficient
|
||||
hid_t file_id = file_open(filename, 'r');
|
||||
check_data_version(file_id);
|
||||
|
||||
// Read nuclide data from HDF5
|
||||
hid_t group = open_group(file_id, name.c_str());
|
||||
int i_nuclide = data::nuclides.size();
|
||||
data::nuclides.push_back(std::make_unique<Nuclide>(
|
||||
group, nuc_temps[i_nuc], i_nuclide));
|
||||
|
||||
// Read from Fortran too
|
||||
nuclide_from_hdf5(group, data::nuclides.back().get(),
|
||||
&nuc_temps[i_nuc].front(), nuc_temps[i_nuc].size(), i_nuclide + 1);
|
||||
|
||||
close_group(group);
|
||||
file_close(file_id);
|
||||
|
||||
// Determine if minimum/maximum energy for this nuclide is greater/less
|
||||
// than the previous
|
||||
if (data::nuclides[i_nuclide]->grid_.size() >= 1) {
|
||||
int neutron = static_cast<int>(ParticleType::neutron);
|
||||
data::energy_min[neutron] = std::max(data::energy_min[neutron],
|
||||
data::nuclides[i_nuclide]->grid_[0].energy.front());
|
||||
data::energy_max[neutron] = std::min(data::energy_max[neutron],
|
||||
data::nuclides[i_nuclide]->grid_[0].energy.back());
|
||||
}
|
||||
|
||||
// Add name and alias to dictionary
|
||||
already_read.insert(name);
|
||||
|
||||
// Check if elemental data has been read, if needed
|
||||
int pos = name.find_first_of("0123456789");
|
||||
std::string element = name.substr(0, pos);
|
||||
if (settings::photon_transport) {
|
||||
if (already_read.find(element) == already_read.end()) {
|
||||
// Read photon interaction data from HDF5 photon library
|
||||
LibraryKey key {Library::Type::photon, element};
|
||||
int idx = data::library_map[key];
|
||||
std::string& filename = data::libraries[idx].path_;
|
||||
int i_element = data::element_map[element];
|
||||
write_message("Reading " + element + " from " + filename, 6);
|
||||
|
||||
// Open file and make sure version is sufficient
|
||||
hid_t file_id = file_open(filename, 'r');
|
||||
check_data_version(file_id);
|
||||
|
||||
// Read element data from HDF5
|
||||
hid_t group = open_group(file_id, element.c_str());
|
||||
data::elements.emplace_back(group, data::elements.size());
|
||||
|
||||
// Determine if minimum/maximum energy for this element is greater/less than
|
||||
// the previous
|
||||
const auto& elem {data::elements.back()};
|
||||
if (elem.energy_.size() >= 1) {
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
int n = elem.energy_.size();
|
||||
data::energy_min[photon] = std::max(data::energy_min[photon],
|
||||
std::exp(elem.energy_(1)));
|
||||
data::energy_max[photon] = std::min(data::energy_max[photon],
|
||||
std::exp(elem.energy_(n - 1)));
|
||||
}
|
||||
|
||||
close_group(group);
|
||||
file_close(file_id);
|
||||
|
||||
// Add element to set
|
||||
already_read.insert(element);
|
||||
}
|
||||
}
|
||||
|
||||
// Read multipole file into the appropriate entry on the nuclides array
|
||||
if (settings::temperature_multipole) read_multipole_data(i_nuclide);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& mat : model::materials) {
|
||||
for (const auto& table : mat->thermal_tables_) {
|
||||
// Get name of S(a,b) table
|
||||
int i_table = table.index_table;
|
||||
std::string& name = thermal_names[i_table];
|
||||
|
||||
if (already_read.find(name) == already_read.end()) {
|
||||
LibraryKey key {Library::Type::thermal, name};
|
||||
int idx = data::library_map[key];
|
||||
std::string& filename = data::libraries[idx].path_;
|
||||
|
||||
write_message("Reading " + name + " from " + filename, 6);
|
||||
|
||||
// Open file and make sure version matches
|
||||
hid_t file_id = file_open(filename, 'r');
|
||||
check_data_version(file_id);
|
||||
|
||||
// Read thermal scattering data from HDF5
|
||||
hid_t group = open_group(file_id, name.c_str());
|
||||
data::thermal_scatt.push_back(std::make_unique<ThermalScattering>(
|
||||
group, thermal_temps[i_table]));
|
||||
close_group(group);
|
||||
file_close(file_id);
|
||||
|
||||
// Add name to dictionary
|
||||
already_read.insert(name);
|
||||
}
|
||||
} // thermal_tables_
|
||||
|
||||
// Finish setting up materials (normalizing densities, etc.)
|
||||
mat->finalize();
|
||||
} // materials
|
||||
|
||||
|
||||
// Set up logarithmic grid for nuclides
|
||||
for (auto& nuc : data::nuclides) {
|
||||
nuc->init_grid();
|
||||
}
|
||||
int neutron = static_cast<int>(ParticleType::neutron);
|
||||
simulation::log_spacing = std::log(data::energy_max[neutron] /
|
||||
data::energy_min[neutron]) / settings::n_log_bins;
|
||||
|
||||
if (settings::photon_transport && settings::electron_treatment == ELECTRON_TTB) {
|
||||
// Determine if minimum/maximum energy for bremsstrahlung is greater/less
|
||||
// than the current minimum/maximum
|
||||
if (data::ttb_e_grid.size() >= 1) {
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
int n_e = data::ttb_e_grid.size();
|
||||
data::energy_min[photon] = std::max(data::energy_min[photon], data::ttb_e_grid(1));
|
||||
data::energy_max[photon] = std::min(data::energy_max[photon], data::ttb_e_grid(n_e - 1));
|
||||
}
|
||||
|
||||
// Take logarithm of energies since they are log-log interpolated
|
||||
data::ttb_e_grid = xt::log(data::ttb_e_grid);
|
||||
}
|
||||
|
||||
// Show which nuclide results in lowest energy for neutron transport
|
||||
for (const auto& nuc : data::nuclides) {
|
||||
// If a nuclide is present in a material that's not used in the model, its
|
||||
// grid has not been allocated
|
||||
if (nuc->grid_.size() > 0) {
|
||||
double max_E = nuc->grid_[0].energy.back();
|
||||
int neutron = static_cast<int>(ParticleType::neutron);
|
||||
if (max_E == data::energy_max[neutron]) {
|
||||
write_message("Maximum neutron transport energy: " +
|
||||
std::to_string(data::energy_max[neutron]) + " eV for " +
|
||||
nuc->name_, 7);
|
||||
if (mpi::master && data::energy_max[neutron] < 20.0e6) {
|
||||
warning("Maximum neutron energy is below 20 MeV. This may bias "
|
||||
" the results.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the user wants multipole, make sure we found a multipole library.
|
||||
if (settings::temperature_multipole) {
|
||||
bool mp_found = false;
|
||||
for (const auto& nuc : data::nuclides) {
|
||||
if (nuc->multipole_) {
|
||||
mp_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mpi::master && !mp_found) {
|
||||
warning("Windowed multipole functionality is turned on, but no multipole "
|
||||
"libraries were found. Make sure that windowed multipole data is "
|
||||
"present in your cross_sections.xml file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_ce_cross_sections_xml()
|
||||
{
|
||||
// Check if cross_sections.xml exists
|
||||
|
|
|
|||
182
src/dagmc.cpp
182
src/dagmc.cpp
|
|
@ -1,16 +1,38 @@
|
|||
#include "openmc/dagmc.h"
|
||||
|
||||
#include "openmc/cell.h"
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/file_utils.h"
|
||||
#include "openmc/string_utils.h"
|
||||
#include "openmc/settings.h"
|
||||
#include "openmc/geometry.h"
|
||||
|
||||
#ifdef DAGMC
|
||||
|
||||
#include "uwuw.hpp"
|
||||
#include "dagmcmetadata.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
namespace openmc {
|
||||
|
||||
#ifdef DAGMC
|
||||
const bool dagmc_enabled = true;
|
||||
#else
|
||||
const bool dagmc_enabled = false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#ifdef DAGMC
|
||||
|
||||
const std::string DAGMC_FILENAME = "dagmc.h5m";
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -20,28 +42,96 @@ moab::DagMC* DAG;
|
|||
|
||||
} // namespace model
|
||||
|
||||
|
||||
bool get_uwuw_materials_xml(std::string& s) {
|
||||
UWUW uwuw(DAGMC_FILENAME.c_str());
|
||||
|
||||
std::stringstream ss;
|
||||
bool uwuw_mats_present = false;
|
||||
if (uwuw.material_library.size() != 0) {
|
||||
uwuw_mats_present = true;
|
||||
// write header
|
||||
ss << "<?xml version=\"1.0\"?>\n";
|
||||
ss << "<materials>\n";
|
||||
const auto& mat_lib = uwuw.material_library;
|
||||
// write materials
|
||||
for (auto mat : mat_lib) { ss << mat.second.openmc("atom"); }
|
||||
// write footer
|
||||
ss << "</materials>";
|
||||
s = ss.str();
|
||||
}
|
||||
|
||||
return uwuw_mats_present;
|
||||
}
|
||||
|
||||
pugi::xml_document* read_uwuw_materials() {
|
||||
pugi::xml_document* doc = nullptr;
|
||||
|
||||
std::string s;
|
||||
bool found_uwuw_mats = get_uwuw_materials_xml(s);
|
||||
if (found_uwuw_mats) {
|
||||
doc = new pugi::xml_document();
|
||||
pugi::xml_parse_result result = doc->load_string(s.c_str());
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
bool write_uwuw_materials_xml() {
|
||||
std::string s;
|
||||
bool found_uwuw_mats = get_uwuw_materials_xml(s);
|
||||
// if there is a material library in the file
|
||||
if (found_uwuw_mats) {
|
||||
// write a material.xml file
|
||||
std::ofstream mats_xml("materials.xml");
|
||||
mats_xml << s;
|
||||
mats_xml.close();
|
||||
}
|
||||
|
||||
return found_uwuw_mats;
|
||||
}
|
||||
|
||||
void load_dagmc_geometry()
|
||||
{
|
||||
if (!model::DAG) {
|
||||
model::DAG = new moab::DagMC();
|
||||
}
|
||||
|
||||
int32_t dagmc_univ_id = 0; // universe is always 0 for DAGMC
|
||||
/// Materials \\\
|
||||
|
||||
moab::ErrorCode rval = model::DAG->load_file("dagmc.h5m");
|
||||
// create uwuw instance
|
||||
UWUW uwuw(DAGMC_FILENAME.c_str());
|
||||
|
||||
// check for uwuw material definitions
|
||||
bool using_uwuw = !uwuw.material_library.empty();
|
||||
|
||||
// notify user if UWUW materials are going to be used
|
||||
if (using_uwuw) {
|
||||
std::cout << "Found UWUW Materials in the DAGMC geometry file.\n";
|
||||
}
|
||||
|
||||
int32_t dagmc_univ_id = 0; // universe is always 0 for DAGMC runs
|
||||
|
||||
// load the DAGMC geometry
|
||||
moab::ErrorCode rval = model::DAG->load_file(DAGMC_FILENAME.c_str());
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
|
||||
// initialize acceleration data structures
|
||||
rval = model::DAG->init_OBBTree();
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
|
||||
std::vector<std::string> prop_keywords;
|
||||
prop_keywords.push_back("mat");
|
||||
prop_keywords.push_back("boundary");
|
||||
|
||||
std::map<std::string, std::string> ph;
|
||||
model::DAG->parse_properties(prop_keywords, ph, ":");
|
||||
// parse model metadata
|
||||
dagmcMetaData DMD(model::DAG);
|
||||
if (using_uwuw) {
|
||||
DMD.load_property_data();
|
||||
}
|
||||
std::vector<std::string> keywords {"temp", "mat", "density", "boundary"};
|
||||
std::map<std::string, std::string> dum;
|
||||
std::string delimiters = ":/";
|
||||
rval = model::DAG->parse_properties(keywords, dum, delimiters.c_str());
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
|
||||
/// Cells (Volumes) \\\
|
||||
|
||||
// initialize cell objects
|
||||
model::n_cells = model::DAG->num_entities(3);
|
||||
|
||||
|
|
@ -69,35 +159,80 @@ void load_dagmc_geometry()
|
|||
model::universes[it->second]->cells_.push_back(i);
|
||||
}
|
||||
|
||||
// check for temperature assignment
|
||||
std::string temp_value;
|
||||
if (model::DAG->has_prop(vol_handle, "temp")) {
|
||||
rval = model::DAG->prop_value(vol_handle, "temp", temp_value);
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
double temp = std::stod(temp_value);
|
||||
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * temp));
|
||||
} else {
|
||||
c->sqrtkT_.push_back(std::sqrt(K_BOLTZMANN * settings::temperature_default));
|
||||
}
|
||||
|
||||
// MATERIALS
|
||||
|
||||
if (model::DAG->is_implicit_complement(vol_handle)) {
|
||||
// assuming implicit complement is void for now
|
||||
c->material_.push_back(MATERIAL_VOID);
|
||||
if (model::DAG->has_prop(vol_handle, "mat")) {
|
||||
// if the implicit complement has been assigned a material, use it
|
||||
std::string comp_mat = DMD.volume_material_property_data_eh[vol_handle];
|
||||
// Note: material numbers are set by UWUW
|
||||
int mat_number = uwuw.material_library[comp_mat].metadata["mat_number"].asInt();
|
||||
c->material_.push_back(mat_number);
|
||||
} else {
|
||||
// if no material is found, the implicit complement is void
|
||||
c->material_.push_back(MATERIAL_VOID);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (model::DAG->has_prop(vol_handle, "mat")){
|
||||
std::string mat_value;
|
||||
// determine volume material assignment
|
||||
std::string mat_value;
|
||||
if (model::DAG->has_prop(vol_handle, "mat")) {
|
||||
rval = model::DAG->prop_value(vol_handle, "mat", mat_value);
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
to_lower(mat_value);
|
||||
|
||||
if (mat_value == "void" || mat_value == "vacuum") {
|
||||
c->material_.push_back(MATERIAL_VOID);
|
||||
} else {
|
||||
c->material_.push_back(std::stoi(mat_value));
|
||||
}
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Volume " << c->id_ << " has no material assignment.";
|
||||
fatal_error(err_msg.str());
|
||||
}
|
||||
|
||||
std::string cmp_str = mat_value;
|
||||
to_lower(cmp_str);
|
||||
// material void checks
|
||||
if (cmp_str.find("void") != std::string::npos ||
|
||||
cmp_str.find("vacuum") != std::string::npos ||
|
||||
cmp_str.find("graveyard") != std::string::npos) {
|
||||
c->material_.push_back(MATERIAL_VOID);
|
||||
} else {
|
||||
if (using_uwuw) {
|
||||
// lookup material in uwuw if the were present
|
||||
std::string uwuw_mat = DMD.volume_material_property_data_eh[vol_handle];
|
||||
if (uwuw.material_library.count(uwuw_mat) != 0) {
|
||||
// Note: material numbers are set by UWUW
|
||||
int mat_number = uwuw.material_library[uwuw_mat].metadata["mat_number"].asInt();
|
||||
c->material_.push_back(mat_number);
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Material with value " << mat_value << " not found ";
|
||||
err_msg << "in the UWUW material library";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
} else {
|
||||
// if not using UWUW materials, we'll find this material
|
||||
// later in the materials.xml
|
||||
c->material_.push_back(std::stoi(mat_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the cell overlap count if necessary.
|
||||
// allocate the cell overlap count if necessary
|
||||
if (settings::check_overlaps) {
|
||||
model::overlap_check_count.resize(model::cells.size(), 0);
|
||||
}
|
||||
|
||||
/// Surfaces \\\
|
||||
|
||||
// initialize surface objects
|
||||
int n_surfaces = model::DAG->num_entities(2);
|
||||
model::surfaces.resize(n_surfaces);
|
||||
|
|
@ -110,8 +245,9 @@ void load_dagmc_geometry()
|
|||
s->id_ = model::DAG->id_by_index(2, i+1);
|
||||
s->dagmc_ptr_ = model::DAG;
|
||||
|
||||
// set BCs
|
||||
std::string bc_value;
|
||||
if (model::DAG->has_prop(surf_handle, "boundary")) {
|
||||
std::string bc_value;
|
||||
rval = model::DAG->prop_value(surf_handle, "boundary", bc_value);
|
||||
MB_CHK_ERR_CONT(rval);
|
||||
to_lower(bc_value);
|
||||
|
|
@ -130,7 +266,8 @@ void load_dagmc_geometry()
|
|||
<< "\" specified on surface " << s->id_;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
} else { // if no BC property is found, set to transmit
|
||||
} else {
|
||||
// if no condition is found, set to transmit
|
||||
s->bc_ = BC_TRANSMIT;
|
||||
}
|
||||
|
||||
|
|
@ -147,5 +284,6 @@ void free_memory_dagmc()
|
|||
delete model::DAG;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,6 +13,11 @@ module dagmc_header
|
|||
subroutine free_memory_dagmc() bind(C)
|
||||
end subroutine free_memory_dagmc
|
||||
|
||||
function read_uwuw_materials() result(doc) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: doc
|
||||
end function read_uwuw_materials
|
||||
|
||||
end interface
|
||||
|
||||
end module dagmc_header
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ using namespace openmc;
|
|||
|
||||
// Functions defined in Fortran
|
||||
extern "C" void free_memory();
|
||||
extern "C" void reset_timers_f();
|
||||
|
||||
int openmc_finalize()
|
||||
{
|
||||
|
|
@ -25,7 +24,6 @@ int openmc_finalize()
|
|||
|
||||
// Reset timers
|
||||
reset_timers();
|
||||
reset_timers_f();
|
||||
|
||||
// Reset global variables
|
||||
settings::assume_separate = false;
|
||||
|
|
@ -127,7 +125,6 @@ int openmc_hard_reset()
|
|||
// Reset all tallies and timers
|
||||
openmc_reset();
|
||||
reset_timers();
|
||||
reset_timers_f();
|
||||
|
||||
// Reset total generations and keff guess
|
||||
simulation::keff = 1.0;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "openmc/cell.h"
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/container_util.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/geometry.h"
|
||||
#include "openmc/lattice.h"
|
||||
|
|
@ -112,6 +113,72 @@ assign_temperatures()
|
|||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
get_temperatures(std::vector<std::vector<double>>& nuc_temps,
|
||||
std::vector<std::vector<double>>& thermal_temps)
|
||||
{
|
||||
for (const auto& cell : model::cells) {
|
||||
// Skip non-material cells.
|
||||
if (cell->fill_ != C_NONE) continue;
|
||||
|
||||
for (int j = 0; j < cell->material_.size(); ++j) {
|
||||
// Skip void materials
|
||||
int i_material = cell->material_[j];
|
||||
if (i_material == MATERIAL_VOID) continue;
|
||||
|
||||
// Get temperature of cell (rounding to nearest integer)
|
||||
double sqrtkT = cell->sqrtkT_.size() == 1 ?
|
||||
cell->sqrtkT_[j] : cell->sqrtkT_[0];
|
||||
double temperature = sqrtkT*sqrtkT / K_BOLTZMANN;
|
||||
|
||||
const auto& mat {model::materials[i_material]};
|
||||
for (const auto& i_nuc : mat->nuclide_) {
|
||||
// Add temperature if it hasn't already been added
|
||||
if (!contains(nuc_temps[i_nuc], temperature)) {
|
||||
nuc_temps[i_nuc].push_back(temperature);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& table : mat->thermal_tables_) {
|
||||
// Get index in data::thermal_scatt array
|
||||
int i_sab = table.index_table;
|
||||
|
||||
// Add temperature if it hasn't already been added
|
||||
if (!contains(thermal_temps[i_sab], temperature)) {
|
||||
thermal_temps[i_sab].push_back(temperature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void finalize_geometry(std::vector<std::vector<double>>& nuc_temps,
|
||||
std::vector<std::vector<double>>& thermal_temps)
|
||||
{
|
||||
// Perform some final operations to set up the geometry
|
||||
adjust_indices();
|
||||
count_cell_instances(model::root_universe);
|
||||
|
||||
// Assign temperatures to cells that don't have temperatures already assigned
|
||||
assign_temperatures();
|
||||
|
||||
// Determine desired temperatures for each nuclide and S(a,b) table
|
||||
get_temperatures(nuc_temps, thermal_temps);
|
||||
|
||||
// Check to make sure there are not too many nested coordinate levels in the
|
||||
// geometry since the coordinate list is statically allocated for performance
|
||||
// reasons
|
||||
if (maximum_levels(model::root_universe) > MAX_COORD) {
|
||||
fatal_error("Too many nested coordinate levels in the geometry. "
|
||||
"Try increasing the maximum number of coordinate levels by "
|
||||
"providing the CMake -Dmaxcoord= option.");
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int32_t
|
||||
find_root_universe()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ module geometry_header
|
|||
use algorithm, only: find
|
||||
use constants, only: K_BOLTZMANN, MATERIAL_VOID
|
||||
use dict_header, only: DictIntInt
|
||||
use material_header, only: Material, materials
|
||||
use nuclide_header
|
||||
use sab_header
|
||||
use stl_vector, only: VectorReal
|
||||
use string, only: to_lower
|
||||
|
||||
|
|
@ -176,70 +174,6 @@ contains
|
|||
sqrtkT = cell_sqrtkT_c(this % ptr, i)
|
||||
end function cell_sqrtkT
|
||||
|
||||
!===============================================================================
|
||||
! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table
|
||||
! appears at in the model. Later, this list is used to determine the actual
|
||||
! temperatures to read (which may be different if interpolation is used)
|
||||
!===============================================================================
|
||||
|
||||
subroutine get_temperatures(nuc_temps, sab_temps)
|
||||
type(VectorReal), allocatable, intent(out) :: nuc_temps(:)
|
||||
type(VectorReal), optional, allocatable, intent(out) :: sab_temps(:)
|
||||
|
||||
integer :: i, j, k
|
||||
integer :: i_nuclide ! index in nuclides array
|
||||
integer :: i_sab ! index in S(a,b) array
|
||||
integer :: i_material
|
||||
real(8) :: temperature ! temperature in Kelvin
|
||||
|
||||
allocate(nuc_temps(n_nuclides))
|
||||
if (present(sab_temps)) allocate(sab_temps(n_sab_tables))
|
||||
|
||||
do i = 1, size(cells)
|
||||
! Skip non-material cells.
|
||||
if (cells(i) % fill() /= C_NONE) cycle
|
||||
|
||||
do j = 1, cells(i) % material_size()
|
||||
! Skip void materials
|
||||
if (cells(i) % material(j) == MATERIAL_VOID) cycle
|
||||
|
||||
! Get temperature of cell (rounding to nearest integer)
|
||||
if (cells(i) % sqrtkT_size() > 1) then
|
||||
temperature = cells(i) % sqrtkT(j-1)**2 / K_BOLTZMANN
|
||||
else
|
||||
temperature = cells(i) % sqrtkT(0)**2 / K_BOLTZMANN
|
||||
end if
|
||||
|
||||
i_material = cells(i) % material(j)
|
||||
|
||||
associate (mat => materials(i_material))
|
||||
NUC_NAMES_LOOP: do k = 1, size(mat % names)
|
||||
! Get index in nuc_temps array
|
||||
i_nuclide = nuclide_dict % get(to_lower(mat % names(k)))
|
||||
|
||||
! Add temperature if it hasn't already been added
|
||||
if (find(nuc_temps(i_nuclide), temperature) == -1) then
|
||||
call nuc_temps(i_nuclide) % push_back(temperature)
|
||||
end if
|
||||
end do NUC_NAMES_LOOP
|
||||
|
||||
if (present(sab_temps) .and. mat % n_sab > 0) then
|
||||
SAB_NAMES_LOOP: do k = 1, size(mat % sab_names)
|
||||
! Get index in nuc_temps array
|
||||
i_sab = sab_dict % get(to_lower(mat % sab_names(k)))
|
||||
|
||||
! Add temperature if it hasn't already been added
|
||||
if (find(sab_temps(i_sab), temperature) == -1) then
|
||||
call sab_temps(i_sab) % push_back(temperature)
|
||||
end if
|
||||
end do SAB_NAMES_LOOP
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end do
|
||||
|
||||
end subroutine get_temperatures
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_GEOMETRY deallocates global arrays defined in this module
|
||||
!===============================================================================
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
|
|
@ -12,20 +13,30 @@
|
|||
|
||||
#include "openmc/capi.h"
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/cross_sections.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/geometry_aux.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/message_passing.h"
|
||||
#include "openmc/mgxs_interface.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/output.h"
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/settings.h"
|
||||
#include "openmc/simulation.h"
|
||||
#include "openmc/string_utils.h"
|
||||
#include "openmc/summary.h"
|
||||
#include "openmc/thermal.h"
|
||||
#include "openmc/timer.h"
|
||||
|
||||
// data/functions from Fortran side
|
||||
extern "C" void print_usage();
|
||||
extern "C" void print_version();
|
||||
extern "C" void read_command_line();
|
||||
extern "C" void read_input_xml();
|
||||
extern "C" void read_geometry_xml();
|
||||
extern "C" void read_materials_xml();
|
||||
extern "C" void read_plots_xml();
|
||||
extern "C" void read_tallies_xml();
|
||||
|
||||
// Paths to various files
|
||||
extern "C" {
|
||||
|
|
@ -248,4 +259,51 @@ parse_command_line(int argc, char* argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
void read_input_xml()
|
||||
{
|
||||
read_settings_xml();
|
||||
read_cross_sections_xml();
|
||||
read_materials_xml();
|
||||
read_geometry_xml();
|
||||
|
||||
// Convert user IDs -> indices, assign temperatures
|
||||
double_2dvec nuc_temps(data::nuclide_map.size());
|
||||
double_2dvec thermal_temps(data::thermal_scatt_map.size());
|
||||
finalize_geometry(nuc_temps, thermal_temps);
|
||||
|
||||
if (settings::run_mode != RUN_MODE_PLOTTING) {
|
||||
simulation::time_read_xs.start();
|
||||
if (settings::run_CE) {
|
||||
// Read continuous-energy cross sections
|
||||
read_ce_cross_sections(nuc_temps, thermal_temps);
|
||||
} else {
|
||||
// Create material macroscopic data for MGXS
|
||||
read_mgxs();
|
||||
create_macro_xs();
|
||||
}
|
||||
simulation::time_read_xs.stop();
|
||||
}
|
||||
|
||||
read_tallies_xml();
|
||||
|
||||
// Initialize distribcell_filters
|
||||
prepare_distribcell();
|
||||
|
||||
if (settings::run_mode == RUN_MODE_PLOTTING) {
|
||||
// Read plots.xml if it exists
|
||||
read_plots_xml();
|
||||
if (mpi::master && settings::verbosity >= 5) print_plot();
|
||||
|
||||
} else {
|
||||
// Write summary information
|
||||
if (mpi::master && settings::output_summary) write_summary();
|
||||
|
||||
// Warn if overlap checking is on
|
||||
if (mpi::master && settings::check_overlaps) {
|
||||
warning("Cell overlap checking is ON.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -15,27 +15,22 @@ module input_xml
|
|||
use material_header
|
||||
use mesh_header
|
||||
use message_passing
|
||||
use mgxs_data, only: create_macro_xs, read_mgxs
|
||||
use mgxs_interface
|
||||
use nuclide_header
|
||||
use multipole_header
|
||||
use output, only: title, header
|
||||
use photon_header
|
||||
use random_lcg, only: prn
|
||||
use surface_header
|
||||
use set_header, only: SetChar
|
||||
use settings
|
||||
use stl_vector, only: VectorInt, VectorReal, VectorChar
|
||||
use string, only: to_lower, to_str, str_to_int, str_to_real, &
|
||||
starts_with, ends_with, split_string, &
|
||||
zero_padded, to_c_string
|
||||
use summary, only: write_summary
|
||||
use tally
|
||||
use tally_header, only: openmc_extend_tallies
|
||||
use tally_derivative_header
|
||||
use tally_filter_header
|
||||
use tally_filter
|
||||
use timer_header, only: time_read_xs
|
||||
use volume_header
|
||||
use xml_interface
|
||||
|
||||
|
|
@ -43,20 +38,11 @@ module input_xml
|
|||
save
|
||||
|
||||
interface
|
||||
subroutine adjust_indices() bind(C)
|
||||
end subroutine adjust_indices
|
||||
|
||||
subroutine assign_temperatures() bind(C)
|
||||
end subroutine assign_temperatures
|
||||
|
||||
subroutine count_cell_instances(univ_indx) bind(C)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: univ_indx
|
||||
end subroutine count_cell_instances
|
||||
|
||||
subroutine prepare_distribcell() bind(C)
|
||||
end subroutine prepare_distribcell
|
||||
|
||||
subroutine read_surfaces(node_ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: node_ptr
|
||||
|
|
@ -67,9 +53,6 @@ module input_xml
|
|||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_cells
|
||||
|
||||
subroutine read_cross_sections_xml() bind(C)
|
||||
end subroutine
|
||||
|
||||
subroutine read_lattices(node_ptr) bind(C)
|
||||
import C_PTR
|
||||
type(C_PTR) :: node_ptr
|
||||
|
|
@ -99,9 +82,6 @@ module input_xml
|
|||
type(C_PTR) :: node_ptr
|
||||
end subroutine read_plots
|
||||
|
||||
subroutine print_plot() bind(C)
|
||||
end subroutine print_plot
|
||||
|
||||
subroutine set_particle_energy_bounds(particle, E_min, E_max) bind(C)
|
||||
import C_INT, C_DOUBLE
|
||||
integer(C_INT), value :: particle
|
||||
|
|
@ -112,86 +92,6 @@ module input_xml
|
|||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! READ_INPUT_XML calls each of the separate subroutines for reading settings,
|
||||
! geometry, materials, and tallies.
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_input_xml() bind(C)
|
||||
|
||||
type(VectorReal), allocatable :: nuc_temps(:) ! List of T to read for each nuclide
|
||||
type(VectorReal), allocatable :: sab_temps(:) ! List of T to read for each S(a,b)
|
||||
|
||||
call read_settings_xml()
|
||||
call read_cross_sections_xml()
|
||||
call read_materials_xml()
|
||||
call read_geometry_xml()
|
||||
|
||||
! Convert user IDs -> indices, assign temperatures
|
||||
call finalize_geometry(nuc_temps, sab_temps)
|
||||
|
||||
if (run_mode /= MODE_PLOTTING) then
|
||||
call time_read_xs % start()
|
||||
if (run_CE) then
|
||||
! Read continuous-energy cross sections
|
||||
call read_ce_cross_sections(nuc_temps, sab_temps)
|
||||
else
|
||||
! Create material macroscopic data for MGXS
|
||||
call read_mgxs()
|
||||
call create_macro_xs()
|
||||
end if
|
||||
call time_read_xs % stop()
|
||||
end if
|
||||
|
||||
call read_tallies_xml()
|
||||
|
||||
! Initialize distribcell_filters
|
||||
call prepare_distribcell()
|
||||
|
||||
if (run_mode == MODE_PLOTTING) then
|
||||
! Read plots.xml if it exists
|
||||
call read_plots_xml()
|
||||
if (master .and. verbosity >= 5) call print_plot()
|
||||
|
||||
else
|
||||
! Normalize atom/weight percents
|
||||
call normalize_ao()
|
||||
|
||||
! Write summary information
|
||||
if (master .and. output_summary) call write_summary()
|
||||
|
||||
! Warn if overlap checking is on
|
||||
if (master .and. check_overlaps) &
|
||||
call warning("Cell overlap checking is ON.")
|
||||
end if
|
||||
|
||||
end subroutine read_input_xml
|
||||
|
||||
subroutine finalize_geometry(nuc_temps, sab_temps)
|
||||
type(VectorReal), allocatable, intent(out) :: nuc_temps(:)
|
||||
type(VectorReal), optional, allocatable, intent(out) :: sab_temps(:)
|
||||
|
||||
! Perform some final operations to set up the geometry
|
||||
call adjust_indices()
|
||||
call count_cell_instances(root_universe)
|
||||
|
||||
! Assign temperatures to cells that don't have temperatures already assigned
|
||||
call assign_temperatures()
|
||||
|
||||
! Determine desired temperatures for each nuclide and S(a,b) table
|
||||
call get_temperatures(nuc_temps, sab_temps)
|
||||
|
||||
! Check to make sure there are not too many nested coordinate levels in the
|
||||
! geometry since the coordinate list is statically allocated for performance
|
||||
! reasons
|
||||
if (maximum_levels(root_universe) > MAX_COORD) then
|
||||
call fatal_error("Too many nested coordinate levels in the geometry. &
|
||||
&Try increasing the maximum number of coordinate levels by &
|
||||
&providing the CMake -Dmaxcoord= option.")
|
||||
end if
|
||||
|
||||
end subroutine finalize_geometry
|
||||
|
||||
!===============================================================================
|
||||
! READ_SETTINGS_XML reads data from a settings.xml file and parses it, checking
|
||||
! for errors and placing properly-formatted data in the right data structures
|
||||
|
|
@ -280,7 +180,7 @@ contains
|
|||
! for errors and placing properly-formatted data in the right data structures
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_geometry_xml()
|
||||
subroutine read_geometry_xml() bind(C)
|
||||
|
||||
integer :: i, n
|
||||
integer :: univ_id
|
||||
|
|
@ -482,417 +382,56 @@ contains
|
|||
end do
|
||||
end subroutine allocate_cells
|
||||
|
||||
subroutine read_materials_xml()
|
||||
integer :: i ! loop index for materials
|
||||
integer :: j ! loop index for nuclides
|
||||
integer :: k ! loop index
|
||||
integer :: n ! number of nuclides
|
||||
integer :: n_sab ! number of sab tables for a material
|
||||
integer :: index_nuclide ! index in nuclides
|
||||
integer :: index_element ! index in elements
|
||||
integer :: index_sab ! index in sab_tables
|
||||
subroutine read_materials_xml() bind(C)
|
||||
logical :: file_exists ! does materials.xml exist?
|
||||
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_WORD_LEN), allocatable :: sarray(:)
|
||||
real(8) :: val ! value entered for density
|
||||
real(8) :: temp_dble ! temporary double prec. real
|
||||
logical :: sum_density ! density is sum of nuclide densities
|
||||
type(VectorChar) :: names ! temporary list of nuclide names
|
||||
type(VectorChar) :: list_iso_lab ! temporary list of isotropic lab scatterers
|
||||
type(VectorReal) :: densities ! temporary list of nuclide densities
|
||||
type(Material), pointer :: mat => null()
|
||||
type(XMLDocument) :: doc
|
||||
type(XMLNode) :: root
|
||||
type(XMLNode) :: node_mat
|
||||
type(XMLNode) :: node_dens
|
||||
type(XMLNode) :: node_nuc
|
||||
type(XMLNode) :: node_sab
|
||||
type(XMLNode), allocatable :: node_mat_list(:)
|
||||
type(XMLNode), allocatable :: node_nuc_list(:)
|
||||
type(XMLNode), allocatable :: node_ele_list(:)
|
||||
type(XMLNode), allocatable :: node_macro_list(:)
|
||||
type(XMLNode), allocatable :: node_sab_list(:)
|
||||
|
||||
interface
|
||||
function nuclides_size() bind(C) result(n)
|
||||
import C_INT
|
||||
integer(C_INT) :: n
|
||||
end function
|
||||
|
||||
function elements_size() bind(C) result(n)
|
||||
import C_INT
|
||||
integer(C_INT) :: n
|
||||
end function
|
||||
end interface
|
||||
|
||||
! Display output message
|
||||
call write_message("Reading materials XML file...", 5)
|
||||
|
||||
! Check if materials.xml exists
|
||||
filename = trim(path_input) // "materials.xml"
|
||||
inquire(FILE=filename, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
call fatal_error("Material XML file '" // trim(filename) // "' does not &
|
||||
&exist!")
|
||||
doc % ptr = C_NULL_PTR
|
||||
|
||||
#ifdef DAGMC
|
||||
if (dagmc) then
|
||||
doc % ptr = read_uwuw_materials()
|
||||
end if
|
||||
#endif
|
||||
|
||||
if (.not. c_associated(doc % ptr)) then
|
||||
! Check if materials.xml exists
|
||||
filename = trim(path_input) // "materials.xml"
|
||||
inquire(FILE=filename, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
call fatal_error("Material XML file '" // trim(filename) // "' does not &
|
||||
&exist!")
|
||||
end if
|
||||
|
||||
! Parse materials.xml file
|
||||
call doc % load_file(filename)
|
||||
root = doc % document_element()
|
||||
|
||||
end if
|
||||
|
||||
root = doc % document_element()
|
||||
call read_materials(root % ptr)
|
||||
|
||||
! Get pointer to list of XML <material>
|
||||
call get_node_list(root, "material", node_mat_list)
|
||||
|
||||
! Allocate materials array
|
||||
n_materials = size(node_mat_list)
|
||||
allocate(materials(n_materials))
|
||||
|
||||
! Initialize count for number of nuclides/S(a,b) tables
|
||||
index_nuclide = 0
|
||||
index_element = 0
|
||||
index_sab = 0
|
||||
|
||||
do i = 1, n_materials
|
||||
mat => materials(i)
|
||||
|
||||
mat % ptr = material_pointer(i - 1)
|
||||
|
||||
! Get pointer to i-th material node
|
||||
node_mat = node_mat_list(i)
|
||||
|
||||
! Check if material is depletable
|
||||
if (check_for_node(node_mat, "depletable")) then
|
||||
call get_node_value(node_mat, "depletable", mat % depletable)
|
||||
end if
|
||||
|
||||
! Copy material name
|
||||
if (check_for_node(node_mat, "name")) then
|
||||
call get_node_value(node_mat, "name", mat % name)
|
||||
end if
|
||||
|
||||
! Get pointer to density element
|
||||
if (check_for_node(node_mat, "density")) then
|
||||
node_dens = node_mat % child("density")
|
||||
else
|
||||
call fatal_error("Must specify density element in material " &
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Copy units
|
||||
call get_node_value(node_dens, "units", units)
|
||||
|
||||
! If the units is 'sum', then the total density of the material is taken
|
||||
! to be the sum of the atom fractions listed on the nuclides
|
||||
if (units == 'sum') then
|
||||
sum_density = .true.
|
||||
|
||||
else if (units == 'macro') then
|
||||
if (check_for_node(node_dens, "value")) then
|
||||
call get_node_value(node_dens, "value", val)
|
||||
else
|
||||
val = ONE
|
||||
end if
|
||||
|
||||
! Set density
|
||||
mat % density = val
|
||||
|
||||
sum_density = .false.
|
||||
|
||||
else
|
||||
call get_node_value(node_dens, "value", val)
|
||||
|
||||
! Check for erroneous density
|
||||
sum_density = .false.
|
||||
if (val <= ZERO) then
|
||||
call fatal_error("Need to specify a positive density on material " &
|
||||
// trim(to_str(mat % id())) // ".")
|
||||
end if
|
||||
|
||||
! Adjust material density based on specified units
|
||||
select case(to_lower(units))
|
||||
case ('g/cc', 'g/cm3')
|
||||
mat % density = -val
|
||||
case ('kg/m3')
|
||||
mat % density = -0.001_8 * val
|
||||
case ('atom/b-cm')
|
||||
mat % density = val
|
||||
case ('atom/cm3', 'atom/cc')
|
||||
mat % density = 1.0e-24_8 * val
|
||||
case default
|
||||
call fatal_error("Unkwown units '" // trim(units) &
|
||||
// "' specified on material " // trim(to_str(mat % id())))
|
||||
end select
|
||||
end if
|
||||
|
||||
! Issue error if elements are provided
|
||||
call get_node_list(node_mat, "element", node_ele_list)
|
||||
|
||||
if (size(node_ele_list) > 0) then
|
||||
call fatal_error("Unable to add an element to material " &
|
||||
// trim(to_str(mat % id())) // " since the element option has &
|
||||
&been removed from the xml input. Elements can only be added via &
|
||||
&the Python API, which will expand elements into their natural &
|
||||
&nuclides.")
|
||||
end if
|
||||
|
||||
! =======================================================================
|
||||
! READ AND PARSE <nuclide> TAGS
|
||||
|
||||
! Check to ensure material has at least one nuclide
|
||||
if (.not. check_for_node(node_mat, "nuclide") .and. &
|
||||
.not. check_for_node(node_mat, "macroscopic")) then
|
||||
call fatal_error("No macroscopic data or nuclides specified on &
|
||||
&material " // trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! Create list of macroscopic x/s based on those specified, just treat
|
||||
! them as nuclides. This is all really a facade so the user thinks they
|
||||
! are entering in macroscopic data but the code treats them the same
|
||||
! as nuclides internally.
|
||||
! Get pointer list of XML <macroscopic>
|
||||
call get_node_list(node_mat, "macroscopic", node_macro_list)
|
||||
if (run_CE .and. (size(node_macro_list) > 0)) then
|
||||
call fatal_error("Macroscopic can not be used in continuous-energy&
|
||||
& mode!")
|
||||
else if (size(node_macro_list) > 1) then
|
||||
call fatal_error("Only one macroscopic object permitted per material, " &
|
||||
// trim(to_str(mat % id())))
|
||||
else if (size(node_macro_list) == 1) then
|
||||
|
||||
node_nuc = node_macro_list(1)
|
||||
|
||||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on macroscopic data in material " &
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
call get_node_value(node_nuc, "name", name)
|
||||
name = trim(name)
|
||||
|
||||
! save name to list
|
||||
call names % push_back(name)
|
||||
|
||||
! Set density for macroscopic data
|
||||
if (units == 'macro') then
|
||||
call densities % push_back(ONE)
|
||||
else
|
||||
call fatal_error("Units can only be macro for macroscopic data " &
|
||||
// trim(name))
|
||||
end if
|
||||
else
|
||||
|
||||
! Get pointer list of XML <nuclide>
|
||||
call get_node_list(node_mat, "nuclide", node_nuc_list)
|
||||
|
||||
! Create list of nuclides based on those specified
|
||||
INDIVIDUAL_NUCLIDES: do j = 1, size(node_nuc_list)
|
||||
! Combine nuclide identifier and cross section and copy into names
|
||||
node_nuc = node_nuc_list(j)
|
||||
|
||||
! Check for empty name on nuclide
|
||||
if (.not. check_for_node(node_nuc, "name")) then
|
||||
call fatal_error("No name specified on nuclide in material " &
|
||||
// trim(to_str(mat % id())))
|
||||
end if
|
||||
|
||||
! store nuclide name
|
||||
call get_node_value(node_nuc, "name", name)
|
||||
name = trim(name)
|
||||
|
||||
! save name to list
|
||||
call names % push_back(name)
|
||||
|
||||
! Check if no atom/weight percents were specified or if both atom and
|
||||
! weight percents were specified
|
||||
if (units == 'macro') then
|
||||
call densities % push_back(ONE)
|
||||
else
|
||||
if (.not. check_for_node(node_nuc, "ao") .and. &
|
||||
.not. check_for_node(node_nuc, "wo")) then
|
||||
call fatal_error("No atom or weight percent specified for &
|
||||
&nuclide" // trim(name))
|
||||
elseif (check_for_node(node_nuc, "ao") .and. &
|
||||
check_for_node(node_nuc, "wo")) then
|
||||
call fatal_error("Cannot specify both atom and weight percents &
|
||||
&for a nuclide: " // trim(name))
|
||||
end if
|
||||
|
||||
! Copy atom/weight percents
|
||||
if (check_for_node(node_nuc, "ao")) then
|
||||
call get_node_value(node_nuc, "ao", temp_dble)
|
||||
call densities % push_back(temp_dble)
|
||||
else
|
||||
call get_node_value(node_nuc, "wo", temp_dble)
|
||||
call densities % push_back(-temp_dble)
|
||||
end if
|
||||
end if
|
||||
end do INDIVIDUAL_NUCLIDES
|
||||
end if
|
||||
|
||||
! =======================================================================
|
||||
! READ AND PARSE <isotropic> element
|
||||
|
||||
if (check_for_node(node_mat, "isotropic")) then
|
||||
n = node_word_count(node_mat, "isotropic")
|
||||
allocate(sarray(n))
|
||||
call get_node_array(node_mat, "isotropic", sarray)
|
||||
do j = 1, n
|
||||
call list_iso_lab % push_back(sarray(j))
|
||||
end do
|
||||
deallocate(sarray)
|
||||
end if
|
||||
|
||||
! ========================================================================
|
||||
! COPY NUCLIDES TO ARRAYS IN MATERIAL
|
||||
|
||||
! allocate arrays in Material object
|
||||
n = names % size()
|
||||
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
|
||||
! Check that this nuclide is listed in the cross_sections.xml file
|
||||
name = trim(names % data(j))
|
||||
if (.not. library_present(LIBRARY_NEUTRON, (to_lower(name)))) then
|
||||
call fatal_error("Could not find nuclide " // trim(name) &
|
||||
// " in cross_sections data file!")
|
||||
end if
|
||||
|
||||
! If this nuclide hasn't been encountered yet, we need to add its name
|
||||
! and alias to the nuclide_dict
|
||||
if (.not. nuclide_dict % has(to_lower(name))) then
|
||||
index_nuclide = index_nuclide + 1
|
||||
mat % nuclide(j) = index_nuclide
|
||||
|
||||
call nuclide_dict % set(to_lower(name), index_nuclide)
|
||||
else
|
||||
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_present(LIBRARY_PHOTON, 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)
|
||||
|
||||
end do ALL_NUCLIDES
|
||||
|
||||
if (run_CE) then
|
||||
! By default, isotropic-in-lab is not used
|
||||
if (list_iso_lab % size() > 0) then
|
||||
mat % has_isotropic_nuclides = .true.
|
||||
allocate(mat % p0(n))
|
||||
mat % p0(:) = .false.
|
||||
|
||||
! Apply isotropic-in-lab treatment to specified nuclides
|
||||
do j = 1, list_iso_lab % size()
|
||||
do k = 1, n
|
||||
if (names % data(k) == list_iso_lab % data(j)) then
|
||||
mat % p0(k) = .true.
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end if
|
||||
end if
|
||||
|
||||
! Check to make sure either all atom percents or all weight percents are
|
||||
! given
|
||||
if (.not. (all(mat % atom_density >= ZERO) .or. &
|
||||
all(mat % atom_density <= ZERO))) then
|
||||
call fatal_error("Cannot mix atom and weight percents in material " &
|
||||
// to_str(mat % id()))
|
||||
end if
|
||||
|
||||
! Determine density if it is a sum value
|
||||
if (sum_density) mat % density = sum(mat % atom_density)
|
||||
|
||||
! Clear lists
|
||||
call names % clear()
|
||||
call densities % clear()
|
||||
call list_iso_lab % clear()
|
||||
|
||||
! =======================================================================
|
||||
! READ AND PARSE <sab> TAG FOR S(a,b) DATA
|
||||
if (run_CE) then
|
||||
! Get pointer list to XML <sab>
|
||||
call get_node_list(node_mat, "sab", node_sab_list)
|
||||
|
||||
n_sab = size(node_sab_list)
|
||||
if (n_sab > 0) then
|
||||
! Set number of S(a,b) tables
|
||||
mat % n_sab = n_sab
|
||||
|
||||
! Allocate names and indices for nuclides and tables -- for now we
|
||||
! allocate these as the number of S(a,b) tables listed. Since a single
|
||||
! table might apply to multiple nuclides, they are resized later if a
|
||||
! table is indeed applied to multiple nuclides.
|
||||
allocate(mat % sab_names(n_sab))
|
||||
allocate(mat % i_sab_tables(n_sab))
|
||||
allocate(mat % sab_fracs(n_sab))
|
||||
|
||||
do j = 1, n_sab
|
||||
! Get pointer to S(a,b) table
|
||||
node_sab = node_sab_list(j)
|
||||
|
||||
! Determine name of S(a,b) table
|
||||
if (.not. check_for_node(node_sab, "name")) then
|
||||
call fatal_error("Need to specify <name> for S(a,b) table.")
|
||||
end if
|
||||
call get_node_value(node_sab, "name", name)
|
||||
name = trim(name)
|
||||
mat % sab_names(j) = name
|
||||
|
||||
! Read the fraction of nuclei affected by this S(a,b) table
|
||||
if (check_for_node(node_sab, "fraction")) then
|
||||
call get_node_value(node_sab, "fraction", mat % sab_fracs(j))
|
||||
else
|
||||
mat % sab_fracs(j) = ONE
|
||||
end if
|
||||
|
||||
! Check that this nuclide is listed in the cross_sections.xml file
|
||||
if (.not. library_present(LIBRARY_THERMAL, to_lower(name))) then
|
||||
call fatal_error("Could not find S(a,b) table " // trim(name) &
|
||||
// " in cross_sections.xml file!")
|
||||
end if
|
||||
|
||||
! If this S(a,b) table hasn't been encountered yet, we need to add its
|
||||
! name and alias to the sab_dict
|
||||
if (.not. sab_dict % has(to_lower(name))) then
|
||||
index_sab = index_sab + 1
|
||||
mat % i_sab_tables(j) = index_sab
|
||||
call sab_dict % set(to_lower(name), index_sab)
|
||||
else
|
||||
mat % i_sab_tables(j) = sab_dict % get(to_lower(name))
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
end if
|
||||
|
||||
! Add material to dictionary
|
||||
call material_dict % set(mat % id(), i)
|
||||
end do
|
||||
|
||||
! Set total number of nuclides and S(a,b) tables
|
||||
n_nuclides = index_nuclide
|
||||
n_elements = index_element
|
||||
n_sab_tables = index_sab
|
||||
! Set total number of nuclides and elements
|
||||
n_nuclides = nuclides_size()
|
||||
n_elements = elements_size()
|
||||
allocate(nuclides(n_nuclides))
|
||||
|
||||
! Close materials XML file
|
||||
call doc % clear()
|
||||
|
|
@ -904,7 +443,7 @@ contains
|
|||
! for errors and placing properly-formatted data in the right data structures
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_tallies_xml()
|
||||
subroutine read_tallies_xml() bind(C)
|
||||
|
||||
integer :: i ! loop over user-specified tallies
|
||||
integer :: j ! loop over words
|
||||
|
|
@ -1230,17 +769,18 @@ contains
|
|||
end if
|
||||
|
||||
! If a specific nuclide was specified
|
||||
word = to_lower(sarray(j))
|
||||
word = sarray(j)
|
||||
|
||||
! Search through nuclides
|
||||
if (.not. nuclide_dict % has(word)) then
|
||||
k = nuclide_map_get(to_c_string(word))
|
||||
if (k == -1) then
|
||||
call fatal_error("Could not find the nuclide " &
|
||||
// trim(word) // " specified in tally " &
|
||||
// trim(to_str(t % id())) // " in any material.")
|
||||
end if
|
||||
|
||||
! Set bin to index in nuclides array
|
||||
nuclide_bins(j) = nuclide_dict % get(word)
|
||||
nuclide_bins(j) = k
|
||||
end do
|
||||
end if
|
||||
|
||||
|
|
@ -1410,7 +950,7 @@ contains
|
|||
! READ_PLOTS_XML reads data from a plots.xml file
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_plots_xml()
|
||||
subroutine read_plots_xml() bind(C)
|
||||
|
||||
logical :: file_exists ! does plots.xml file exist?
|
||||
character(MAX_LINE_LEN) :: filename ! absolute path to plots.xml
|
||||
|
|
@ -1511,348 +1051,12 @@ contains
|
|||
call read_mg_cross_sections_header_c(file_id)
|
||||
|
||||
! Get the minimum and maximum energies
|
||||
energy_min(NEUTRON) = energy_bins(num_energy_groups + 1)
|
||||
energy_max(NEUTRON) = energy_bins(1)
|
||||
call set_particle_energy_bounds(NEUTRON, energy_min(NEUTRON), &
|
||||
energy_max(NEUTRON))
|
||||
call set_particle_energy_bounds(NEUTRON, &
|
||||
energy_bins(num_energy_groups + 1), energy_bins(1))
|
||||
|
||||
! Close MGXS HDF5 file
|
||||
call file_close(file_id)
|
||||
|
||||
end subroutine read_mg_cross_sections_header
|
||||
|
||||
!===============================================================================
|
||||
! NORMALIZE_AO Normalize the nuclide atom percents
|
||||
!===============================================================================
|
||||
|
||||
subroutine normalize_ao()
|
||||
integer :: i ! index in materials array
|
||||
integer :: j ! index over nuclides in material
|
||||
real(8) :: sum_percent ! summation
|
||||
real(8) :: awr ! atomic weight ratio
|
||||
real(8) :: x ! atom percent
|
||||
logical :: percent_in_atom ! nuclides specified in atom percent?
|
||||
logical :: density_in_atom ! density specified in atom/b-cm?
|
||||
|
||||
do i = 1, size(materials)
|
||||
associate (mat => materials(i))
|
||||
percent_in_atom = (mat % atom_density(1) > ZERO)
|
||||
density_in_atom = (mat % density > ZERO)
|
||||
|
||||
sum_percent = ZERO
|
||||
do j = 1, size(mat % nuclide)
|
||||
! determine atomic weight ratio
|
||||
if (run_CE) then
|
||||
awr = nuclides(mat % nuclide(j)) % awr
|
||||
else
|
||||
awr = get_awr_c(mat % nuclide(j))
|
||||
end if
|
||||
|
||||
! if given weight percent, convert all values so that they are divided
|
||||
! by awr. thus, when a sum is done over the values, it's actually
|
||||
! sum(w/awr)
|
||||
if (.not. percent_in_atom) then
|
||||
mat % atom_density(j) = -mat % atom_density(j) / awr
|
||||
end if
|
||||
end do
|
||||
|
||||
! determine normalized atom percents. if given atom percents, this is
|
||||
! straightforward. if given weight percents, the value is w/awr and is
|
||||
! divided by sum(w/awr)
|
||||
sum_percent = sum(mat % atom_density)
|
||||
mat % atom_density = mat % atom_density / sum_percent
|
||||
|
||||
! Change density in g/cm^3 to atom/b-cm. Since all values are now in
|
||||
! atom percent, the sum needs to be re-evaluated as 1/sum(x*awr)
|
||||
if (.not. density_in_atom) then
|
||||
sum_percent = ZERO
|
||||
do j = 1, mat % n_nuclides
|
||||
if (run_CE) then
|
||||
awr = nuclides(mat % nuclide(j)) % awr
|
||||
else
|
||||
awr = get_awr_c(mat % nuclide(j))
|
||||
end if
|
||||
x = mat % atom_density(j)
|
||||
sum_percent = sum_percent + x*awr
|
||||
end do
|
||||
sum_percent = ONE / sum_percent
|
||||
mat%density = -mat % density * N_AVOGADRO &
|
||||
/ MASS_NEUTRON * sum_percent
|
||||
end if
|
||||
|
||||
! Calculate nuclide atom densities
|
||||
mat % atom_density = mat % density * mat % atom_density
|
||||
|
||||
! Calculate density in g/cm^3.
|
||||
mat % density_gpcc = ZERO
|
||||
do j = 1, mat % n_nuclides
|
||||
if (run_CE) then
|
||||
awr = nuclides(mat % nuclide(j)) % awr
|
||||
else
|
||||
awr = ONE
|
||||
end if
|
||||
mat % density_gpcc = mat % density_gpcc &
|
||||
+ mat % atom_density(j) * awr * MASS_NEUTRON / N_AVOGADRO
|
||||
end do
|
||||
end associate
|
||||
end do
|
||||
|
||||
end subroutine normalize_ao
|
||||
|
||||
subroutine read_ce_cross_sections(nuc_temps, sab_temps)
|
||||
type(VectorReal), intent(in) :: nuc_temps(:)
|
||||
type(VectorReal), intent(in) :: sab_temps(:)
|
||||
|
||||
integer :: i, j
|
||||
integer :: i_nuclide
|
||||
integer :: i_element
|
||||
integer :: i_sab
|
||||
integer(C_INT) :: n
|
||||
integer(HID_T) :: file_id
|
||||
integer(HID_T) :: group_id
|
||||
real(C_DOUBLE) :: dummy
|
||||
logical :: mp_found ! if windowed multipole libraries were found
|
||||
character(MAX_WORD_LEN) :: name
|
||||
character(MAX_FILE_LEN) :: filename
|
||||
character(3) :: element
|
||||
type(SetChar) :: already_read
|
||||
type(SetChar) :: element_already_read
|
||||
|
||||
interface
|
||||
subroutine photon_from_hdf5(group) bind(C)
|
||||
import HID_T
|
||||
integer(HID_T), value :: group
|
||||
end subroutine
|
||||
|
||||
subroutine read_ce_cross_sections_c() bind(C)
|
||||
end subroutine
|
||||
end interface
|
||||
|
||||
allocate(nuclides(n_nuclides))
|
||||
|
||||
! Read cross sections
|
||||
do i = 1, size(materials)
|
||||
do j = 1, size(materials(i) % names)
|
||||
name = materials(i) % names(j)
|
||||
|
||||
if (.not. already_read % contains(name)) then
|
||||
filename = library_path(LIBRARY_NEUTRON, to_lower(name))
|
||||
i_nuclide = nuclide_dict % get(to_lower(name))
|
||||
|
||||
call write_message('Reading ' // trim(name) // ' from ' // &
|
||||
trim(filename), 6)
|
||||
|
||||
! Open file and make sure version is sufficient
|
||||
file_id = file_open(filename, 'r')
|
||||
call check_data_version(file_id)
|
||||
|
||||
! Read nuclide data from HDF5
|
||||
group_id = open_group(file_id, name)
|
||||
call nuclides(i_nuclide) % from_hdf5(group_id, nuc_temps(i_nuclide), &
|
||||
temperature_method, temperature_tolerance, temperature_range, &
|
||||
master, i_nuclide)
|
||||
call close_group(group_id)
|
||||
call file_close(file_id)
|
||||
|
||||
! 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), &
|
||||
nuclides(i_nuclide) % grid(1) % energy(1))
|
||||
energy_max(NEUTRON) = min(energy_max(NEUTRON), nuclides(i_nuclide) % &
|
||||
grid(1) % energy(size(nuclides(i_nuclide) % grid(1) % energy)))
|
||||
call set_particle_energy_bounds(NEUTRON, energy_min(NEUTRON), &
|
||||
energy_max(NEUTRON))
|
||||
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
|
||||
filename = library_path(LIBRARY_PHOTON, to_lower(element))
|
||||
i_element = element_dict % get(element)
|
||||
call write_message('Reading ' // trim(element) // ' from ' // &
|
||||
trim(filename), 6)
|
||||
|
||||
! Open file and make sure version is sufficient
|
||||
file_id = file_open(filename, 'r')
|
||||
call check_data_version(file_id)
|
||||
|
||||
! Read element data from HDF5
|
||||
group_id = open_group(file_id, element)
|
||||
|
||||
call photon_from_hdf5(group_id)
|
||||
call close_group(group_id)
|
||||
call file_close(file_id)
|
||||
|
||||
! 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
|
||||
|
||||
! Check if material is fissionable
|
||||
if (nuclides(materials(i) % nuclide(j)) % fissionable) then
|
||||
call materials(i) % set_fissionable(.true.)
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
|
||||
call read_ce_cross_sections_c()
|
||||
|
||||
! Set up logarithmic grid for nuclides
|
||||
do i = 1, size(nuclides)
|
||||
call nuclides(i) % init_grid()
|
||||
end do
|
||||
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
|
||||
if (.not. allocated(materials(i) % sab_names)) cycle
|
||||
|
||||
do j = 1, size(materials(i) % sab_names)
|
||||
! Get name of S(a,b) table
|
||||
name = materials(i) % sab_names(j)
|
||||
|
||||
if (.not. already_read % contains(name)) then
|
||||
filename = library_path(LIBRARY_THERMAL, to_lower(name))
|
||||
i_sab = sab_dict % get(to_lower(name))
|
||||
|
||||
call write_message('Reading ' // trim(name) // ' from ' // &
|
||||
trim(filename), 6)
|
||||
|
||||
! Open file and make sure version matches
|
||||
file_id = file_open(filename, 'r')
|
||||
call check_data_version(file_id)
|
||||
|
||||
! Read S(a,b) data from HDF5
|
||||
group_id = open_group(file_id, name)
|
||||
n = sab_temps(i_sab) % size()
|
||||
if (n > 0) then
|
||||
call sab_from_hdf5(group_id, sab_temps(i_sab) % data(1), n)
|
||||
else
|
||||
! In this case, data(1) doesn't exist, so we just pass a dummy value
|
||||
call sab_from_hdf5(group_id, dummy, n)
|
||||
end if
|
||||
|
||||
call close_group(group_id)
|
||||
call file_close(file_id)
|
||||
|
||||
! Add name to dictionary
|
||||
call already_read % add(name)
|
||||
end if
|
||||
end do
|
||||
|
||||
! Associate S(a,b) tables with specific nuclides
|
||||
call materials(i) % assign_sab_tables()
|
||||
end do
|
||||
|
||||
! Show which nuclide results in lowest energy for neutron transport
|
||||
do i = 1, size(nuclides)
|
||||
! If a nuclide is present in a material that's not used in the model, its
|
||||
! 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
|
||||
call write_message("Maximum neutron transport energy: " // &
|
||||
trim(to_str(energy_max(NEUTRON))) // " eV for " // &
|
||||
trim(adjustl(nuclides(i) % name)), 7)
|
||||
if (master .and. energy_max(NEUTRON) < 20.e6) call warning("Maximum &
|
||||
&neutron energy is below 20 MeV. This may bias the results.")
|
||||
exit
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
|
||||
! If the user wants multipole, make sure we found a multipole library.
|
||||
if (temperature_multipole) then
|
||||
mp_found = .false.
|
||||
do i = 1, size(nuclides)
|
||||
if (nuclides(i) % mp_present) then
|
||||
mp_found = .true.
|
||||
exit
|
||||
end if
|
||||
end do
|
||||
if (master .and. .not. mp_found) call warning("Windowed multipole &
|
||||
&functionality is turned on, but no multipole libraries were found. &
|
||||
&Make sure that windowed multipole data is present in your &
|
||||
&cross_sections.xml file.")
|
||||
end if
|
||||
|
||||
call already_read % clear()
|
||||
call element_already_read % clear()
|
||||
|
||||
end subroutine read_ce_cross_sections
|
||||
|
||||
!===============================================================================
|
||||
! READ_MULTIPOLE_DATA checks for the existence of a multipole library in the
|
||||
! directory and loads it using multipole_read
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_multipole_data(i_table)
|
||||
|
||||
integer, intent(in) :: i_table ! index in nuclides/sab_tables
|
||||
|
||||
logical :: file_exists ! Does multipole library exist?
|
||||
character(7) :: readable ! Is multipole library readable?
|
||||
character(MAX_FILE_LEN) :: filename ! Path to multipole xs library
|
||||
integer(HID_T) :: file_id
|
||||
integer(HID_T) :: group_id
|
||||
|
||||
interface
|
||||
subroutine nuclide_load_multipole(ptr, group) bind(C)
|
||||
import C_PTR, HID_T
|
||||
type(C_PTR), value :: ptr
|
||||
integer(HID_T), value :: group
|
||||
end subroutine
|
||||
end interface
|
||||
|
||||
associate (nuc => nuclides(i_table))
|
||||
|
||||
! Look for WMP data in cross_sections.xml
|
||||
if (library_present(LIBRARY_WMP, to_lower(nuc % name))) then
|
||||
filename = library_path(LIBRARY_WMP, to_lower(nuc % name))
|
||||
else
|
||||
nuc % mp_present = .false.
|
||||
return
|
||||
end if
|
||||
|
||||
! Check if Multipole library exists and is readable
|
||||
inquire(FILE=filename, EXIST=file_exists, READ=readable)
|
||||
if (.not. file_exists) then
|
||||
nuc % mp_present = .false.
|
||||
return
|
||||
elseif (readable(1:3) == 'NO') then
|
||||
call fatal_error("Multipole library '" // trim(filename) // "' is not &
|
||||
&readable! Change file permissions with chmod command.")
|
||||
end if
|
||||
|
||||
! Display message
|
||||
call write_message("Reading " // trim(nuc % name) // " WMP data from " &
|
||||
// filename, 6)
|
||||
|
||||
! Open file and make sure version is sufficient
|
||||
file_id = file_open(filename, 'r')
|
||||
call check_wmp_version(file_id)
|
||||
|
||||
! Read nuclide data from HDF5
|
||||
group_id = open_group(file_id, nuc % name)
|
||||
nuc % mp_present = .true.
|
||||
call nuclide_load_multipole(nuc % ptr, group_id)
|
||||
call close_group(group_id)
|
||||
|
||||
! Close the file
|
||||
call file_close(file_id)
|
||||
|
||||
end associate
|
||||
|
||||
end subroutine read_multipole_data
|
||||
|
||||
end module input_xml
|
||||
|
|
|
|||
|
|
@ -1,311 +0,0 @@
|
|||
module list_header
|
||||
|
||||
!===============================================================================
|
||||
! LIST_HEADER module
|
||||
!
|
||||
! This module contains a linked list structure with convenience methods such as
|
||||
! append, contains, remove, index, get_item, size, etc. This is an updated
|
||||
! implementation with type-bound procedures (F2003).
|
||||
!===============================================================================
|
||||
|
||||
use constants, only: ERROR_INT, ERROR_REAL, MAX_WORD_LEN
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! LISTELEM* types hold one piece of data and a pointer to the next piece of data
|
||||
!===============================================================================
|
||||
|
||||
type :: ListElemChar
|
||||
character(MAX_WORD_LEN) :: data
|
||||
type(ListElemChar), pointer :: next => null()
|
||||
type(ListElemChar), pointer :: prev => null()
|
||||
end type ListElemChar
|
||||
|
||||
!===============================================================================
|
||||
! LIST* types contain the linked list with convenience methods. We originally
|
||||
! considered using unlimited polymorphism to provide a single type, but compiler
|
||||
! support is still spotty, and in many cases it doesn't prevent duplication of
|
||||
! code. For the time being, a separate derived type exists for each datatype.
|
||||
!===============================================================================
|
||||
|
||||
type, public :: ListChar
|
||||
private
|
||||
integer :: count = 0 ! Number of elements in list
|
||||
|
||||
! Used in get_item for fast sequential lookups
|
||||
integer :: last_index = huge(0)
|
||||
type(ListElemChar), pointer :: last_elem => null()
|
||||
|
||||
! Pointers to beginning and end of list
|
||||
type(ListElemChar), public, pointer :: head => null()
|
||||
type(ListElemChar), public, pointer :: tail => null()
|
||||
contains
|
||||
procedure :: append => list_append_char ! Add item to end of list
|
||||
procedure :: clear => list_clear_char ! Remove all items
|
||||
procedure :: contains => list_contains_char ! Does list contain?
|
||||
procedure :: get_item => list_get_item_char ! Get i-th item in list
|
||||
procedure :: index => list_index_char ! Determine index of given item
|
||||
procedure :: insert => list_insert_char ! Insert item in i-th position
|
||||
procedure :: remove => list_remove_char ! Remove specified item
|
||||
procedure :: size => list_size_char ! Size of list
|
||||
end type ListChar
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! LIST_APPEND appends an item to the end of the list. If the list is empty, it
|
||||
! becomes the first item.
|
||||
!===============================================================================
|
||||
|
||||
subroutine list_append_char(this, data)
|
||||
class(ListChar) :: this
|
||||
character(*) :: data
|
||||
|
||||
type(ListElemChar), pointer :: elem
|
||||
|
||||
! Create element and set dat
|
||||
allocate(elem)
|
||||
elem % data = data
|
||||
|
||||
if (.not. associated(this % head)) then
|
||||
! If list is empty, set head and tail to new element
|
||||
this % head => elem
|
||||
this % tail => elem
|
||||
else
|
||||
! Otherwise append element at end of list
|
||||
this % tail % next => elem
|
||||
elem % prev => this % tail
|
||||
this % tail => this % tail % next
|
||||
end if
|
||||
|
||||
this % count = this % count + 1
|
||||
|
||||
end subroutine list_append_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_CLEAR removes all elements from the list
|
||||
!===============================================================================
|
||||
|
||||
subroutine list_clear_char(this)
|
||||
class(ListChar) :: this
|
||||
|
||||
type(ListElemChar), pointer :: current => null()
|
||||
type(ListElemChar), pointer :: next => null()
|
||||
|
||||
if (this % count > 0) then
|
||||
current => this % head
|
||||
do while (associated(current))
|
||||
! Set pointer to next element
|
||||
next => current % next
|
||||
|
||||
! Deallocate memory for current element
|
||||
deallocate(current)
|
||||
|
||||
! Move to next element
|
||||
current => next
|
||||
end do
|
||||
|
||||
nullify(this % head)
|
||||
nullify(this % tail)
|
||||
this % count = 0
|
||||
end if
|
||||
|
||||
end subroutine list_clear_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_CONTAINS determines whether the list contains a specified item. Since it
|
||||
! relies on the index method, it is O(n).
|
||||
!===============================================================================
|
||||
|
||||
function list_contains_char(this, data) result(in_list)
|
||||
class(ListChar) :: this
|
||||
character(*) :: data
|
||||
logical :: in_list
|
||||
|
||||
in_list = (this % index(data) > 0)
|
||||
|
||||
end function list_contains_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_GET_ITEM returns the item in the list at position 'i_list'. If the index
|
||||
! is out of bounds, an error code is returned.
|
||||
! ===============================================================================
|
||||
|
||||
function list_get_item_char(this, i_list) result(data)
|
||||
class(ListChar) :: this
|
||||
integer :: i_list
|
||||
character(MAX_WORD_LEN) :: data
|
||||
|
||||
integer :: last_index
|
||||
|
||||
if (i_list < 1 .or. i_list > this % count) then
|
||||
! Check for index out of bounds
|
||||
data = ""
|
||||
elseif (i_list == 1) then
|
||||
data = this % head % data
|
||||
this % last_index = 1
|
||||
this % last_elem => this % head
|
||||
elseif (i_list == this % count) then
|
||||
data = this % tail % data
|
||||
this % last_index = this % count
|
||||
this % last_elem => this % tail
|
||||
else
|
||||
if (i_list < this % last_index) then
|
||||
this % last_index = 1
|
||||
this % last_elem => this % head
|
||||
end if
|
||||
|
||||
do last_index = this % last_index + 1, i_list
|
||||
this % last_elem => this % last_elem % next
|
||||
this % last_index = last_index
|
||||
end do
|
||||
data = this % last_elem % data
|
||||
end if
|
||||
|
||||
end function list_get_item_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_INDEX determines the first index in the list that contains 'data'. If
|
||||
! 'data' is not present in the list, the return value is -1.
|
||||
!===============================================================================
|
||||
|
||||
function list_index_char(this, data) result(i_list)
|
||||
|
||||
class(ListChar) :: this
|
||||
character(*) :: data
|
||||
integer :: i_list
|
||||
|
||||
type(ListElemChar), pointer :: elem
|
||||
|
||||
i_list = 0
|
||||
elem => this % head
|
||||
do while (associated(elem))
|
||||
i_list = i_list + 1
|
||||
if (data == elem % data) exit
|
||||
elem => elem % next
|
||||
end do
|
||||
|
||||
! Check if we reached the end of the list
|
||||
if (.not. associated(elem)) i_list = -1
|
||||
|
||||
end function list_index_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_INSERT inserts 'data' at index 'i_list' within the list. If 'i_list'
|
||||
! exceeds the size of the list, the data is appends at the end of the list.
|
||||
!===============================================================================
|
||||
|
||||
subroutine list_insert_char(this, i_list, data)
|
||||
|
||||
class(ListChar) :: this
|
||||
integer :: i_list
|
||||
character(*) :: data
|
||||
|
||||
integer :: i
|
||||
type(ListElemChar), pointer :: elem => null()
|
||||
type(ListElemChar), pointer :: new_elem => null()
|
||||
|
||||
if (i_list > this % count) then
|
||||
! Check whether specified index is greater than number of elements -- if
|
||||
! so, just append it to the end of the list
|
||||
call this % append(data)
|
||||
|
||||
else if (i_list == 1) then
|
||||
! Check for new head element
|
||||
allocate(new_elem)
|
||||
new_elem % data = data
|
||||
new_elem % next => this % head
|
||||
this % head => new_elem
|
||||
this % count = this % count + 1
|
||||
|
||||
else
|
||||
! Default case with new element somewhere in middle of list
|
||||
if (i_list >= this % last_index) then
|
||||
i = this % last_index
|
||||
elem => this % last_elem
|
||||
else
|
||||
i = 0
|
||||
elem => this % head
|
||||
end if
|
||||
do while (associated(elem))
|
||||
if (i == i_list) then
|
||||
! Allocate new element
|
||||
allocate(new_elem)
|
||||
new_elem % data = data
|
||||
|
||||
! Put it before the i-th element
|
||||
new_elem % prev => elem % prev
|
||||
new_elem % next => elem
|
||||
new_elem % prev % next => new_elem
|
||||
new_elem % next % prev => new_elem
|
||||
this % count = this % count + 1
|
||||
this % last_index = i_list
|
||||
this % last_elem => new_elem
|
||||
exit
|
||||
end if
|
||||
i = i + 1
|
||||
elem => elem % next
|
||||
end do
|
||||
end if
|
||||
|
||||
end subroutine list_insert_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_REMOVE removes the first item in the list that contains 'data'. If 'data'
|
||||
! is not in the list, no action is taken.
|
||||
!===============================================================================
|
||||
|
||||
subroutine list_remove_char(this, data)
|
||||
|
||||
class(ListChar) :: this
|
||||
character(*) :: data
|
||||
|
||||
type(ListElemChar), pointer :: elem => null()
|
||||
|
||||
elem => this % head
|
||||
do while (associated(elem))
|
||||
! Check for matching data
|
||||
if (elem % data == data) then
|
||||
|
||||
! Determine whether the current element is the head, tail, or a middle
|
||||
! element
|
||||
if (associated(elem, this % head)) then
|
||||
this % head => elem % next
|
||||
if (associated(elem, this % tail)) nullify(this % tail)
|
||||
if (associated(this % head)) nullify(this % head % prev)
|
||||
deallocate(elem)
|
||||
else if (associated(elem, this % tail)) then
|
||||
this % tail => elem % prev
|
||||
deallocate(this % tail % next)
|
||||
else
|
||||
elem % prev % next => elem % next
|
||||
elem % next % prev => elem % prev
|
||||
deallocate(elem)
|
||||
end if
|
||||
|
||||
! Decrease count and exit
|
||||
this % count = this % count - 1
|
||||
exit
|
||||
end if
|
||||
|
||||
! Advance pointers
|
||||
elem => elem % next
|
||||
end do
|
||||
|
||||
end subroutine list_remove_char
|
||||
|
||||
!===============================================================================
|
||||
! LIST_SIZE returns the number of elements in the list
|
||||
!===============================================================================
|
||||
|
||||
function list_size_char(this) result(size)
|
||||
|
||||
class(ListChar) :: this
|
||||
integer :: size
|
||||
|
||||
size = this % count
|
||||
|
||||
end function list_size_char
|
||||
|
||||
end module list_header
|
||||
886
src/material.cpp
886
src/material.cpp
|
|
@ -1,7 +1,8 @@
|
|||
#include "openmc/material.h"
|
||||
|
||||
#include <algorithm> // for min, max
|
||||
#include <algorithm> // for min, max, sort, fill
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
|
@ -9,12 +10,21 @@
|
|||
#include "xtensor/xoperation.hpp"
|
||||
#include "xtensor/xview.hpp"
|
||||
|
||||
#include "openmc/capi.h"
|
||||
#include "openmc/cross_sections.h"
|
||||
#include "openmc/container_util.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/message_passing.h"
|
||||
#include "openmc/mgxs_interface.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/photon.h"
|
||||
#include "openmc/search.h"
|
||||
#include "openmc/settings.h"
|
||||
#include "openmc/simulation.h"
|
||||
#include "openmc/string_utils.h"
|
||||
#include "openmc/thermal.h"
|
||||
#include "openmc/xml_interface.h"
|
||||
|
||||
namespace openmc {
|
||||
|
|
@ -42,6 +52,230 @@ Material::Material(pugi::xml_node node)
|
|||
fatal_error("Must specify id of material in materials XML file.");
|
||||
}
|
||||
|
||||
if (check_for_node(node, "name")) {
|
||||
name_ = get_node_value(node, "name");
|
||||
}
|
||||
|
||||
if (check_for_node(node, "depletable")) {
|
||||
depletable_ = get_node_value_bool(node, "depletable");
|
||||
}
|
||||
|
||||
bool sum_density {false};
|
||||
pugi::xml_node density_node = node.child("density");
|
||||
std::string units;
|
||||
if (density_node) {
|
||||
units = get_node_value(density_node, "units");
|
||||
if (units == "sum") {
|
||||
sum_density = true;
|
||||
} else if (units == "macro") {
|
||||
if (check_for_node(density_node, "value")) {
|
||||
density_ = std::stod(get_node_value(density_node, "value"));
|
||||
} else {
|
||||
density_ = 1.0;
|
||||
}
|
||||
} else {
|
||||
double val = std::stod(get_node_value(density_node, "value"));
|
||||
if (val <= 0.0) {
|
||||
fatal_error("Need to specify a positive density on material "
|
||||
+ std::to_string(id_) + ".");
|
||||
}
|
||||
|
||||
if (units == "g/cc" || units == "g/cm3") {
|
||||
density_ = -val;
|
||||
} else if (units == "kg/m3") {
|
||||
density_ = -1.0e-3 * val;
|
||||
} else if (units == "atom/b-cm") {
|
||||
density_ = val;
|
||||
} else if (units == "atom/cc" || units == "atom/cm3") {
|
||||
density_ = 1.0e-24 * val;
|
||||
} else {
|
||||
fatal_error("Unknown units '" + units + "' specified on material "
|
||||
+ std::to_string(id_) + ".");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fatal_error("Must specify <density> element in material "
|
||||
+ std::to_string(id_) + ".");
|
||||
}
|
||||
|
||||
if (node.child("element")) {
|
||||
fatal_error("Unable to add an element to material " + std::to_string(id_) +
|
||||
" since the element option has been removed from the xml input. "
|
||||
"Elements can only be added via the Python API, which will expand "
|
||||
"elements into their natural nuclides.");
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// READ AND PARSE <nuclide> TAGS
|
||||
|
||||
// Check to ensure material has at least one nuclide
|
||||
if (!check_for_node(node, "nuclide") && !check_for_node(node, "macroscopic")) {
|
||||
fatal_error("No macroscopic data or nuclides specified on material "
|
||||
+ std::to_string(id_));
|
||||
}
|
||||
|
||||
// Create list of macroscopic x/s based on those specified, just treat
|
||||
// them as nuclides. This is all really a facade so the user thinks they
|
||||
// are entering in macroscopic data but the code treats them the same
|
||||
// as nuclides internally.
|
||||
// Get pointer list of XML <macroscopic>
|
||||
auto node_macros = node.children("macroscopic");
|
||||
int num_macros = std::distance(node_macros.begin(), node_macros.end());
|
||||
|
||||
std::vector<std::string> names;
|
||||
std::vector<double> densities;
|
||||
if (settings::run_CE && num_macros > 0) {
|
||||
fatal_error("Macroscopic can not be used in continuous-energy mode.");
|
||||
} else if (num_macros > 1) {
|
||||
fatal_error("Only one macroscopic object permitted per material, "
|
||||
+ std::to_string(id_));
|
||||
} else if (num_macros == 1) {
|
||||
pugi::xml_node node_nuc = *node_macros.begin();
|
||||
|
||||
// Check for empty name on nuclide
|
||||
if (!check_for_node(node_nuc, "name")) {
|
||||
fatal_error("No name specified on macroscopic data in material "
|
||||
+ std::to_string(id_));
|
||||
}
|
||||
|
||||
// store nuclide name
|
||||
std::string name = get_node_value(node_nuc, "name", false, true);
|
||||
names.push_back(name);
|
||||
|
||||
// Set density for macroscopic data
|
||||
if (units == "macro") {
|
||||
densities.push_back(1.0);
|
||||
} else {
|
||||
fatal_error("Units can only be macro for macroscopic data " + name);
|
||||
}
|
||||
} else {
|
||||
// Create list of nuclides based on those specified
|
||||
for (auto node_nuc : node.children("nuclide")) {
|
||||
// Check for empty name on nuclide
|
||||
if (!check_for_node(node_nuc, "name")) {
|
||||
fatal_error("No name specified on nuclide in material "
|
||||
+ std::to_string(id_));
|
||||
}
|
||||
|
||||
// store nuclide name
|
||||
std::string name = get_node_value(node_nuc, "name", false, true);
|
||||
names.push_back(name);
|
||||
|
||||
// Check if no atom/weight percents were specified or if both atom and
|
||||
// weight percents were specified
|
||||
if (units == "macro") {
|
||||
densities.push_back(1.0);
|
||||
} else {
|
||||
bool has_ao = check_for_node(node_nuc, "ao");
|
||||
bool has_wo = check_for_node(node_nuc, "wo");
|
||||
|
||||
if (!has_ao && !has_wo) {
|
||||
fatal_error("No atom or weight percent specified for nuclide: " + name);
|
||||
} else if (has_ao && has_wo) {
|
||||
fatal_error("Cannot specify both atom and weight percents for a "
|
||||
"nuclide: " + name);
|
||||
}
|
||||
|
||||
// Copy atom/weight percents
|
||||
if (has_ao) {
|
||||
densities.push_back(std::stod(get_node_value(node_nuc, "ao")));
|
||||
} else {
|
||||
densities.push_back(-std::stod(get_node_value(node_nuc, "wo")));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// READ AND PARSE <isotropic> element
|
||||
|
||||
std::vector<std::string> iso_lab;
|
||||
if (check_for_node(node, "isotropic")) {
|
||||
iso_lab = get_node_array<std::string>(node, "isotropic");
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// COPY NUCLIDES TO ARRAYS IN MATERIAL
|
||||
|
||||
// allocate arrays in Material object
|
||||
auto n = names.size();
|
||||
nuclide_.reserve(n);
|
||||
atom_density_ = xt::empty<double>({n});
|
||||
if (settings::photon_transport) element_.reserve(n);
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
const auto& name {names[i]};
|
||||
|
||||
// Check that this nuclide is listed in the cross_sections.xml file
|
||||
LibraryKey key {Library::Type::neutron, name};
|
||||
if (data::library_map.find(key) == data::library_map.end()) {
|
||||
fatal_error("Could not find nuclide " + name + " in cross_sections.xml.");
|
||||
}
|
||||
|
||||
// If this nuclide hasn't been encountered yet, we need to add its name
|
||||
// and alias to the nuclide_dict
|
||||
if (data::nuclide_map.find(name) == data::nuclide_map.end()) {
|
||||
int index = data::nuclide_map.size();
|
||||
data::nuclide_map[name] = index;
|
||||
nuclide_.push_back(index);
|
||||
} else {
|
||||
nuclide_.push_back(data::nuclide_map[name]);
|
||||
}
|
||||
|
||||
// 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 (settings::photon_transport) {
|
||||
int pos = name.find_first_of("0123456789");
|
||||
std::string element = name.substr(0, pos);
|
||||
|
||||
// Make sure photon cross section data is available
|
||||
LibraryKey key {Library::Type::photon, element};
|
||||
if (data::library_map.find(key) == data::library_map.end()) {
|
||||
fatal_error("Could not find element " + element
|
||||
+ " in cross_sections.xml.");
|
||||
}
|
||||
|
||||
if (data::element_map.find(element) == data::element_map.end()) {
|
||||
int index = data::element_map.size();
|
||||
data::element_map[element] = index;
|
||||
element_.push_back(index);
|
||||
} else {
|
||||
element_.push_back(data::element_map[element]);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy atom/weight percent
|
||||
atom_density_(i) = densities[i];
|
||||
}
|
||||
|
||||
if (settings::run_CE) {
|
||||
// By default, isotropic-in-lab is not used
|
||||
if (iso_lab.size() > 0) {
|
||||
p0_.resize(n);
|
||||
|
||||
// Apply isotropic-in-lab treatment to specified nuclides
|
||||
for (int j = 0; j < n; ++j) {
|
||||
for (const auto& nuc : iso_lab) {
|
||||
if (names[j] == nuc) {
|
||||
p0_[j] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check to make sure either all atom percents or all weight percents are
|
||||
// given
|
||||
if (!(xt::all(atom_density_ >= 0.0) || xt::all(atom_density_ <= 0.0))) {
|
||||
fatal_error("Cannot mix atom and weight percents in material "
|
||||
+ std::to_string(id_));
|
||||
}
|
||||
|
||||
// Determine density if it is a sum value
|
||||
if (sum_density) density_ = xt::sum(atom_density_)();
|
||||
|
||||
|
||||
if (check_for_node(node, "temperature")) {
|
||||
temperature_ = std::stod(get_node_value(node, "temperature"));
|
||||
}
|
||||
|
|
@ -49,6 +283,170 @@ Material::Material(pugi::xml_node node)
|
|||
if (check_for_node(node, "volume")) {
|
||||
volume_ = std::stod(get_node_value(node, "volume"));
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// READ AND PARSE <sab> TAG FOR THERMAL SCATTERING DATA
|
||||
if (settings::run_CE) {
|
||||
// Loop over <sab> elements
|
||||
|
||||
std::vector<std::string> sab_names;
|
||||
for (auto node_sab : node.children("sab")) {
|
||||
// Determine name of thermal scattering table
|
||||
if (!check_for_node(node_sab, "name")) {
|
||||
fatal_error("Need to specify <name> for thermal scattering table.");
|
||||
}
|
||||
std::string name = get_node_value(node_sab, "name");
|
||||
sab_names.push_back(name);
|
||||
|
||||
// Read the fraction of nuclei affected by this thermal scattering table
|
||||
double fraction = 1.0;
|
||||
if (check_for_node(node_sab, "fraction")) {
|
||||
fraction = std::stod(get_node_value(node_sab, "fraction"));
|
||||
}
|
||||
|
||||
// Check that the thermal scattering table is listed in the
|
||||
// cross_sections.xml file
|
||||
LibraryKey key {Library::Type::thermal, name};
|
||||
if (data::library_map.find(key) == data::library_map.end()) {
|
||||
fatal_error("Could not find thermal scattering data " + name +
|
||||
" in cross_sections.xml file.");
|
||||
}
|
||||
|
||||
// Determine index of thermal scattering data in global
|
||||
// data::thermal_scatt array
|
||||
int index_table;
|
||||
if (data::thermal_scatt_map.find(name) == data::thermal_scatt_map.end()) {
|
||||
index_table = data::thermal_scatt_map.size();
|
||||
data::thermal_scatt_map[name] = index_table;
|
||||
} else {
|
||||
index_table = data::thermal_scatt_map[name];
|
||||
}
|
||||
|
||||
// Add entry to thermal tables vector. For now, we put the nuclide index
|
||||
// as zero since we don't know which nuclides the table is being applied
|
||||
// to yet (this is assigned in init_thermal)
|
||||
thermal_tables_.push_back({index_table, 0, fraction});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Material::finalize()
|
||||
{
|
||||
// Set fissionable if any nuclide is fissionable
|
||||
for (const auto& i_nuc : nuclide_) {
|
||||
if (data::nuclides[i_nuc]->fissionable_) {
|
||||
fissionable_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate material bremsstrahlung data for electrons and positrons
|
||||
if (settings::photon_transport && settings::electron_treatment == ELECTRON_TTB) {
|
||||
this->init_bremsstrahlung();
|
||||
}
|
||||
|
||||
// Assign thermal scattering tables
|
||||
this->init_thermal();
|
||||
|
||||
// Normalize density
|
||||
this->normalize_density();
|
||||
}
|
||||
|
||||
void Material::normalize_density()
|
||||
{
|
||||
bool percent_in_atom = (atom_density_(0) > 0.0);
|
||||
bool density_in_atom = (density_ > 0.0);
|
||||
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
// determine atomic weight ratio
|
||||
int i_nuc = nuclide_[i];
|
||||
double awr = settings::run_CE ?
|
||||
data::nuclides[i_nuc]->awr_ : data::nuclides_MG[i_nuc].awr;
|
||||
|
||||
// if given weight percent, convert all values so that they are divided
|
||||
// by awr. thus, when a sum is done over the values, it's actually
|
||||
// sum(w/awr)
|
||||
if (!percent_in_atom) atom_density_(i) = -atom_density_(i) / awr;
|
||||
}
|
||||
|
||||
// determine normalized atom percents. if given atom percents, this is
|
||||
// straightforward. if given weight percents, the value is w/awr and is
|
||||
// divided by sum(w/awr)
|
||||
atom_density_ /= xt::sum(atom_density_)();
|
||||
|
||||
// Change density in g/cm^3 to atom/b-cm. Since all values are now in
|
||||
// atom percent, the sum needs to be re-evaluated as 1/sum(x*awr)
|
||||
if (!density_in_atom) {
|
||||
double sum_percent = 0.0;
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
int i_nuc = nuclide_[i];
|
||||
double awr = settings::run_CE ?
|
||||
data::nuclides[i_nuc]->awr_ : data::nuclides_MG[i_nuc].awr;
|
||||
sum_percent += atom_density_(i)*awr;
|
||||
}
|
||||
sum_percent = 1.0 / sum_percent;
|
||||
density_ = -density_ * N_AVOGADRO / MASS_NEUTRON * sum_percent;
|
||||
}
|
||||
|
||||
// Calculate nuclide atom densities
|
||||
atom_density_ *= density_;
|
||||
|
||||
// Calculate density in g/cm^3.
|
||||
density_gpcc_ = 0.0;
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
int i_nuc = nuclide_[i];
|
||||
double awr = settings::run_CE ? data::nuclides[i_nuc]->awr_ : 1.0;
|
||||
density_gpcc_ += atom_density_(i) * awr * MASS_NEUTRON / N_AVOGADRO;
|
||||
}
|
||||
}
|
||||
|
||||
void Material::init_thermal()
|
||||
{
|
||||
std::vector<ThermalTable> tables;
|
||||
|
||||
for (const auto& table : thermal_tables_) {
|
||||
// In order to know which nuclide the S(a,b) table applies to, we need
|
||||
// to search through the list of nuclides for one which has a matching
|
||||
// name
|
||||
bool found = false;
|
||||
for (int j = 0; j < nuclide_.size(); ++j) {
|
||||
const auto& name {data::nuclides[nuclide_[j]]->name_};
|
||||
if (contains(data::thermal_scatt[table.index_table]->nuclides_, name)) {
|
||||
tables.push_back({table.index_table, j, table.fraction});
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to make sure thermal scattering table matched a nuclide
|
||||
if (!found) {
|
||||
fatal_error("Thermal scattering table " + data::thermal_scatt[
|
||||
table.index_table]->name_ + " did not match any nuclide on material "
|
||||
+ std::to_string(id_));
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure each nuclide only appears in one table.
|
||||
for (int j = 0; j < tables.size(); ++j) {
|
||||
for (int k = j+1; k < tables.size(); ++k) {
|
||||
if (tables[j].index_nuclide == tables[k].index_nuclide) {
|
||||
int index = nuclide_[tables[j].index_nuclide];
|
||||
auto name = data::nuclides[index]->name_;
|
||||
fatal_error(name + " in material " + std::to_string(id_) + " was found "
|
||||
"in multiple thermal scattering tables. Each nuclide can appear in "
|
||||
"only one table per material.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are multiple S(a,b) tables, we need to make sure that the
|
||||
// entries in i_sab_nuclides are sorted or else they won't be applied
|
||||
// correctly in the cross_section module.
|
||||
std::sort(tables.begin(), tables.end(), [](ThermalTable a, ThermalTable b) {
|
||||
return a.index_nuclide < b.index_nuclide;
|
||||
});
|
||||
|
||||
// Update the list of thermal tables
|
||||
thermal_tables_ = tables;
|
||||
}
|
||||
|
||||
void Material::init_bremsstrahlung()
|
||||
|
|
@ -60,14 +458,8 @@ void Material::init_bremsstrahlung()
|
|||
auto n_k = data::ttb_k_grid.size();
|
||||
auto n_e = data::ttb_e_grid.size();
|
||||
|
||||
// Get pointers to nuclides, elements, densities
|
||||
int32_t index;
|
||||
openmc_get_material_index(id_, &index);
|
||||
int* nuclide_;
|
||||
double* atom_density_;
|
||||
int n;
|
||||
openmc_material_get_densities(index, &nuclide_, &atom_density_, &n);
|
||||
int* element_ = material_element(index);
|
||||
// Determine number of elements
|
||||
int n = element_.size();
|
||||
|
||||
for (int particle = 0; particle < 2; ++particle) {
|
||||
// Loop over logic twice, once for electron, once for positron
|
||||
|
|
@ -99,10 +491,8 @@ void Material::init_bremsstrahlung()
|
|||
// fixed in the future.
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// Get pointer to current element
|
||||
// TODO: off-by-one
|
||||
const auto& elm = data::elements[element_[i] - 1];
|
||||
// TODO: off-by-one
|
||||
double awr = data::nuclides[nuclide_[i] - 1]->awr_;
|
||||
const auto& elm = data::elements[element_[i]];
|
||||
double awr = data::nuclides[nuclide_[i]]->awr_;
|
||||
|
||||
// Get atomic density and mass density of nuclide given atom/weight percent
|
||||
double atom_density = (atom_density_[0] > 0.0) ?
|
||||
|
|
@ -241,6 +631,236 @@ void Material::init_bremsstrahlung()
|
|||
}
|
||||
}
|
||||
|
||||
void Material::init_nuclide_index()
|
||||
{
|
||||
int n = settings::run_CE ?
|
||||
data::nuclides.size() : data::nuclides_MG.size();
|
||||
mat_nuclide_index_.resize(n);
|
||||
std::fill(mat_nuclide_index_.begin(), mat_nuclide_index_.end(), -1);
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
mat_nuclide_index_[nuclide_[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void Material::calculate_xs(const Particle& p) const
|
||||
{
|
||||
// Set all material macroscopic cross sections to zero
|
||||
simulation::material_xs.total = 0.0;
|
||||
simulation::material_xs.absorption = 0.0;
|
||||
simulation::material_xs.fission = 0.0;
|
||||
simulation::material_xs.nu_fission = 0.0;
|
||||
|
||||
if (p.type == static_cast<int>(ParticleType::neutron)) {
|
||||
this->calculate_neutron_xs(p);
|
||||
} else if (p.type == static_cast<int>(ParticleType::photon)) {
|
||||
this->calculate_photon_xs(p);
|
||||
}
|
||||
}
|
||||
|
||||
void Material::calculate_neutron_xs(const Particle& p) const
|
||||
{
|
||||
int neutron = static_cast<int>(ParticleType::neutron);
|
||||
|
||||
// Find energy index on energy grid
|
||||
int i_grid = std::log(p.E/data::energy_min[neutron])/simulation::log_spacing;
|
||||
|
||||
// Determine if this material has S(a,b) tables
|
||||
bool check_sab = (thermal_tables_.size() > 0);
|
||||
|
||||
// Initialize position in i_sab_nuclides
|
||||
int j = 0;
|
||||
|
||||
// Add contribution from each nuclide in material
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
// ======================================================================
|
||||
// CHECK FOR S(A,B) TABLE
|
||||
|
||||
int i_sab = C_NONE;
|
||||
double sab_frac = 0.0;
|
||||
|
||||
// Check if this nuclide matches one of the S(a,b) tables specified.
|
||||
// This relies on thermal_tables_ being sorted by .index_nuclide
|
||||
if (check_sab) {
|
||||
const auto& sab {thermal_tables_[j]};
|
||||
if (i == sab.index_nuclide) {
|
||||
// Get index in sab_tables
|
||||
i_sab = sab.index_table;
|
||||
sab_frac = sab.fraction;
|
||||
|
||||
// If particle energy is greater than the highest energy for the
|
||||
// S(a,b) table, then don't use the S(a,b) table
|
||||
if (p.E > data::thermal_scatt[i_sab]->threshold()) i_sab = C_NONE;
|
||||
|
||||
// Increment position in thermal_tables_
|
||||
++j;
|
||||
|
||||
// Don't check for S(a,b) tables if there are no more left
|
||||
if (j == thermal_tables_.size()) check_sab = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// CALCULATE MICROSCOPIC CROSS SECTION
|
||||
|
||||
// Determine microscopic cross sections for this nuclide
|
||||
int i_nuclide = nuclide_[i];
|
||||
|
||||
// Calculate microscopic cross section for this nuclide
|
||||
const auto& micro {simulation::micro_xs[i_nuclide]};
|
||||
if (p.E != micro.last_E
|
||||
|| p.sqrtkT != micro.last_sqrtkT
|
||||
|| i_sab != micro.index_sab
|
||||
|| sab_frac != micro.sab_frac) {
|
||||
data::nuclides[i_nuclide]->calculate_xs(i_sab, p.E, i_grid,
|
||||
p.sqrtkT, sab_frac);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// ADD TO MACROSCOPIC CROSS SECTION
|
||||
|
||||
// Copy atom density of nuclide in material
|
||||
double atom_density = atom_density_(i);
|
||||
|
||||
// Add contributions to cross sections
|
||||
simulation::material_xs.total += atom_density * micro.total;
|
||||
simulation::material_xs.absorption += atom_density * micro.absorption;
|
||||
simulation::material_xs.fission += atom_density * micro.fission;
|
||||
simulation::material_xs.nu_fission += atom_density * micro.nu_fission;
|
||||
}
|
||||
}
|
||||
|
||||
void Material::calculate_photon_xs(const Particle& p) const
|
||||
{
|
||||
simulation::material_xs.coherent = 0.0;
|
||||
simulation::material_xs.incoherent = 0.0;
|
||||
simulation::material_xs.photoelectric = 0.0;
|
||||
simulation::material_xs.pair_production = 0.0;
|
||||
|
||||
// Add contribution from each nuclide in material
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
// ========================================================================
|
||||
// CALCULATE MICROSCOPIC CROSS SECTION
|
||||
|
||||
// Determine microscopic cross sections for this nuclide
|
||||
int i_element = element_[i];
|
||||
|
||||
// Calculate microscopic cross section for this nuclide
|
||||
const auto& micro {simulation::micro_photon_xs[i_element]};
|
||||
if (p.E != micro.last_E) {
|
||||
data::elements[i_element].calculate_xs(p.E);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// ADD TO MACROSCOPIC CROSS SECTION
|
||||
|
||||
// Copy atom density of nuclide in material
|
||||
double atom_density = atom_density_(i);
|
||||
|
||||
// Add contributions to material macroscopic cross sections
|
||||
simulation::material_xs.total += atom_density * micro.total;
|
||||
simulation::material_xs.coherent += atom_density * micro.coherent;
|
||||
simulation::material_xs.incoherent += atom_density * micro.incoherent;
|
||||
simulation::material_xs.photoelectric += atom_density * micro.photoelectric;
|
||||
simulation::material_xs.pair_production += atom_density * micro.pair_production;
|
||||
}
|
||||
}
|
||||
|
||||
int Material::set_density(double density, std::string units)
|
||||
{
|
||||
if (nuclide_.empty()) {
|
||||
set_errmsg("No nuclides exist in material yet.");
|
||||
return OPENMC_E_ALLOCATE;
|
||||
}
|
||||
|
||||
if (units == "atom/b-cm") {
|
||||
// Set total density based on value provided
|
||||
density_ = density;
|
||||
|
||||
// Determine normalized atom percents
|
||||
double sum_percent = xt::sum(atom_density_)();
|
||||
atom_density_ /= sum_percent;
|
||||
|
||||
// Recalculate nuclide atom densities based on given density
|
||||
atom_density_ *= density;
|
||||
|
||||
// Calculate density in g/cm^3.
|
||||
density_gpcc_ = 0.0;
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
int i_nuc = nuclide_[i];
|
||||
double awr = data::nuclides[i_nuc]->awr_;
|
||||
density_gpcc_ += atom_density_(i) * awr * MASS_NEUTRON / N_AVOGADRO;
|
||||
}
|
||||
} else if (units == "g/cm3" || units == "g/cc") {
|
||||
// Determine factor by which to change densities
|
||||
double previous_density_gpcc = density_gpcc_;
|
||||
double f = density / previous_density_gpcc;
|
||||
|
||||
// Update densities
|
||||
density_gpcc_ = density;
|
||||
density_ *= f;
|
||||
atom_density_ *= f;
|
||||
} else {
|
||||
set_errmsg("Invalid units '" + units + "' specified.");
|
||||
return OPENMC_E_INVALID_ARGUMENT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Material::to_hdf5(hid_t group) const
|
||||
{
|
||||
hid_t material_group = create_group(group, "material " + std::to_string(id_));
|
||||
|
||||
write_attribute(material_group, "depletable", static_cast<int>(depletable_));
|
||||
if (volume_ > 0.0) {
|
||||
write_attribute(material_group, "volume", volume_);
|
||||
}
|
||||
write_dataset(material_group, "name", name_);
|
||||
write_dataset(material_group, "atom_density", density_);
|
||||
|
||||
// Copy nuclide/macro name for each nuclide to vector
|
||||
std::vector<std::string> nuc_names;
|
||||
std::vector<std::string> macro_names;
|
||||
std::vector<double> nuc_densities;
|
||||
if (settings::run_CE) {
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
int i_nuc = nuclide_[i];
|
||||
nuc_names.push_back(data::nuclides[i_nuc]->name_);
|
||||
nuc_densities.push_back(atom_density_(i));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < nuclide_.size(); ++i) {
|
||||
int i_nuc = nuclide_[i];
|
||||
if (data::nuclides_MG[i_nuc].awr != MACROSCOPIC_AWR) {
|
||||
nuc_names.push_back(data::nuclides_MG[i_nuc].name);
|
||||
nuc_densities.push_back(atom_density_(i));
|
||||
} else {
|
||||
macro_names.push_back(data::nuclides_MG[i_nuc].name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write vector to 'nuclides'
|
||||
if (!nuc_names.empty()) {
|
||||
write_dataset(material_group, "nuclides", nuc_names);
|
||||
write_dataset(material_group, "nuclide_densities", nuc_densities);
|
||||
}
|
||||
|
||||
// Write vector to 'macroscopics'
|
||||
if (!macro_names.empty()) {
|
||||
write_dataset(material_group, "macroscopics", macro_names);
|
||||
}
|
||||
|
||||
if (!thermal_tables_.empty()) {
|
||||
std::vector<std::string> sab_names;
|
||||
for (const auto& table : thermal_tables_) {
|
||||
sab_names.push_back(data::thermal_scatt[table.index_table]->name_);
|
||||
}
|
||||
write_dataset(material_group, "sab_names", sab_names);
|
||||
}
|
||||
|
||||
close_group(material_group);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-method functions
|
||||
//==============================================================================
|
||||
|
|
@ -268,35 +888,114 @@ read_materials(pugi::xml_node* node)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" void read_ce_cross_sections_c()
|
||||
{
|
||||
for (auto& mat : model::materials) {
|
||||
// Generate material bremsstrahlung data for electrons and positrons
|
||||
if (settings::photon_transport && settings::electron_treatment == ELECTRON_TTB) {
|
||||
mat->init_bremsstrahlung();
|
||||
}
|
||||
}
|
||||
|
||||
if (settings::photon_transport && settings::electron_treatment == ELECTRON_TTB) {
|
||||
// Determine if minimum/maximum energy for bremsstrahlung is greater/less
|
||||
// than the current minimum/maximum
|
||||
if (data::ttb_e_grid.size() >= 1) {
|
||||
// TODO: off-by-one
|
||||
int photon = static_cast<int>(ParticleType::photon) - 1;
|
||||
int n_e = data::ttb_e_grid.size();
|
||||
data::energy_min[photon] = std::max(data::energy_min[photon], data::ttb_e_grid(1));
|
||||
data::energy_max[photon] = std::min(data::energy_max[photon], data::ttb_e_grid(n_e - 1));
|
||||
}
|
||||
|
||||
// Take logarithm of energies since they are log-log interpolated
|
||||
data::ttb_e_grid = xt::log(data::ttb_e_grid);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// C API
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
openmc_get_material_index(int32_t id, int32_t* index)
|
||||
{
|
||||
auto it = model::material_map.find(id);
|
||||
if (it == model::material_map.end()) {
|
||||
set_errmsg("No material exists with ID=" + std::to_string(id) + ".");
|
||||
return OPENMC_E_INVALID_ID;
|
||||
} else {
|
||||
*index = it->second + 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_add_nuclide(int32_t index, const char* name, double density)
|
||||
{
|
||||
int err = 0;
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
Material* m = model::materials[index - 1];
|
||||
|
||||
// Check if nuclide is already in material
|
||||
for (int i = 0; i < m->nuclide_.size(); ++i) {
|
||||
int i_nuc = m->nuclide_[i];
|
||||
if (data::nuclides[i_nuc]->name_ == name) {
|
||||
double awr = data::nuclides[i_nuc]->awr_;
|
||||
m->density_ += density - m->atom_density_(i);
|
||||
m->density_gpcc_ += (density - m->atom_density_(i))
|
||||
* awr * MASS_NEUTRON / N_AVOGADRO;
|
||||
m->atom_density_(i) = density;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If nuclide wasn't found, extend nuclide/density arrays
|
||||
err = openmc_load_nuclide(name);
|
||||
|
||||
if (err == 0) {
|
||||
// Append new nuclide/density
|
||||
int i_nuc = data::nuclide_map[name];
|
||||
m->nuclide_.push_back(i_nuc);
|
||||
|
||||
auto n = m->nuclide_.size();
|
||||
|
||||
// Create copy of atom_density_ array with one extra entry
|
||||
xt::xtensor<double, 1> atom_density = xt::zeros<double>({n});
|
||||
xt::view(atom_density, xt::range(0, n-1)) = m->atom_density_;
|
||||
atom_density(n) = density;
|
||||
m->atom_density_ = atom_density;
|
||||
|
||||
m->density_ += density;
|
||||
m->density_gpcc_ += density * data::nuclides[i_nuc]->awr_
|
||||
* MASS_NEUTRON / N_AVOGADRO;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
auto& mat = model::materials[index - 1];
|
||||
if (!mat->nuclide_.empty()) {
|
||||
*nuclides = mat->nuclide_.data();
|
||||
*densities = mat->atom_density_.data();
|
||||
*n = mat->nuclide_.size();
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Material atom density array has not been allocated.");
|
||||
return OPENMC_E_ALLOCATE;
|
||||
}
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_get_fissionable(int32_t index, bool* fissionable)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
*fissionable = model::materials[index - 1]->fissionable_;
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_get_id(int32_t index, int32_t* id)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
*id = model::materials[index - 1]->id_;
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_get_volume(int32_t index, double* volume)
|
||||
{
|
||||
|
|
@ -317,6 +1016,67 @@ openmc_material_get_volume(int32_t index, double* volume)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_set_density(int32_t index, double density, const char* units)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
return model::materials[index - 1]->set_density(density, units);
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_set_densities(int32_t index, int n, const char** name, const double* density)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
// TODO: off-by-one
|
||||
auto& mat {model::materials[index - 1]};
|
||||
if (n != mat->nuclide_.size()) {
|
||||
mat->nuclide_.resize(n);
|
||||
mat->atom_density_ = xt::zeros<double>({n});
|
||||
}
|
||||
|
||||
double sum_density = 0.0;
|
||||
for (int i = 0; i < n; ++i) {
|
||||
std::string nuc {name[i]};
|
||||
if (data::nuclide_map.find(nuc) == data::nuclide_map.end()) {
|
||||
int err = openmc_load_nuclide(nuc.c_str());
|
||||
if (err < 0) return err;
|
||||
}
|
||||
|
||||
mat->nuclide_[i] = data::nuclide_map[nuc];
|
||||
mat->atom_density_(i) = density[i];
|
||||
sum_density += density[i];
|
||||
}
|
||||
|
||||
// Set total density to the sum of the vector
|
||||
|
||||
int err = mat->set_density(sum_density, "atom/b-cm");
|
||||
|
||||
// Assign S(a,b) tables
|
||||
mat->init_thermal();
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_set_id(int32_t index, int32_t id)
|
||||
{
|
||||
if (index >= 1 && index <= model::materials.size()) {
|
||||
model::materials[index - 1]->id_ = id;
|
||||
model::material_map[id] = index - 1;
|
||||
return 0;
|
||||
} else {
|
||||
set_errmsg("Index in materials array is out of bounds.");
|
||||
return OPENMC_E_OUT_OF_BOUNDS;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_material_set_volume(int32_t index, double volume)
|
||||
{
|
||||
|
|
@ -335,43 +1095,57 @@ openmc_material_set_volume(int32_t index, double volume)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" int
|
||||
openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end)
|
||||
{
|
||||
if (index_start) *index_start = model::materials.size() + 1;
|
||||
if (index_end) *index_end = model::materials.size() + n;
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
model::materials.push_back(new Material());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
Material* material_pointer(int32_t indx) {return model::materials[indx];}
|
||||
size_t n_materials() { return model::materials.size(); }
|
||||
|
||||
int32_t material_id(Material* mat) {return mat->id_;}
|
||||
int32_t material_id(int32_t i_mat) {return model::materials[i_mat - 1]->id_;}
|
||||
|
||||
void material_set_id(Material* mat, int32_t id, int32_t index)
|
||||
int material_nuclide(int32_t i_mat, int idx)
|
||||
{
|
||||
mat->id_ = id;
|
||||
//TODO: off-by-one
|
||||
model::material_map[id] = index - 1;
|
||||
return model::materials[i_mat - 1]->nuclide_[idx - 1] + 1;
|
||||
}
|
||||
|
||||
bool material_fissionable(Material* mat) {return mat->fissionable;}
|
||||
|
||||
void material_set_fissionable(Material* mat, bool fissionable)
|
||||
int material_nuclide_size(int32_t i_mat)
|
||||
{
|
||||
mat->fissionable = fissionable;
|
||||
return model::materials[i_mat - 1]->nuclide_.size();
|
||||
}
|
||||
|
||||
void material_init_bremsstrahlung(Material* mat)
|
||||
double material_atom_density(int32_t i_mat, int idx)
|
||||
{
|
||||
mat->init_bremsstrahlung();
|
||||
return model::materials[i_mat - 1]->atom_density_(idx - 1);
|
||||
}
|
||||
|
||||
void extend_materials_c(int32_t n)
|
||||
double material_density_gpcc(int32_t i_mat)
|
||||
{
|
||||
model::materials.reserve(model::materials.size() + n);
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
model::materials.push_back(new Material());
|
||||
}
|
||||
return model::materials[i_mat - 1]->density_gpcc_;
|
||||
}
|
||||
|
||||
void free_memory_material_c()
|
||||
int material_nuclide_index(int32_t i_mat, int i_nuc)
|
||||
{
|
||||
return model::materials[i_mat - 1]->mat_nuclide_index_[i_nuc - 1] + 1;
|
||||
}
|
||||
|
||||
void material_calculate_xs(const Particle* p)
|
||||
{
|
||||
model::materials[p->material - 1]->calculate_xs(*p);
|
||||
}
|
||||
|
||||
void free_memory_material()
|
||||
{
|
||||
for (Material *mat : model::materials) {delete mat;}
|
||||
model::materials.clear();
|
||||
|
|
|
|||
|
|
@ -2,860 +2,63 @@ module material_header
|
|||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use constants
|
||||
use dict_header, only: DictIntInt
|
||||
use error
|
||||
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
|
||||
use string, only: to_str, to_f_string, to_c_string
|
||||
|
||||
implicit none
|
||||
|
||||
private
|
||||
public :: free_memory_material
|
||||
public :: openmc_extend_materials
|
||||
public :: openmc_get_material_index
|
||||
public :: openmc_material_add_nuclide
|
||||
public :: openmc_material_get_id
|
||||
public :: openmc_material_get_densities
|
||||
public :: openmc_material_get_volume
|
||||
public :: openmc_material_set_density
|
||||
public :: openmc_material_set_densities
|
||||
public :: openmc_material_set_id
|
||||
public :: material_pointer
|
||||
public :: material_calculate_xs
|
||||
public :: material_id
|
||||
public :: material_nuclide
|
||||
public :: material_nuclide_size
|
||||
public :: material_nuclide_index
|
||||
public :: material_atom_density
|
||||
public :: material_density_gpcc
|
||||
|
||||
interface
|
||||
function material_pointer(mat_ind) bind(C) result(ptr)
|
||||
import C_PTR, C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: mat_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function material_pointer
|
||||
|
||||
function material_id_c(mat_ptr) bind(C, name='material_id') result(id)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T) :: id
|
||||
end function material_id_c
|
||||
|
||||
subroutine material_set_id_c(mat_ptr, id, index) &
|
||||
bind(C, name='material_set_id')
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
integer(C_INT32_T), intent(in), value :: id
|
||||
integer(C_INT32_T), intent(in), value :: index
|
||||
end subroutine material_set_id_c
|
||||
|
||||
function material_fissionable_c(mat_ptr) &
|
||||
bind(C, name='material_fissionable') result(fissionable)
|
||||
import C_PTR, C_BOOL
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
logical(C_BOOL) :: fissionable
|
||||
end function material_fissionable_c
|
||||
|
||||
subroutine material_set_fissionable_c(mat_ptr, fissionable) &
|
||||
bind(C, name='material_set_fissionable')
|
||||
import C_PTR, C_BOOL
|
||||
type(C_PTR), intent(in), value :: mat_ptr
|
||||
logical(C_BOOL), intent(in), value :: fissionable
|
||||
end subroutine material_set_fissionable_c
|
||||
|
||||
subroutine extend_materials_c(n) bind(C)
|
||||
function material_id(i_mat) bind(C) result(id)
|
||||
import C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: n
|
||||
end subroutine extend_materials_c
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
integer(C_INT32_T) :: id
|
||||
end function
|
||||
|
||||
function openmc_material_get_volume(index, volume) result(err) bind(C)
|
||||
import C_INT32_T, C_DOUBLE, C_INT
|
||||
integer(C_INT32_T), value :: index
|
||||
real(C_DOUBLE), intent(out) :: volume
|
||||
integer(C_INT) :: err
|
||||
end function openmc_material_get_volume
|
||||
subroutine material_calculate_xs(p) bind(C)
|
||||
import Particle
|
||||
type(Particle), intent(in) :: p
|
||||
end subroutine
|
||||
|
||||
function material_nuclide(i_mat, idx) bind(C) result(nuc)
|
||||
import C_INT32_T, C_INT
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
integer(C_INT), value :: idx
|
||||
integer(C_INT) :: nuc
|
||||
end function
|
||||
|
||||
function material_nuclide_size(i_mat) bind(C) result(n)
|
||||
import C_INT32_T, C_INT
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
integer(C_INT) :: n
|
||||
end function
|
||||
|
||||
function material_nuclide_index(i_mat, i_nuc) bind(C) result(idx)
|
||||
import C_INT32_T, C_INT
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
integer(C_INT), value :: i_nuc
|
||||
integer(C_INT) :: idx
|
||||
end function
|
||||
|
||||
function material_atom_density(i_mat, idx) bind(C) result(density)
|
||||
import C_INT32_T, C_INT, C_DOUBLE
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
integer(C_INT), value :: idx
|
||||
real(C_DOUBLE) :: density
|
||||
end function
|
||||
|
||||
function material_density_gpcc(i_mat) bind(C) result(density)
|
||||
import C_INT32_T, C_DOUBLE
|
||||
integer(C_INT32_T), value :: i_mat
|
||||
real(C_DOUBLE) :: density
|
||||
end function
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL describes a material by its constituent nuclides
|
||||
!===============================================================================
|
||||
|
||||
type, public :: Material
|
||||
type(C_PTR) :: ptr
|
||||
character(len=104) :: name = "" ! User-defined name
|
||||
integer :: n_nuclides = 0 ! number of nuclides
|
||||
integer, allocatable :: nuclide(:) ! index in nuclides array
|
||||
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
|
||||
|
||||
! To improve performance of tallying, we store an array (direct address
|
||||
! table) that indicates for each nuclide in the global nuclides(:) array the
|
||||
! index of the corresponding nuclide in the Material % nuclide(:) array. If
|
||||
! it is not present in the material, the entry is set to zero.
|
||||
integer, allocatable :: mat_nuclide_index(:)
|
||||
|
||||
! S(a,b) data
|
||||
integer :: n_sab = 0 ! number of S(a,b) tables
|
||||
integer, allocatable :: i_sab_nuclides(:) ! index of corresponding nuclide
|
||||
integer, allocatable :: i_sab_tables(:) ! index in sab_tables
|
||||
real(8), allocatable :: sab_fracs(:) ! how often to use S(a,b)
|
||||
|
||||
! Temporary names read during initialization
|
||||
character(20), allocatable :: names(:) ! isotope names
|
||||
character(20), allocatable :: sab_names(:) ! name of S(a,b) table
|
||||
|
||||
! Does this material contain fissionable nuclides? Is it depletable?
|
||||
logical :: depletable = .false.
|
||||
|
||||
! enforce isotropic scattering in lab for specific nuclides
|
||||
logical :: has_isotropic_nuclides = .false.
|
||||
logical, allocatable :: p0(:)
|
||||
|
||||
contains
|
||||
procedure :: id => material_id
|
||||
procedure :: set_id => material_set_id
|
||||
procedure :: fissionable => material_fissionable
|
||||
procedure :: set_fissionable => material_set_fissionable
|
||||
procedure :: set_density => material_set_density
|
||||
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
|
||||
|
||||
type(Material), public, allocatable, target :: materials(:)
|
||||
|
||||
! Dictionary that maps user IDs to indices in 'materials'
|
||||
type(DictIntInt), public :: material_dict
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL_SET_DENSITY sets the total density of a material in atom/b-cm.
|
||||
!===============================================================================
|
||||
|
||||
function material_id(this) result(id)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T) :: id
|
||||
id = material_id_c(this % ptr)
|
||||
end function material_id
|
||||
|
||||
subroutine material_set_id(this, id, index)
|
||||
class(Material), intent(in) :: this
|
||||
integer(C_INT32_T), intent(in) :: id
|
||||
integer(C_INT32_T), intent(in) :: index
|
||||
call material_set_id_c(this % ptr, id, index)
|
||||
end subroutine material_set_id
|
||||
|
||||
function material_fissionable(this) result(fissionable)
|
||||
class(Material), intent(in) :: this
|
||||
logical(C_BOOL) :: fissionable
|
||||
fissionable = material_fissionable_c(this % ptr)
|
||||
end function material_fissionable
|
||||
|
||||
subroutine material_set_fissionable(this, fissionable)
|
||||
class(Material),intent(in) :: this
|
||||
logical, intent(in) :: fissionable
|
||||
call material_set_fissionable_c(this % ptr, logical(fissionable, C_BOOL))
|
||||
end subroutine material_set_fissionable
|
||||
|
||||
function material_set_density(this, density, units) result(err)
|
||||
class(Material), intent(inout) :: this
|
||||
real(8), intent(in) :: density
|
||||
character(*), intent(in) :: units
|
||||
integer :: err
|
||||
|
||||
integer :: i
|
||||
real(8) :: sum_percent
|
||||
real(8) :: awr
|
||||
real(8) :: previous_density_gpcc
|
||||
real(8) :: f
|
||||
|
||||
if (allocated(this % atom_density)) then
|
||||
err = 0
|
||||
select case (units)
|
||||
case ('atom/b-cm')
|
||||
! Set total density based on value provided
|
||||
this % density = density
|
||||
|
||||
! Determine normalized atom percents
|
||||
sum_percent = sum(this % atom_density)
|
||||
this % atom_density(:) = this % atom_density / sum_percent
|
||||
|
||||
! Recalculate nuclide atom densities based on given density
|
||||
this % atom_density(:) = density * this % atom_density
|
||||
|
||||
! Calculate density in g/cm^3.
|
||||
this % density_gpcc = ZERO
|
||||
do i = 1, this % n_nuclides
|
||||
awr = nuclides(this % nuclide(i)) % awr
|
||||
this % density_gpcc = this % density_gpcc &
|
||||
+ this % atom_density(i) * awr * MASS_NEUTRON / N_AVOGADRO
|
||||
end do
|
||||
case ('g/cm3', 'g/cc')
|
||||
! Determine factor by which to change densities
|
||||
previous_density_gpcc = this % density_gpcc
|
||||
f = density / previous_density_gpcc
|
||||
|
||||
! Update densities
|
||||
this % density_gpcc = density
|
||||
this % density = f * this % density
|
||||
this % atom_density(:) = f * this % atom_density(:)
|
||||
case default
|
||||
err = E_INVALID_ARGUMENT
|
||||
call set_errmsg("Invalid units '" // trim(units) // "' specified.")
|
||||
end select
|
||||
else
|
||||
err = E_ALLOCATE
|
||||
call set_errmsg("Material atom density array hasn't been allocated.")
|
||||
end if
|
||||
end function material_set_density
|
||||
|
||||
!===============================================================================
|
||||
! INIT_NUCLIDE_INDEX creates a mapping from indices in the global nuclides(:)
|
||||
! array to the Material % nuclides array
|
||||
!===============================================================================
|
||||
|
||||
subroutine material_init_nuclide_index(this)
|
||||
class(Material), intent(inout) :: this
|
||||
|
||||
integer :: i
|
||||
|
||||
! Allocate nuclide index array and set to zeros
|
||||
if (allocated(this % mat_nuclide_index)) &
|
||||
deallocate(this % mat_nuclide_index)
|
||||
allocate(this % mat_nuclide_index(n_nuclides))
|
||||
this % mat_nuclide_index(:) = 0
|
||||
|
||||
! Assign entries in the index array
|
||||
do i = 1, this % n_nuclides
|
||||
this % mat_nuclide_index(this % nuclide(i)) = i
|
||||
end do
|
||||
end subroutine material_init_nuclide_index
|
||||
|
||||
!===============================================================================
|
||||
! ASSIGN_SAB_TABLES assigns S(alpha,beta) tables to specific nuclides within
|
||||
! materials so the code knows when to apply bound thermal scattering data
|
||||
!===============================================================================
|
||||
|
||||
subroutine material_assign_sab_tables(this)
|
||||
class(Material), intent(inout) :: this
|
||||
|
||||
integer :: j ! index over nuclides in material
|
||||
integer :: k ! index over S(a,b) tables in material
|
||||
integer :: m ! position for sorting
|
||||
integer :: i_sab
|
||||
integer :: temp_nuclide ! temporary value for sorting
|
||||
integer :: temp_table ! temporary value for sorting
|
||||
real(8) :: temp_frac ! temporary value for sorting
|
||||
logical :: found
|
||||
type(VectorInt) :: i_sab_tables
|
||||
type(VectorInt) :: i_sab_nuclides
|
||||
type(VectorReal) :: sab_fracs
|
||||
|
||||
if (.not. allocated(this % i_sab_tables)) return
|
||||
|
||||
ASSIGN_SAB: do k = 1, size(this % i_sab_tables)
|
||||
! In order to know which nuclide the S(a,b) table applies to, we need
|
||||
! to search through the list of nuclides for one which has a matching
|
||||
! name
|
||||
found = .false.
|
||||
i_sab = this % i_sab_tables(k)
|
||||
FIND_NUCLIDE: do j = 1, size(this % nuclide)
|
||||
if (sab_has_nuclide(i_sab, to_c_string(nuclides(this % nuclide(j)) % name))) then
|
||||
call i_sab_tables % push_back(i_sab)
|
||||
call i_sab_nuclides % push_back(j)
|
||||
call sab_fracs % push_back(this % sab_fracs(k))
|
||||
found = .true.
|
||||
end if
|
||||
end do FIND_NUCLIDE
|
||||
|
||||
! Check to make sure S(a,b) table matched a nuclide
|
||||
if (.not. found) then
|
||||
call fatal_error("S(a,b) table " // trim(this % &
|
||||
sab_names(k)) // " did not match any nuclide on material " &
|
||||
// trim(to_str(this % id())))
|
||||
end if
|
||||
end do ASSIGN_SAB
|
||||
|
||||
! Make sure each nuclide only appears in one table.
|
||||
do j = 1, i_sab_nuclides % size()
|
||||
do k = j+1, i_sab_nuclides % size()
|
||||
if (i_sab_nuclides % data(j) == i_sab_nuclides % data(k)) then
|
||||
call fatal_error(trim( &
|
||||
nuclides(this % nuclide(i_sab_nuclides % data(j))) % name) &
|
||||
// " in material " // trim(to_str(this % id())) // " was found &
|
||||
&in multiple S(a,b) tables. Each nuclide can only appear in &
|
||||
&one S(a,b) table per material.")
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
|
||||
! Update i_sab_tables and i_sab_nuclides
|
||||
deallocate(this % i_sab_tables)
|
||||
deallocate(this % sab_fracs)
|
||||
if (allocated(this % i_sab_nuclides)) deallocate(this % i_sab_nuclides)
|
||||
m = i_sab_tables % size()
|
||||
allocate(this % i_sab_tables(m))
|
||||
allocate(this % i_sab_nuclides(m))
|
||||
allocate(this % sab_fracs(m))
|
||||
this % i_sab_tables(:) = i_sab_tables % data(1:m)
|
||||
this % i_sab_nuclides(:) = i_sab_nuclides % data(1:m)
|
||||
this % sab_fracs(:) = sab_fracs % data(1:m)
|
||||
|
||||
! Clear entries in vectors for next material
|
||||
call i_sab_tables % clear()
|
||||
call i_sab_nuclides % clear()
|
||||
call sab_fracs % clear()
|
||||
|
||||
! If there are multiple S(a,b) tables, we need to make sure that the
|
||||
! entries in i_sab_nuclides are sorted or else they won't be applied
|
||||
! correctly in the cross_section module. The algorithm here is a simple
|
||||
! insertion sort -- don't need anything fancy!
|
||||
|
||||
if (size(this % i_sab_tables) > 1) then
|
||||
SORT_SAB: do k = 2, size(this % i_sab_tables)
|
||||
! Save value to move
|
||||
m = k
|
||||
temp_nuclide = this % i_sab_nuclides(k)
|
||||
temp_table = this % i_sab_tables(k)
|
||||
temp_frac = this % i_sab_tables(k)
|
||||
|
||||
MOVE_OVER: do
|
||||
! Check if insertion value is greater than (m-1)th value
|
||||
if (temp_nuclide >= this % i_sab_nuclides(m-1)) exit
|
||||
|
||||
! Move values over until hitting one that's not larger
|
||||
this % i_sab_nuclides(m) = this % i_sab_nuclides(m-1)
|
||||
this % i_sab_tables(m) = this % i_sab_tables(m-1)
|
||||
this % sab_fracs(m) = this % sab_fracs(m-1)
|
||||
m = m - 1
|
||||
|
||||
! Exit if we've reached the beginning of the list
|
||||
if (m == 1) exit
|
||||
end do MOVE_OVER
|
||||
|
||||
! Put the original value into its new position
|
||||
this % i_sab_nuclides(m) = temp_nuclide
|
||||
this % i_sab_tables(m) = temp_table
|
||||
this % sab_fracs(m) = temp_frac
|
||||
end do SORT_SAB
|
||||
end if
|
||||
|
||||
! Deallocate temporary arrays for names of nuclides and S(a,b) tables
|
||||
if (allocated(this % names)) deallocate(this % names)
|
||||
end subroutine material_assign_sab_tables
|
||||
|
||||
!===============================================================================
|
||||
! MATERIAL_CALCULATE_XS determines the macroscopic cross sections for the
|
||||
! material the particle is currently traveling through.
|
||||
!===============================================================================
|
||||
|
||||
subroutine material_calculate_xs(this, p)
|
||||
class(Material), intent(in) :: this
|
||||
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
|
||||
integer :: i_sab ! index into sab_tables array
|
||||
integer :: j ! index in this % i_sab_nuclides
|
||||
integer :: i_grid ! index into logarithmic mapping array or material
|
||||
! union grid
|
||||
real(8) :: atom_density ! atom density of a nuclide
|
||||
real(8) :: sab_frac ! fraction of atoms affected by S(a,b)
|
||||
logical :: check_sab ! should we check for S(a,b) table?
|
||||
|
||||
! Find energy index on energy grid
|
||||
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)
|
||||
|
||||
! Initialize position in i_sab_nuclides
|
||||
j = 1
|
||||
|
||||
! Add contribution from each nuclide in material
|
||||
do i = 1, this % n_nuclides
|
||||
! ======================================================================
|
||||
! CHECK FOR S(A,B) TABLE
|
||||
|
||||
i_sab = 0
|
||||
sab_frac = ZERO
|
||||
|
||||
! Check if this nuclide matches one of the S(a,b) tables specified.
|
||||
! This relies on i_sab_nuclides being in sorted order
|
||||
if (check_sab) then
|
||||
if (i == this % i_sab_nuclides(j)) then
|
||||
! Get index in sab_tables
|
||||
i_sab = this % i_sab_tables(j)
|
||||
sab_frac = this % sab_fracs(j)
|
||||
|
||||
! If particle energy is greater than the highest energy for the
|
||||
! S(a,b) table, then don't use the S(a,b) table
|
||||
if (p % E > sab_threshold(i_sab)) then
|
||||
i_sab = 0
|
||||
end if
|
||||
|
||||
! Increment position in i_sab_nuclides
|
||||
j = j + 1
|
||||
|
||||
! Don't check for S(a,b) tables if there are no more left
|
||||
if (j > size(this % i_sab_tables)) check_sab = .false.
|
||||
end if
|
||||
end if
|
||||
|
||||
! ======================================================================
|
||||
! CALCULATE MICROSCOPIC CROSS SECTION
|
||||
|
||||
! Determine microscopic cross sections for this nuclide
|
||||
i_nuclide = this % nuclide(i)
|
||||
|
||||
! Calculate microscopic cross section for this nuclide
|
||||
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 + 1 &
|
||||
.or. sab_frac /= micro_xs(i_nuclide) % sab_frac) then
|
||||
call nuclides(i_nuclide) % calculate_xs(i_sab, p % E, i_grid, &
|
||||
p % sqrtkT, sab_frac)
|
||||
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_xs(i_nuclide) % total
|
||||
|
||||
! Add contributions to material macroscopic absorption cross section
|
||||
material_xs % absorption = material_xs % absorption + &
|
||||
atom_density * micro_xs(i_nuclide) % absorption
|
||||
|
||||
! Add contributions to material macroscopic fission cross section
|
||||
material_xs % fission = material_xs % fission + &
|
||||
atom_density * micro_xs(i_nuclide) % fission
|
||||
|
||||
! Add contributions to material macroscopic nu-fission cross section
|
||||
material_xs % nu_fission = material_xs % nu_fission + &
|
||||
atom_density * micro_xs(i_nuclide) % nu_fission
|
||||
end do
|
||||
|
||||
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
|
||||
|
||||
interface
|
||||
subroutine photon_calculate_xs(i_element, E) bind(C)
|
||||
import C_INT, C_DOUBLE
|
||||
integer(C_INT), value :: i_element
|
||||
real(C_DOUBLE), value :: E
|
||||
end subroutine
|
||||
end interface
|
||||
|
||||
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 photon_calculate_xs(i_element, p % E)
|
||||
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
|
||||
!===============================================================================
|
||||
|
||||
subroutine free_memory_material()
|
||||
interface
|
||||
subroutine free_memory_material_c() bind(C)
|
||||
end subroutine free_memory_material_c
|
||||
end interface
|
||||
call free_memory_material_c()
|
||||
n_materials = 0
|
||||
if (allocated(materials)) deallocate(materials)
|
||||
call material_dict % clear()
|
||||
end subroutine free_memory_material
|
||||
|
||||
!===============================================================================
|
||||
! C API FUNCTIONS
|
||||
!===============================================================================
|
||||
|
||||
function openmc_extend_materials(n, index_start, index_end) result(err) bind(C)
|
||||
! Extend the materials array by n elements
|
||||
integer(C_INT32_T), value, intent(in) :: n
|
||||
integer(C_INT32_T), optional, intent(out) :: index_start
|
||||
integer(C_INT32_T), optional, intent(out) :: index_end
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: i
|
||||
type(Material), allocatable :: temp(:) ! temporary materials array
|
||||
|
||||
if (n_materials == 0) then
|
||||
! Allocate materials array
|
||||
allocate(materials(n))
|
||||
else
|
||||
! Allocate materials array with increased size
|
||||
allocate(temp(n_materials + n))
|
||||
|
||||
! Move original materials to temporary array
|
||||
temp(1:n_materials) = materials(:)
|
||||
|
||||
! Move allocation from temporary array
|
||||
call move_alloc(FROM=temp, TO=materials)
|
||||
end if
|
||||
|
||||
! Return indices in materials array
|
||||
if (present(index_start)) index_start = n_materials + 1
|
||||
if (present(index_end)) index_end = n_materials + n
|
||||
n_materials = n_materials + n
|
||||
|
||||
! Extend the C++ materials array and get pointers to the C++ objects
|
||||
call extend_materials_c(n)
|
||||
do i = n_materials - n, n_materials
|
||||
materials(i) % ptr = material_pointer(i - 1)
|
||||
end do
|
||||
|
||||
err = 0
|
||||
end function openmc_extend_materials
|
||||
|
||||
function openmc_get_material_index(id, index) result(err) bind(C)
|
||||
! Returns the index in the materials array of a material with a given ID
|
||||
integer(C_INT32_T), value :: id
|
||||
integer(C_INT32_T), intent(out) :: index
|
||||
integer(C_INT) :: err
|
||||
|
||||
if (allocated(materials)) then
|
||||
if (material_dict % has(id)) then
|
||||
index = material_dict % get(id)
|
||||
err = 0
|
||||
else
|
||||
err = E_INVALID_ID
|
||||
call set_errmsg("No material exists with ID=" // trim(to_str(id)) // ".")
|
||||
end if
|
||||
else
|
||||
err = E_ALLOCATE
|
||||
call set_errmsg("Memory has not been allocated for materials.")
|
||||
end if
|
||||
end function openmc_get_material_index
|
||||
|
||||
|
||||
function openmc_material_add_nuclide(index, name, density) result(err) bind(C)
|
||||
! Add a nuclide at a specified density in atom/b-cm to a material
|
||||
integer(C_INT32_T), value, intent(in) :: index
|
||||
character(kind=C_CHAR) :: name(*)
|
||||
real(C_DOUBLE), value, intent(in) :: density
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: j, k, n
|
||||
real(8) :: awr
|
||||
integer, allocatable :: new_nuclide(:)
|
||||
real(8), allocatable :: new_density(:)
|
||||
character(:), allocatable :: name_
|
||||
|
||||
name_ = to_f_string(name)
|
||||
|
||||
err = E_UNASSIGNED
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
associate (m => materials(index))
|
||||
! Check if nuclide is already in material
|
||||
do j = 1, m % n_nuclides
|
||||
k = m % nuclide(j)
|
||||
if (nuclides(k) % name == name_) then
|
||||
awr = nuclides(k) % awr
|
||||
m % density = m % density + density - m % atom_density(j)
|
||||
m % density_gpcc = m % density_gpcc + (density - &
|
||||
m % atom_density(j)) * awr * MASS_NEUTRON / N_AVOGADRO
|
||||
m % atom_density(j) = density
|
||||
err = 0
|
||||
end if
|
||||
end do
|
||||
|
||||
! If nuclide wasn't found, extend nuclide/density arrays
|
||||
if (err /= 0) then
|
||||
! If nuclide hasn't been loaded, load it now
|
||||
err = openmc_load_nuclide(name)
|
||||
|
||||
if (err == 0) then
|
||||
! Extend arrays
|
||||
n = m % n_nuclides
|
||||
allocate(new_nuclide(n + 1))
|
||||
if (n > 0) new_nuclide(1:n) = m % nuclide
|
||||
call move_alloc(FROM=new_nuclide, TO=m % nuclide)
|
||||
|
||||
allocate(new_density(n + 1))
|
||||
if (n > 0) new_density(1:n) = m % atom_density
|
||||
call move_alloc(FROM=new_density, TO=m % atom_density)
|
||||
|
||||
! Append new nuclide/density
|
||||
k = nuclide_dict % get(to_lower(name_))
|
||||
m % nuclide(n + 1) = k
|
||||
m % atom_density(n + 1) = density
|
||||
m % density = m % density + density
|
||||
m % density_gpcc = m % density_gpcc + &
|
||||
density * nuclides(k) % awr * MASS_NEUTRON / N_AVOGADRO
|
||||
m % n_nuclides = n + 1
|
||||
end if
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
|
||||
end function openmc_material_add_nuclide
|
||||
|
||||
|
||||
function openmc_material_get_densities(index, nuclides, densities, n) &
|
||||
result(err) bind(C)
|
||||
! returns an array of nuclide densities in a material
|
||||
integer(C_INT32_T), value :: index
|
||||
type(C_PTR), intent(out) :: nuclides
|
||||
type(C_PTR), intent(out) :: densities
|
||||
integer(C_INT), intent(out) :: n
|
||||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
associate (m => materials(index))
|
||||
if (allocated(m % atom_density)) then
|
||||
nuclides = C_LOC(m % nuclide(1))
|
||||
densities = C_LOC(m % atom_density(1))
|
||||
n = size(m % atom_density)
|
||||
err = 0
|
||||
else
|
||||
err = E_ALLOCATE
|
||||
call set_errmsg("Material atom density array has not been allocated.")
|
||||
end if
|
||||
end associate
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
end function openmc_material_get_densities
|
||||
|
||||
|
||||
function openmc_material_get_id(index, id) result(err) bind(C)
|
||||
! returns the ID of a material
|
||||
integer(C_INT32_T), value :: index
|
||||
integer(C_INT32_T), intent(out) :: id
|
||||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
id = materials(index) % id()
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
end function openmc_material_get_id
|
||||
|
||||
|
||||
function openmc_material_get_fissionable(index, fissionable) result(err) bind(C)
|
||||
! returns whether a material is fissionable
|
||||
integer(C_INT32_T), value :: index
|
||||
logical(C_BOOL), intent(out) :: fissionable
|
||||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
fissionable = materials(index) % fissionable()
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
end function openmc_material_get_fissionable
|
||||
|
||||
|
||||
function openmc_material_set_id(index, id) result(err) bind(C)
|
||||
! Set the ID of a material
|
||||
integer(C_INT32_T), value, intent(in) :: index
|
||||
integer(C_INT32_T), value, intent(in) :: id
|
||||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= n_materials) then
|
||||
call materials(index) % set_id(id, index)
|
||||
call material_dict % set(id, index)
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
end function openmc_material_set_id
|
||||
|
||||
|
||||
function openmc_material_set_density(index, density, units) result(err) bind(C)
|
||||
! Set the total density of a material
|
||||
integer(C_INT32_T), value, intent(in) :: index
|
||||
real(C_DOUBLE), value, intent(in) :: density
|
||||
character(kind=C_CHAR), intent(in) :: units(*)
|
||||
integer(C_INT) :: err
|
||||
|
||||
character(:), allocatable :: units_
|
||||
|
||||
! Convert C string to Fortran string
|
||||
units_ = to_f_string(units)
|
||||
|
||||
err = E_UNASSIGNED
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
associate (m => materials(index))
|
||||
err = m % set_density(density, units_)
|
||||
end associate
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
end function openmc_material_set_density
|
||||
|
||||
|
||||
function openmc_material_set_densities(index, n, name, density) result(err) bind(C)
|
||||
! Sets the densities for a list of nuclides in a material. If the nuclides
|
||||
! don't already exist in the material, they will be added
|
||||
integer(C_INT32_T), value, intent(in) :: index
|
||||
integer(C_INT), value, intent(in) :: n
|
||||
type(C_PTR), intent(in) :: name(n)
|
||||
real(C_DOUBLE), intent(in) :: density(n)
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: i
|
||||
integer :: stat
|
||||
character(C_CHAR), pointer :: string(:)
|
||||
character(len=:, kind=C_CHAR), allocatable :: name_
|
||||
|
||||
if (index >= 1 .and. index <= size(materials)) then
|
||||
associate (m => materials(index))
|
||||
! If nuclide/density arrays are not correct size, reallocate
|
||||
if (n /= m % n_nuclides) then
|
||||
deallocate(m % nuclide, m % atom_density, STAT=stat)
|
||||
allocate(m % nuclide(n), m % atom_density(n))
|
||||
end if
|
||||
|
||||
do i = 1, n
|
||||
! Convert C string to Fortran string
|
||||
call c_f_pointer(name(i), string, [10])
|
||||
name_ = to_lower(to_f_string(string))
|
||||
|
||||
if (.not. nuclide_dict % has(name_)) then
|
||||
err = openmc_load_nuclide(string)
|
||||
if (err < 0) return
|
||||
end if
|
||||
|
||||
m % nuclide(i) = nuclide_dict % get(name_)
|
||||
m % atom_density(i) = density(i)
|
||||
end do
|
||||
m % n_nuclides = n
|
||||
|
||||
! Set total density to the sum of the vector
|
||||
err = m % set_density(sum(density), 'atom/b-cm')
|
||||
|
||||
! Assign S(a,b) tables
|
||||
call m % assign_sab_tables()
|
||||
end associate
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index in materials array is out of bounds.")
|
||||
end if
|
||||
|
||||
end function openmc_material_set_densities
|
||||
|
||||
!===============================================================================
|
||||
! Fortran compatibility
|
||||
!===============================================================================
|
||||
|
||||
function material_isotropic(i_material, i_nuc_mat) result(iso) bind(C)
|
||||
integer(C_INT), value :: i_material
|
||||
integer(C_INT), value :: i_nuc_mat
|
||||
logical(C_BOOL) :: iso
|
||||
|
||||
iso = .false.
|
||||
associate (mat => materials(i_material))
|
||||
if (mat % has_isotropic_nuclides) then
|
||||
iso = mat % p0(i_nuc_mat)
|
||||
end if
|
||||
end associate
|
||||
end function
|
||||
|
||||
function material_element(i_material) result(ptr) bind(C)
|
||||
integer(C_INT), value :: i_material
|
||||
type(C_PTR) :: ptr
|
||||
ptr = C_LOC(materials(i_material) % element(1))
|
||||
end function
|
||||
|
||||
end module material_header
|
||||
|
|
|
|||
54
src/mgxs.cpp
54
src/mgxs.cpp
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/mgxs_interface.h"
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/settings.h"
|
||||
#include "openmc/string_utils.h"
|
||||
|
||||
|
||||
|
|
@ -41,7 +43,7 @@ std::vector<Mgxs> macro_xs;
|
|||
void
|
||||
Mgxs::init(const std::string& in_name, double in_awr,
|
||||
const std::vector<double>& in_kTs, bool in_fissionable, int in_scatter_format,
|
||||
int in_num_groups, int in_num_delayed_groups, bool in_is_isotropic,
|
||||
bool in_is_isotropic,
|
||||
const std::vector<double>& in_polar, const std::vector<double>& in_azimuthal)
|
||||
{
|
||||
// Set the metadata
|
||||
|
|
@ -51,8 +53,8 @@ Mgxs::init(const std::string& in_name, double in_awr,
|
|||
kTs = xt::adapt(in_kTs);
|
||||
fissionable = in_fissionable;
|
||||
scatter_format = in_scatter_format;
|
||||
num_groups = in_num_groups;
|
||||
num_delayed_groups = in_num_delayed_groups;
|
||||
num_groups = data::num_energy_groups;
|
||||
num_delayed_groups = data::num_delayed_groups;
|
||||
xs.resize(in_kTs.size());
|
||||
is_isotropic = in_is_isotropic;
|
||||
n_pol = in_polar.size();
|
||||
|
|
@ -73,9 +75,8 @@ Mgxs::init(const std::string& in_name, double in_awr,
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
|
||||
int in_num_delayed_groups, const std::vector<double>& temperature,
|
||||
double tolerance, std::vector<int>& temps_to_read, int& order_dim, int& method)
|
||||
Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector<double>& temperature,
|
||||
std::vector<int>& temps_to_read, int& order_dim)
|
||||
{
|
||||
// get name
|
||||
char char_name[MAX_WORD_LEN];
|
||||
|
|
@ -114,20 +115,20 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
|
|||
|
||||
// If only one temperature is available, lets just use nearest temperature
|
||||
// interpolation
|
||||
if ((num_temps == 1) && (method == TEMPERATURE_INTERPOLATION)) {
|
||||
if ((num_temps == 1) && (settings::temperature_method == TEMPERATURE_INTERPOLATION)) {
|
||||
warning("Cross sections for " + strtrim(name) + " are only available " +
|
||||
"at one temperature. Reverting to the nearest temperature " +
|
||||
"method.");
|
||||
method = TEMPERATURE_NEAREST;
|
||||
settings::temperature_method = TEMPERATURE_NEAREST;
|
||||
}
|
||||
|
||||
switch(method) {
|
||||
switch(settings::temperature_method) {
|
||||
case TEMPERATURE_NEAREST:
|
||||
// Determine actual temperatures to read
|
||||
for (const auto& T : temperature) {
|
||||
auto i_closest = xt::argmin(xt::abs(available_temps - T))[0];
|
||||
double temp_actual = available_temps[i_closest];
|
||||
if (std::fabs(temp_actual - T) < tolerance) {
|
||||
if (std::fabs(temp_actual - T) < settings::temperature_tolerance) {
|
||||
if (std::find(temps_to_read.begin(), temps_to_read.end(), std::round(temp_actual))
|
||||
== temps_to_read.end()) {
|
||||
temps_to_read.push_back(std::round(temp_actual));
|
||||
|
|
@ -276,39 +277,34 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, int in_num_groups,
|
|||
|
||||
// Finally use this data to initialize the MGXS Object
|
||||
init(in_name, in_awr, in_kTs, in_fissionable, in_scatter_format,
|
||||
in_num_groups, in_num_delayed_groups, in_is_isotropic, in_polar,
|
||||
in_is_isotropic, in_polar,
|
||||
in_azimuthal);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups,
|
||||
const std::vector<double>& temperature, double tolerance, int max_order,
|
||||
bool legendre_to_tabular, int legendre_to_tabular_points, int& method)
|
||||
Mgxs::Mgxs(hid_t xs_id, const std::vector<double>& temperature)
|
||||
{
|
||||
// Call generic data gathering routine (will populate the metadata)
|
||||
int order_data;
|
||||
std::vector<int> temps_to_read;
|
||||
metadata_from_hdf5(xs_id, energy_groups, delayed_groups, temperature,
|
||||
tolerance, temps_to_read, order_data, method);
|
||||
metadata_from_hdf5(xs_id, temperature, temps_to_read, order_data);
|
||||
|
||||
// Set number of energy and delayed groups
|
||||
int final_scatter_format = scatter_format;
|
||||
if (legendre_to_tabular) {
|
||||
if (settings::legendre_to_tabular) {
|
||||
if (scatter_format == ANGLE_LEGENDRE) final_scatter_format = ANGLE_TABULAR;
|
||||
}
|
||||
|
||||
// Load the more specific XsData information
|
||||
for (int t = 0; t < temps_to_read.size(); t++) {
|
||||
xs[t] = XsData(energy_groups, delayed_groups, fissionable,
|
||||
final_scatter_format, n_pol, n_azi);
|
||||
xs[t] = XsData(fissionable, final_scatter_format, n_pol, n_azi);
|
||||
// Get the temperature as a string and then open the HDF5 group
|
||||
std::string temp_str = std::to_string(temps_to_read[t]) + "K";
|
||||
hid_t xsdata_grp = open_group(xs_id, temp_str.c_str());
|
||||
|
||||
xs[t].from_hdf5(xsdata_grp, fissionable, scatter_format,
|
||||
final_scatter_format, order_data, max_order,
|
||||
legendre_to_tabular_points, is_isotropic, n_pol, n_azi);
|
||||
final_scatter_format, order_data, is_isotropic, n_pol, n_azi);
|
||||
close_group(xsdata_grp);
|
||||
|
||||
} // end temperature loop
|
||||
|
|
@ -320,8 +316,7 @@ Mgxs::Mgxs(hid_t xs_id, int energy_groups, int delayed_groups,
|
|||
//==============================================================================
|
||||
|
||||
Mgxs::Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
|
||||
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities,
|
||||
double tolerance, int& method)
|
||||
const std::vector<Mgxs*>& micros, const std::vector<double>& atom_densities)
|
||||
{
|
||||
// Get the minimum data needed to initialize:
|
||||
// Dont need awr, but lets just initialize it anyways
|
||||
|
|
@ -341,13 +336,12 @@ Mgxs::Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
|
|||
std::vector<double> in_azimuthal = micros[0]->azimuthal;
|
||||
|
||||
init(in_name, in_awr, mat_kTs, in_fissionable, in_scatter_format,
|
||||
in_num_groups, in_num_delayed_groups, in_is_isotropic, in_polar,
|
||||
in_azimuthal);
|
||||
in_is_isotropic, in_polar, in_azimuthal);
|
||||
|
||||
// Create the xs data for each temperature
|
||||
for (int t = 0; t < mat_kTs.size(); t++) {
|
||||
xs[t] = XsData(in_num_groups, in_num_delayed_groups, in_fissionable,
|
||||
in_scatter_format, in_polar.size(), in_azimuthal.size());
|
||||
xs[t] = XsData(in_fissionable, in_scatter_format, in_polar.size(),
|
||||
in_azimuthal.size());
|
||||
|
||||
// Find the right temperature index to use
|
||||
double temp_desired = mat_kTs[t];
|
||||
|
|
@ -357,13 +351,13 @@ Mgxs::Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
|
|||
std::vector<int> micro_t(micros.size(), 0);
|
||||
std::vector<double> micro_t_interp(micros.size(), 0.);
|
||||
for (int m = 0; m < micros.size(); m++) {
|
||||
switch(method) {
|
||||
switch(settings::temperature_method) {
|
||||
case TEMPERATURE_NEAREST:
|
||||
{
|
||||
micro_t[m] = xt::argmin(xt::abs(micros[m]->kTs - temp_desired))[0];
|
||||
auto temp_actual = micros[m]->kTs[micro_t[m]];
|
||||
|
||||
if (std::abs(temp_actual - temp_desired) >= K_BOLTZMANN * tolerance) {
|
||||
if (std::abs(temp_actual - temp_desired) >= K_BOLTZMANN * settings::temperature_tolerance) {
|
||||
std::stringstream msg;
|
||||
msg << "MGXS Library does not contain cross section for " << name
|
||||
<< " at or near " << std::round(temp_desired / K_BOLTZMANN) << "K.";
|
||||
|
|
@ -395,7 +389,7 @@ Mgxs::Mgxs(const std::string& in_name, const std::vector<double>& mat_kTs,
|
|||
// If we are doing nearest temperature interpolation, then we don't need
|
||||
// to do the 2nd temperature
|
||||
int num_interp_points = 2;
|
||||
if (method == TEMPERATURE_NEAREST) num_interp_points = 1;
|
||||
if (settings::temperature_method == TEMPERATURE_NEAREST) num_interp_points = 1;
|
||||
for (int interp_point = 0; interp_point < num_interp_points; interp_point++) {
|
||||
std::vector<double> interp(micros.size());
|
||||
std::vector<double> temp_indices(micros.size());
|
||||
|
|
|
|||
|
|
@ -1,178 +0,0 @@
|
|||
module mgxs_data
|
||||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use constants
|
||||
use algorithm, only: find
|
||||
use dict_header, only: DictCharInt
|
||||
use error, only: fatal_error, write_message
|
||||
use geometry_header, only: get_temperatures, cells
|
||||
use hdf5_interface
|
||||
use material_header, only: Material, materials, n_materials
|
||||
use mgxs_interface
|
||||
use nuclide_header, only: n_nuclides
|
||||
use set_header, only: SetChar
|
||||
use settings
|
||||
use stl_vector, only: VectorReal, VectorChar
|
||||
use string, only: to_lower
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! READ_XS reads all the cross sections for the problem and stores them in
|
||||
! nuclides and sab_tables arrays
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_mgxs()
|
||||
integer :: i ! index in materials array
|
||||
integer :: j ! index over nuclides in material
|
||||
integer :: i_nuclide ! index in nuclides array
|
||||
character(20) :: name ! name of library to load
|
||||
type(Material), pointer :: mat
|
||||
type(SetChar) :: already_read
|
||||
integer(HID_T) :: file_id
|
||||
logical :: file_exists
|
||||
type(VectorReal), allocatable, target :: temps(:)
|
||||
character(MAX_WORD_LEN) :: word
|
||||
integer, allocatable :: array(:)
|
||||
|
||||
! Check if MGXS Library exists
|
||||
inquire(FILE=path_cross_sections, EXIST=file_exists)
|
||||
if (.not. file_exists) then
|
||||
|
||||
! Could not find MGXS Library file
|
||||
call fatal_error("Cross sections HDF5 file '" &
|
||||
// trim(path_cross_sections) // "' does not exist!")
|
||||
end if
|
||||
|
||||
call write_message("Loading cross section data...", 5)
|
||||
|
||||
! Get temperatures
|
||||
call get_temperatures(temps)
|
||||
|
||||
! Open file for reading
|
||||
file_id = file_open(path_cross_sections, 'r', parallel=.true.)
|
||||
|
||||
! Read filetype
|
||||
call read_attribute(word, file_id, "filetype")
|
||||
if (word /= 'mgxs') then
|
||||
call fatal_error("Provided MGXS Library is not a MGXS Library file.")
|
||||
end if
|
||||
|
||||
! Read revision number for the MGXS Library file and make sure it matches
|
||||
! with the current version
|
||||
call read_attribute(array, file_id, "version")
|
||||
if (any(array /= VERSION_MGXS_LIBRARY)) then
|
||||
call fatal_error("MGXS Library file version does not match current &
|
||||
&version supported by OpenMC.")
|
||||
end if
|
||||
|
||||
! ==========================================================================
|
||||
! READ ALL MGXS CROSS SECTION TABLES
|
||||
|
||||
! Loop over all files
|
||||
MATERIAL_LOOP: do i = 1, n_materials
|
||||
mat => materials(i)
|
||||
|
||||
NUCLIDE_LOOP: do j = 1, mat % n_nuclides
|
||||
name = trim(mat % names(j)) // C_NULL_CHAR
|
||||
i_nuclide = mat % nuclide(j)
|
||||
|
||||
if (.not. already_read % contains(name)) then
|
||||
call add_mgxs_c(file_id, name, num_energy_groups, num_delayed_groups, &
|
||||
temps(i_nuclide) % size(), temps(i_nuclide) % data, &
|
||||
temperature_tolerance, max_order, &
|
||||
logical(legendre_to_tabular, C_BOOL), &
|
||||
legendre_to_tabular_points, temperature_method)
|
||||
|
||||
call already_read % add(name)
|
||||
end if
|
||||
end do NUCLIDE_LOOP
|
||||
|
||||
call mat % set_fissionable( &
|
||||
logical(query_fissionable_c(mat % n_nuclides, mat % nuclide)))
|
||||
|
||||
end do MATERIAL_LOOP
|
||||
|
||||
call file_close(file_id)
|
||||
|
||||
! Avoid some valgrind leak errors
|
||||
call already_read % clear()
|
||||
|
||||
end subroutine read_mgxs
|
||||
|
||||
!===============================================================================
|
||||
! CREATE_MACRO_XS generates the macroscopic xs from the microscopic input data
|
||||
!===============================================================================
|
||||
|
||||
subroutine create_macro_xs()
|
||||
integer :: i_mat ! index in materials array
|
||||
type(Material), pointer :: mat ! current material
|
||||
type(VectorReal), allocatable :: kTs(:)
|
||||
character(MAX_WORD_LEN) :: name ! name of material
|
||||
|
||||
! Get temperatures to read for each material
|
||||
call get_mat_kTs(kTs)
|
||||
|
||||
! Force all nuclides in a material to be the same representation.
|
||||
! Therefore type(nuclides(mat % nuclide(1)) % obj) dictates type(macroxs).
|
||||
! At the same time, we will find the scattering type, as that will dictate
|
||||
! how we allocate the scatter object within macroxs.allocate(macro_xs(n_materials))
|
||||
do i_mat = 1, n_materials
|
||||
|
||||
! Get the material
|
||||
mat => materials(i_mat)
|
||||
|
||||
name = trim(mat % name) // C_NULL_CHAR
|
||||
|
||||
call create_macro_xs_c(name, mat % n_nuclides, mat % nuclide, &
|
||||
kTs(i_mat) % size(), kTs(i_mat) % data, mat % atom_density, &
|
||||
temperature_tolerance, temperature_method)
|
||||
end do
|
||||
|
||||
end subroutine create_macro_xs
|
||||
|
||||
!===============================================================================
|
||||
! GET_MAT_kTs returns a list of temperatures (in eV) that each
|
||||
! material appears at in the model.
|
||||
!===============================================================================
|
||||
|
||||
subroutine get_mat_kTs(kTs)
|
||||
|
||||
type(VectorReal), allocatable, intent(out) :: kTs(:)
|
||||
integer :: i, j ! Cell and material index
|
||||
integer :: i_material ! Index in materials array
|
||||
real(8) :: kT ! temperature in eV
|
||||
|
||||
allocate(kTs(size(materials)))
|
||||
|
||||
do i = 1, size(cells)
|
||||
! Skip non-material cells
|
||||
if (cells(i) % fill() /= C_NONE) cycle
|
||||
|
||||
do j = 1, cells(i) % material_size()
|
||||
|
||||
! Skip void materials
|
||||
if (cells(i) % material(j) == MATERIAL_VOID) cycle
|
||||
|
||||
! Get temperature of cell (rounding to nearest integer)
|
||||
if (cells(i) % sqrtkT_size() > 1) then
|
||||
kT = cells(i) % sqrtkT(j-1)**2
|
||||
else
|
||||
kT = cells(i) % sqrtkT(0)**2
|
||||
end if
|
||||
|
||||
i_material = cells(i) % material(j)
|
||||
|
||||
! Add temperature if it hasn't already been added
|
||||
if (find(kTs(i_material), kT) == -1) then
|
||||
call kTs(i_material) % push_back(kT)
|
||||
end if
|
||||
|
||||
end do
|
||||
end do
|
||||
|
||||
end subroutine get_mat_kTs
|
||||
|
||||
end module mgxs_data
|
||||
|
|
@ -7,48 +7,6 @@ module mgxs_interface
|
|||
implicit none
|
||||
|
||||
interface
|
||||
|
||||
subroutine add_mgxs_c(file_id, name, energy_groups, delayed_groups, &
|
||||
n_temps, temps, tolerance, max_order, legendre_to_tabular, &
|
||||
legendre_to_tabular_points, method) bind(C)
|
||||
use ISO_C_BINDING
|
||||
import HID_T
|
||||
implicit none
|
||||
integer(HID_T), value, intent(in) :: file_id
|
||||
character(kind=C_CHAR),intent(in) :: name(*)
|
||||
integer(C_INT), value, intent(in) :: energy_groups
|
||||
integer(C_INT), value, intent(in) :: delayed_groups
|
||||
integer(C_INT), value, intent(in) :: n_temps
|
||||
real(C_DOUBLE), intent(in) :: temps(1:n_temps)
|
||||
real(C_DOUBLE), value, intent(in) :: tolerance
|
||||
integer(C_INT), value, intent(in) :: max_order
|
||||
logical(C_BOOL),value, intent(in) :: legendre_to_tabular
|
||||
integer(C_INT), value, intent(in) :: legendre_to_tabular_points
|
||||
integer(C_INT), intent(inout) :: method
|
||||
end subroutine add_mgxs_c
|
||||
|
||||
function query_fissionable_c(n_nuclides, i_nuclides) result(result) bind(C)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
integer(C_INT), value, intent(in) :: n_nuclides
|
||||
integer(C_INT), intent(in) :: i_nuclides(1:n_nuclides)
|
||||
logical(C_BOOL) :: result
|
||||
end function query_fissionable_c
|
||||
|
||||
subroutine create_macro_xs_c(name, n_nuclides, i_nuclides, n_temps, temps, &
|
||||
atom_densities, tolerance, method) bind(C)
|
||||
use ISO_C_BINDING
|
||||
implicit none
|
||||
character(kind=C_CHAR),intent(in) :: name(*)
|
||||
integer(C_INT), value, intent(in) :: n_nuclides
|
||||
integer(C_INT), intent(in) :: i_nuclides(1:n_nuclides)
|
||||
integer(C_INT), value, intent(in) :: n_temps
|
||||
real(C_DOUBLE), intent(in) :: temps(1:n_temps)
|
||||
real(C_DOUBLE), intent(in) :: atom_densities(1:n_nuclides)
|
||||
real(C_DOUBLE), value, intent(in) :: tolerance
|
||||
integer(C_INT), intent(inout) :: method
|
||||
end subroutine create_macro_xs_c
|
||||
|
||||
subroutine calculate_xs_c(i_mat, gin, sqrtkT, uvw, total_xs, abs_xs, &
|
||||
nu_fiss_xs) bind(C)
|
||||
use ISO_C_BINDING
|
||||
|
|
@ -130,7 +88,7 @@ module mgxs_interface
|
|||
integer(C_INT), bind(C) :: num_energy_groups
|
||||
|
||||
! Number of delayed groups
|
||||
integer(C_INT) :: num_delayed_groups
|
||||
integer(C_INT), bind(C) :: num_delayed_groups
|
||||
|
||||
! Energy group structure with decreasing energy
|
||||
real(8), allocatable :: energy_bins(:)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,19 @@
|
|||
#include "openmc/mgxs_interface.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "openmc/cell.h"
|
||||
#include "openmc/cross_sections.h"
|
||||
#include "openmc/container_util.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/file_utils.h"
|
||||
#include "openmc/geometry_aux.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/material.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/settings.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
|
@ -25,75 +34,152 @@ std::vector<double> rev_energy_bins;
|
|||
// Mgxs data loading interface methods
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
add_mgxs_c(hid_t file_id, const char* name, int energy_groups,
|
||||
int delayed_groups, int n_temps, const double temps[], double tolerance,
|
||||
int max_order, bool legendre_to_tabular, int legendre_to_tabular_points,
|
||||
int& method)
|
||||
void read_mgxs()
|
||||
{
|
||||
// Convert temps to a vector for the from_hdf5 function
|
||||
std::vector<double> temperature(temps, temps + n_temps);
|
||||
// Check if MGXS Library exists
|
||||
if (!file_exists(settings::path_cross_sections)) {
|
||||
// Could not find MGXS Library file
|
||||
fatal_error("Cross sections HDF5 file '" + settings::path_cross_sections +
|
||||
"' does not exist.");
|
||||
}
|
||||
|
||||
write_message("Loading cross section data...", 5);
|
||||
|
||||
// Get temperatures
|
||||
std::vector<std::vector<double>> nuc_temps(data::nuclide_map.size());
|
||||
std::vector<std::vector<double>> dummy;
|
||||
get_temperatures(nuc_temps, dummy);
|
||||
|
||||
// Open file for reading
|
||||
hid_t file_id = file_open(settings::path_cross_sections, 'r');
|
||||
|
||||
// Read filetype
|
||||
std::string type;
|
||||
read_attribute(file_id, "filetype", type);
|
||||
if (type != "mgxs") {
|
||||
fatal_error("Provided MGXS Library is not a MGXS Library file.");
|
||||
}
|
||||
|
||||
// Read revision number for the MGXS Library file and make sure it matches
|
||||
// with the current version
|
||||
std::array<int, 2> array;
|
||||
read_attribute(file_id, "version", array);
|
||||
if (array != VERSION_MGXS_LIBRARY) {
|
||||
fatal_error("MGXS Library file version does not match current version "
|
||||
"supported by OpenMC.");
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// READ ALL MGXS CROSS SECTION TABLES
|
||||
|
||||
std::unordered_set<std::string> already_read;
|
||||
|
||||
// Build vector of nuclide names
|
||||
std::vector<std::string> nuclide_names(data::nuclide_map.size());
|
||||
for (const auto& kv : data::nuclide_map) {
|
||||
nuclide_names[kv.second] = kv.first;
|
||||
}
|
||||
|
||||
// Loop over all files
|
||||
for (const auto& mat : model::materials) {
|
||||
for (int i_nuc : mat->nuclide_) {
|
||||
std::string& name = nuclide_names[i_nuc];
|
||||
|
||||
if (already_read.find(name) == already_read.end()) {
|
||||
add_mgxs(file_id, name, nuc_temps[i_nuc]);
|
||||
already_read.insert(name);
|
||||
}
|
||||
|
||||
if (data::nuclides_MG[i_nuc].fissionable) {
|
||||
mat->fissionable_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file_close(file_id);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
add_mgxs(hid_t file_id, const std::string& name,
|
||||
const std::vector<double>& temperature)
|
||||
{
|
||||
write_message("Loading " + std::string(name) + " data...", 6);
|
||||
|
||||
// Check to make sure cross section set exists in the library
|
||||
hid_t xs_grp;
|
||||
if (object_exists(file_id, name)) {
|
||||
xs_grp = open_group(file_id, name);
|
||||
if (object_exists(file_id, name.c_str())) {
|
||||
xs_grp = open_group(file_id, name.c_str());
|
||||
} else {
|
||||
fatal_error("Data for " + std::string(name) + " does not exist in "
|
||||
+ "provided MGXS Library");
|
||||
}
|
||||
|
||||
Mgxs mg(xs_grp, energy_groups, delayed_groups, temperature, tolerance,
|
||||
max_order, legendre_to_tabular, legendre_to_tabular_points, method);
|
||||
|
||||
data::nuclides_MG.push_back(mg);
|
||||
data::nuclides_MG.emplace_back(xs_grp, temperature);
|
||||
close_group(xs_grp);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
bool
|
||||
query_fissionable_c(int n_nuclides, const int i_nuclides[])
|
||||
void create_macro_xs()
|
||||
{
|
||||
bool result = false;
|
||||
for (int n = 0; n < n_nuclides; n++) {
|
||||
if (data::nuclides_MG[i_nuclides[n] - 1].fissionable) result = true;
|
||||
// Get temperatures to read for each material
|
||||
auto kTs = get_mat_kTs();
|
||||
|
||||
// Force all nuclides in a material to be the same representation.
|
||||
// Therefore type(nuclides[mat->nuclide_[0]]) dictates type(macroxs).
|
||||
// At the same time, we will find the scattering type, as that will dictate
|
||||
// how we allocate the scatter object within macroxs.
|
||||
for (int i = 0; i < model::materials.size(); ++i) {
|
||||
if (kTs[i].size() > 0) {
|
||||
// Convert atom_densities to a vector
|
||||
Material* mat = model::materials[i];
|
||||
std::vector<double> atom_densities(mat->atom_density_.begin(),
|
||||
mat->atom_density_.end());
|
||||
|
||||
// Build array of pointers to nuclides_MG's Mgxs objects needed for this
|
||||
// material
|
||||
std::vector<Mgxs*> mgxs_ptr;
|
||||
for (int i_nuclide : mat->nuclide_) {
|
||||
mgxs_ptr.push_back(&data::nuclides_MG[i_nuclide]);
|
||||
}
|
||||
|
||||
data::macro_xs.emplace_back(mat->name_, kTs[i], mgxs_ptr, atom_densities);
|
||||
} else {
|
||||
// Preserve the ordering of materials by including a blank entry
|
||||
data::macro_xs.emplace_back();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
create_macro_xs_c(const char* mat_name, int n_nuclides, const int i_nuclides[],
|
||||
int n_temps, const double temps[], const double atom_densities[],
|
||||
double tolerance, int& method)
|
||||
std::vector<std::vector<double>> get_mat_kTs()
|
||||
{
|
||||
if (n_temps > 0) {
|
||||
// // Convert temps to a vector
|
||||
std::vector<double> temperature(temps, temps + n_temps);
|
||||
std::vector<std::vector<double>> kTs(model::materials.size());
|
||||
|
||||
// Convert atom_densities to a vector
|
||||
std::vector<double> atom_densities_vec(atom_densities,
|
||||
atom_densities + n_nuclides);
|
||||
for (const auto& cell : model::cells) {
|
||||
// Skip non-material cells
|
||||
if (cell->fill_ != C_NONE) continue;
|
||||
|
||||
// Build array of pointers to nuclides_MG's Mgxs objects needed for this
|
||||
// material
|
||||
std::vector<Mgxs*> mgxs_ptr(n_nuclides);
|
||||
for (int n = 0; n < n_nuclides; n++) {
|
||||
mgxs_ptr[n] = &data::nuclides_MG[i_nuclides[n] - 1];
|
||||
for (int j = 0; j < cell->material_.size(); ++j) {
|
||||
// Skip void materials
|
||||
int i_material = cell->material_[j];
|
||||
if (i_material == MATERIAL_VOID) continue;
|
||||
|
||||
// Get temperature of cell (rounding to nearest integer)
|
||||
double sqrtkT = cell->sqrtkT_.size() == 1 ?
|
||||
cell->sqrtkT_[j] : cell->sqrtkT_[0];
|
||||
double kT = sqrtkT * sqrtkT;
|
||||
|
||||
// Add temperature if it hasn't already been added
|
||||
if (!contains(kTs[i_material], kT)) {
|
||||
kTs[i_material].push_back(kT);
|
||||
}
|
||||
}
|
||||
|
||||
Mgxs macro(mat_name, temperature, mgxs_ptr, atom_densities_vec,
|
||||
tolerance, method);
|
||||
data::macro_xs.emplace_back(macro);
|
||||
} else {
|
||||
// Preserve the ordering of materials by including a blank entry
|
||||
Mgxs macro;
|
||||
data::macro_xs.emplace_back(macro);
|
||||
}
|
||||
return kTs;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
module multipole_header
|
||||
|
||||
use constants
|
||||
use hdf5_interface
|
||||
use error, only: fatal_error
|
||||
use string, only: to_str
|
||||
|
||||
implicit none
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! CHECK_WMP_VERSION checks for the right version of WMP data within HDF5
|
||||
! files
|
||||
!===============================================================================
|
||||
|
||||
subroutine check_wmp_version(file_id)
|
||||
integer(HID_T), intent(in) :: file_id
|
||||
|
||||
integer, allocatable :: version(:)
|
||||
|
||||
if (attribute_exists(file_id, 'version')) then
|
||||
call read_attribute(version, file_id, 'version')
|
||||
if (version(1) /= WMP_VERSION(1)) then
|
||||
call fatal_error("WMP data format uses version " // trim(to_str(&
|
||||
version(1))) // "." // trim(to_str(version(2))) // " whereas &
|
||||
&your installation of OpenMC expects version " // trim(to_str(&
|
||||
WMP_VERSION(1))) // ".x data.")
|
||||
end if
|
||||
else
|
||||
call fatal_error("WMP data does not indicate a version. Your &
|
||||
&installation of OpenMC expects version " // trim(to_str(&
|
||||
WMP_VERSION(1))) // ".x data.")
|
||||
end if
|
||||
end subroutine check_wmp_version
|
||||
|
||||
end module multipole_header
|
||||
115
src/nuclide.cpp
115
src/nuclide.cpp
|
|
@ -1,6 +1,8 @@
|
|||
#include "openmc/nuclide.h"
|
||||
|
||||
#include "openmc/capi.h"
|
||||
#include "openmc/container_util.h"
|
||||
#include "openmc/cross_sections.h"
|
||||
#include "openmc/endf.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
|
|
@ -29,6 +31,7 @@ namespace data {
|
|||
std::array<double, 2> energy_min {0.0, 0.0};
|
||||
std::array<double, 2> energy_max {INFTY, INFTY};
|
||||
std::vector<std::unique_ptr<Nuclide>> nuclides;
|
||||
std::unordered_map<std::string, int> nuclide_map;
|
||||
} // namespace data
|
||||
|
||||
namespace simulation {
|
||||
|
|
@ -46,7 +49,7 @@ int Nuclide::XS_FISSION {2};
|
|||
int Nuclide::XS_NU_FISSION {3};
|
||||
int Nuclide::XS_PHOTON_PROD {4};
|
||||
|
||||
Nuclide::Nuclide(hid_t group, const double* temperature, int n, int i_nuclide)
|
||||
Nuclide::Nuclide(hid_t group, const std::vector<double>& temperature, int i_nuclide)
|
||||
: i_nuclide_{i_nuclide}
|
||||
{
|
||||
// Get name of nuclide from group, removing leading '/'
|
||||
|
|
@ -81,6 +84,7 @@ Nuclide::Nuclide(hid_t group, const double* temperature, int n, int i_nuclide)
|
|||
// temperature range was given, in which case all temperatures in the range
|
||||
// are loaded irrespective of what temperatures actually appear in the model
|
||||
std::vector<int> temps_to_read;
|
||||
int n = temperature.size();
|
||||
double T_min = n > 0 ? settings::temperature_range[0] : 0.0;
|
||||
double T_max = n > 0 ? settings::temperature_range[1] : INFTY;
|
||||
if (T_max > 0.0) {
|
||||
|
|
@ -94,8 +98,7 @@ Nuclide::Nuclide(hid_t group, const double* temperature, int n, int i_nuclide)
|
|||
switch (settings::temperature_method) {
|
||||
case TEMPERATURE_NEAREST:
|
||||
// Find nearest temperatures
|
||||
for (int i = 0; i < n; ++i) {
|
||||
double T_desired = temperature[i];
|
||||
for (double T_desired : temperature) {
|
||||
|
||||
// Determine closest temperature
|
||||
double min_delta_T = INFTY;
|
||||
|
|
@ -129,9 +132,7 @@ Nuclide::Nuclide(hid_t group, const double* temperature, int n, int i_nuclide)
|
|||
case TEMPERATURE_INTERPOLATION:
|
||||
// If temperature interpolation or multipole is selected, get a list of
|
||||
// bounding temperatures for each actual temperature present in the model
|
||||
for (int i = 0; i < n; ++i) {
|
||||
double T_desired = temperature[i];
|
||||
|
||||
for (double T_desired : temperature) {
|
||||
bool found_pair = false;
|
||||
for (int j = 0; j < temps_available.size() - 1; ++j) {
|
||||
if (temps_available[j] <= T_desired && T_desired < temps_available[j + 1]) {
|
||||
|
|
@ -391,7 +392,7 @@ void Nuclide::create_derived()
|
|||
|
||||
void Nuclide::init_grid()
|
||||
{
|
||||
int neutron = static_cast<int>(ParticleType::neutron) - 1;
|
||||
int neutron = static_cast<int>(ParticleType::neutron);
|
||||
double E_min = data::energy_min[neutron];
|
||||
double E_max = data::energy_max[neutron];
|
||||
int M = settings::n_log_bins;
|
||||
|
|
@ -865,6 +866,76 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const
|
|||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-member functions
|
||||
//==============================================================================
|
||||
|
||||
void check_data_version(hid_t file_id)
|
||||
{
|
||||
if (attribute_exists(file_id, "version")) {
|
||||
std::vector<int> version;
|
||||
read_attribute(file_id, "version", version);
|
||||
if (version[0] != HDF5_VERSION[0]) {
|
||||
fatal_error("HDF5 data format uses version " + std::to_string(version[0])
|
||||
+ "." + std::to_string(version[1]) + " whereas your installation of "
|
||||
"OpenMC expects version " + std::to_string(HDF5_VERSION[0])
|
||||
+ ".x data.");
|
||||
}
|
||||
} else {
|
||||
fatal_error("HDF5 data does not indicate a version. Your installation of "
|
||||
"OpenMC expects version " + std::to_string(HDF5_VERSION[0]) +
|
||||
".x data.");
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// C API
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void extend_nuclides();
|
||||
|
||||
extern "C" int openmc_load_nuclide(const char* name)
|
||||
{
|
||||
if (data::nuclide_map.find(name) == data::nuclide_map.end()) {
|
||||
const auto& it = data::library_map.find({Library::Type::neutron, name});
|
||||
if (it != data::library_map.end()) {
|
||||
// Extend nuclides array on Fortran side
|
||||
extend_nuclides();
|
||||
|
||||
// Get filename for library containing nuclide
|
||||
int idx = it->second;
|
||||
std::string& filename = data::libraries[idx].path_;
|
||||
write_message("Reading " + std::string{name} + " from " + filename, 6);
|
||||
|
||||
// Open file and make sure version is sufficient
|
||||
hid_t file_id = file_open(filename, 'r');
|
||||
check_data_version(file_id);
|
||||
|
||||
// Read nuclide data from HDF5
|
||||
hid_t group = open_group(file_id, name);
|
||||
std::vector<double> temperature;
|
||||
int i_nuclide = data::nuclides.size();
|
||||
data::nuclides.push_back(std::make_unique<Nuclide>(
|
||||
group, temperature, i_nuclide));
|
||||
close_group(group);
|
||||
file_close(file_id);
|
||||
|
||||
// Add entry to nuclide dictionary
|
||||
data::nuclide_map[name] = i_nuclide;
|
||||
|
||||
// Initialize nuclide grid
|
||||
data::nuclides.back()->init_grid();
|
||||
|
||||
// Read multipole file into the appropriate entry on the nuclides array
|
||||
if (settings::temperature_multipole) read_multipole_data(i_nuclide);
|
||||
} else {
|
||||
set_errmsg("Nuclide '" + std::string{name} + "' is not present in library.");
|
||||
return OPENMC_E_DATA;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
|
@ -872,19 +943,16 @@ void Nuclide::calculate_urr_xs(int i_temp, double E) const
|
|||
extern "C" void
|
||||
set_particle_energy_bounds(int particle, double E_min, double E_max)
|
||||
{
|
||||
data::energy_min[particle - 1] = E_min;
|
||||
data::energy_max[particle - 1] = E_max;
|
||||
data::energy_min[particle] = E_min;
|
||||
data::energy_max[particle] = E_max;
|
||||
}
|
||||
|
||||
extern "C" Nuclide* nuclide_from_hdf5_c(hid_t group, const double* temperature, int n)
|
||||
{
|
||||
data::nuclides.push_back(std::make_unique<Nuclide>(group, temperature, n,
|
||||
data::nuclides.size()));
|
||||
return data::nuclides.back().get();
|
||||
}
|
||||
extern "C" int nuclides_size() { return data::nuclide_map.size(); }
|
||||
|
||||
extern "C" void nuclide_init_grid_c(Nuclide* nuc) { nuc->init_grid(); }
|
||||
|
||||
extern "C" double nuclide_awr(int i_nuc) { return data::nuclides[i_nuc - 1]->awr_; }
|
||||
|
||||
extern "C" Reaction* nuclide_reaction(Nuclide* nuc, int i_rx)
|
||||
{
|
||||
return nuc->reactions_[i_rx-1].get();
|
||||
|
|
@ -916,11 +984,6 @@ extern "C" double nuclide_fission_q_recov(Nuclide* nuc, double E)
|
|||
return nuc->fission_q_recov_ ? (*nuc->fission_q_recov_)(E) : 0.0;
|
||||
}
|
||||
|
||||
extern "C" void nuclide_load_multipole(Nuclide* nuc, hid_t group)
|
||||
{
|
||||
nuc->multipole_ = std::make_unique<WindowedMultipole>(group);
|
||||
}
|
||||
|
||||
extern "C" void multipole_deriv_eval(Nuclide* nuc, double E, double sqrtkT,
|
||||
double* sig_s, double* sig_a, double* sig_f)
|
||||
{
|
||||
|
|
@ -933,7 +996,17 @@ extern "C" bool multipole_in_range(Nuclide* nuc, double E)
|
|||
E <= nuc->multipole_->E_max_;
|
||||
}
|
||||
|
||||
extern "C" void nuclides_clear() { data::nuclides.clear(); }
|
||||
extern "C" void nuclides_clear()
|
||||
{
|
||||
data::nuclides.clear();
|
||||
data::nuclide_map.clear();
|
||||
}
|
||||
|
||||
extern "C" int nuclide_map_get(const char* name)
|
||||
{
|
||||
auto it = data::nuclide_map.find(name);
|
||||
return it == data::nuclide_map.end() ? -1 : it->second + 1;
|
||||
}
|
||||
|
||||
extern "C" NuclideMicroXS* micro_xs_ptr();
|
||||
extern "C" ElementMicroXS* micro_photon_xs_ptr();
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@ module nuclide_header
|
|||
|
||||
use algorithm, only: sort, find, binary_search
|
||||
use constants
|
||||
use dict_header, only: DictIntInt, DictCharInt
|
||||
use endf, only: reaction_name, is_fission, is_disappearance, &
|
||||
is_inelastic_scatter
|
||||
use endf, only: is_fission, is_disappearance
|
||||
use endf_header, only: Function1D, Polynomial, Tabulated1D
|
||||
use error
|
||||
use hdf5_interface
|
||||
use message_passing
|
||||
use random_lcg, only: prn, future_prn, prn_set_stream
|
||||
use reaction_header, only: Reaction
|
||||
use settings
|
||||
use stl_vector, only: VectorInt, VectorReal
|
||||
|
|
@ -66,9 +63,6 @@ module nuclide_header
|
|||
! Fission information
|
||||
integer, allocatable :: index_fission(:) ! indices in reactions
|
||||
|
||||
! Multipole data
|
||||
logical :: mp_present = .false.
|
||||
|
||||
! Reactions
|
||||
type(Reaction), allocatable :: reactions(:)
|
||||
|
||||
|
|
@ -79,7 +73,6 @@ module nuclide_header
|
|||
type(C_PTR) :: ptr
|
||||
|
||||
contains
|
||||
procedure :: from_hdf5 => nuclide_from_hdf5
|
||||
procedure :: init_grid => nuclide_init_grid
|
||||
procedure :: nu => nuclide_nu
|
||||
procedure, private :: create_derived => nuclide_create_derived
|
||||
|
|
@ -151,18 +144,12 @@ module nuclide_header
|
|||
! Nuclear data for each nuclide
|
||||
type(Nuclide), allocatable, target :: nuclides(:)
|
||||
integer(C_INT), bind(C) :: n_nuclides
|
||||
type(DictCharInt) :: nuclide_dict
|
||||
|
||||
! Cross section caches
|
||||
type(NuclideMicroXS), allocatable, target :: micro_xs(:) ! Cache for each nuclide
|
||||
type(MaterialMacroXS), bind(C) :: material_xs ! Cache for current material
|
||||
!$omp threadprivate(micro_xs, material_xs)
|
||||
|
||||
! Minimum/maximum energies
|
||||
real(8) :: energy_min(2) = [ZERO, ZERO]
|
||||
real(8) :: energy_max(2) = [INFINITY, INFINITY]
|
||||
|
||||
|
||||
interface
|
||||
function library_present_c(type, name) result(b) bind(C, name='library_present')
|
||||
import C_INT, C_CHAR, C_BOOL
|
||||
|
|
@ -195,6 +182,12 @@ module nuclide_header
|
|||
logical(C_BOOL) :: b
|
||||
end function
|
||||
|
||||
function nuclide_awr(i_nuc) result(awr) bind(C)
|
||||
import C_INT, C_DOUBLE
|
||||
integer(C_INT), value :: i_nuc
|
||||
real(C_DOUBLE) :: awr
|
||||
end function
|
||||
|
||||
function nuclide_fission_q_prompt(ptr, E) result(q) bind(C)
|
||||
import C_PTR, C_DOUBLE
|
||||
type(C_PTR), value :: ptr
|
||||
|
|
@ -208,6 +201,12 @@ module nuclide_header
|
|||
real(C_DOUBLE), value :: E
|
||||
real(C_DOUBLE) :: q
|
||||
end function
|
||||
|
||||
function nuclide_map_get(name) result(idx) bind(C)
|
||||
import C_CHAR, C_INT
|
||||
character(kind=C_CHAR), intent(in) :: name(*)
|
||||
integer(C_INT) :: idx
|
||||
end function
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
|
@ -238,27 +237,22 @@ contains
|
|||
ptr = C_LOC(micro_xs(1))
|
||||
end function
|
||||
|
||||
subroutine nuclide_from_hdf5(this, group_id, temperature, method, tolerance, &
|
||||
minmax, master, i_nuclide)
|
||||
class(Nuclide), intent(inout) :: this
|
||||
integer(HID_T), intent(in) :: group_id
|
||||
type(VectorReal), intent(in), target :: temperature ! list of desired temperatures
|
||||
integer, intent(inout) :: method
|
||||
real(8), intent(in) :: tolerance
|
||||
real(8), intent(in) :: minmax(2) ! range of temperatures
|
||||
logical(C_BOOL), intent(in) :: master ! if this is the master proc
|
||||
integer, intent(in) :: i_nuclide ! Nuclide index in nuclides
|
||||
subroutine nuclide_from_hdf5(group_id, ptr, temps, n, i_nuclide) bind(C)
|
||||
integer(HID_T), value :: group_id
|
||||
type(C_PTR), value :: ptr
|
||||
type(C_PTR), value :: temps
|
||||
integer(C_INT), value :: n
|
||||
integer(C_INT), value :: i_nuclide
|
||||
|
||||
real(C_DOUBLE), pointer :: temperature(:) ! list of desired temperatures
|
||||
|
||||
integer :: i
|
||||
integer :: i_closest
|
||||
integer :: n_temperature
|
||||
integer(HID_T) :: nu_group
|
||||
integer(HID_T) :: energy_group, energy_dset
|
||||
integer(HID_T) :: kT_group
|
||||
integer(HID_T) :: rxs_group
|
||||
integer(HID_T) :: rx_group
|
||||
integer(HID_T) :: fer_group ! fission_energy_release group
|
||||
integer(HID_T) :: fer_dset
|
||||
integer(HSIZE_T) :: j
|
||||
integer(HSIZE_T) :: dims(1)
|
||||
character(MAX_WORD_LEN) :: temp_str
|
||||
|
|
@ -270,23 +264,11 @@ contains
|
|||
type(VectorInt) :: MTs
|
||||
type(VectorInt) :: temps_to_read
|
||||
|
||||
interface
|
||||
function nuclide_from_hdf5_c(group, temperature, n) result(ptr) bind(C)
|
||||
import HID_T, C_DOUBLE, C_INT, C_PTR
|
||||
integer(HID_T), value :: group
|
||||
type(C_PTR), value :: temperature
|
||||
integer(C_INT), value :: n
|
||||
type(C_PTR) :: ptr
|
||||
end function
|
||||
end interface
|
||||
! Get array passed
|
||||
call c_f_pointer(temps, temperature, [n])
|
||||
|
||||
! Read data on C++ side
|
||||
if (temperature % size() > 0) then
|
||||
this % ptr = nuclide_from_hdf5_c(group_id, C_LOC(temperature % data(1)), &
|
||||
temperature % size())
|
||||
else
|
||||
this % ptr = nuclide_from_hdf5_c(group_id, C_NULL_PTR, 0)
|
||||
end if
|
||||
associate (this => nuclides(i_nuclide))
|
||||
this % ptr = ptr
|
||||
|
||||
! Get name of nuclide from group
|
||||
this % name = get_name(group_id)
|
||||
|
|
@ -311,35 +293,35 @@ contains
|
|||
call sort(temps_available)
|
||||
|
||||
! If only one temperature is available, revert to nearest temperature
|
||||
if (size(temps_available) == 1 .and. method == TEMPERATURE_INTERPOLATION) then
|
||||
if (size(temps_available) == 1 .and. temperature_method == TEMPERATURE_INTERPOLATION) then
|
||||
if (master) then
|
||||
call warning("Cross sections for " // trim(this % name) // " are only &
|
||||
&available at one temperature. Reverting to nearest temperature &
|
||||
&method.")
|
||||
end if
|
||||
method = TEMPERATURE_NEAREST
|
||||
temperature_method = TEMPERATURE_NEAREST
|
||||
end if
|
||||
|
||||
! Determine actual temperatures to read -- start by checking whether a
|
||||
! temperature range was given, in which case all temperatures in the range
|
||||
! are loaded irrespective of what temperatures actually appear in the model
|
||||
if (minmax(2) > ZERO) then
|
||||
if (temperature_range(2) > ZERO) then
|
||||
do i = 1, size(temps_available)
|
||||
temp_actual = temps_available(i)
|
||||
if (minmax(1) <= temp_actual .and. temp_actual <= minmax(2)) then
|
||||
if (temperature_range(1) <= temp_actual .and. temp_actual <= temperature_range(2)) then
|
||||
call temps_to_read % push_back(nint(temp_actual))
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
select case (method)
|
||||
select case (temperature_method)
|
||||
case (TEMPERATURE_NEAREST)
|
||||
! Find nearest temperatures
|
||||
do i = 1, temperature % size()
|
||||
temp_desired = temperature % data(i)
|
||||
do i = 1, n
|
||||
temp_desired = temperature(i)
|
||||
i_closest = minloc(abs(temps_available - temp_desired), dim=1)
|
||||
temp_actual = temps_available(i_closest)
|
||||
if (abs(temp_actual - temp_desired) < tolerance) then
|
||||
if (abs(temp_actual - temp_desired) < temperature_tolerance) then
|
||||
if (find(temps_to_read, nint(temp_actual)) == -1) then
|
||||
call temps_to_read % push_back(nint(temp_actual))
|
||||
|
||||
|
|
@ -362,8 +344,8 @@ contains
|
|||
case (TEMPERATURE_INTERPOLATION)
|
||||
! If temperature interpolation or multipole is selected, get a list of
|
||||
! bounding temperatures for each actual temperature present in the model
|
||||
TEMP_LOOP: do i = 1, temperature % size()
|
||||
temp_desired = temperature % data(i)
|
||||
TEMP_LOOP: do i = 1, n
|
||||
temp_desired = temperature(i)
|
||||
|
||||
do j = 1, size(temps_available) - 1
|
||||
if (temps_available(j) <= temp_desired .and. &
|
||||
|
|
@ -441,6 +423,7 @@ contains
|
|||
|
||||
! Finalize with the nuclide index
|
||||
this % i_nuclide = i_nuclide
|
||||
end associate
|
||||
|
||||
end subroutine nuclide_from_hdf5
|
||||
|
||||
|
|
@ -640,31 +623,6 @@ contains
|
|||
call nuclide_calculate_elastic_xs_c(this % ptr)
|
||||
end subroutine nuclide_calculate_elastic_xs
|
||||
|
||||
!===============================================================================
|
||||
! CHECK_DATA_VERSION checks for the right version of nuclear data within HDF5
|
||||
! files
|
||||
!===============================================================================
|
||||
|
||||
subroutine check_data_version(file_id)
|
||||
integer(HID_T), intent(in) :: file_id
|
||||
|
||||
integer, allocatable :: version(:)
|
||||
|
||||
if (attribute_exists(file_id, 'version')) then
|
||||
call read_attribute(version, file_id, 'version')
|
||||
if (version(1) /= HDF5_VERSION(1)) then
|
||||
call fatal_error("HDF5 data format uses version " // trim(to_str(&
|
||||
version(1))) // "." // trim(to_str(version(2))) // " whereas &
|
||||
&your installation of OpenMC expects version " // trim(to_str(&
|
||||
HDF5_VERSION(1))) // ".x data.")
|
||||
end if
|
||||
else
|
||||
call fatal_error("HDF5 data does not indicate a version. Your &
|
||||
&installation of OpenMC expects version " // trim(to_str(&
|
||||
HDF5_VERSION(1))) // ".x data.")
|
||||
end if
|
||||
end subroutine check_data_version
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_NUCLIDE deallocates global arrays defined in this module
|
||||
!===============================================================================
|
||||
|
|
@ -686,7 +644,6 @@ contains
|
|||
end if
|
||||
n_nuclides = 0
|
||||
|
||||
call nuclide_dict % clear()
|
||||
call library_clear()
|
||||
|
||||
end subroutine free_memory_nuclide
|
||||
|
|
@ -706,11 +663,10 @@ contains
|
|||
! Copy array of C_CHARs to normal Fortran string
|
||||
name_ = to_f_string(name)
|
||||
|
||||
err = 0
|
||||
if (allocated(nuclides)) then
|
||||
if (nuclide_dict % has(to_lower(name_))) then
|
||||
index = nuclide_dict % get(to_lower(name_))
|
||||
err = 0
|
||||
else
|
||||
index = nuclide_map_get(name)
|
||||
if (index == -1) then
|
||||
err = E_DATA
|
||||
call set_errmsg("No nuclide named '" // trim(name_) // &
|
||||
"' has been loaded.")
|
||||
|
|
@ -722,63 +678,6 @@ contains
|
|||
end function openmc_get_nuclide_index
|
||||
|
||||
|
||||
function openmc_load_nuclide(name) result(err) bind(C)
|
||||
! Load a nuclide from the cross section library
|
||||
character(kind=C_CHAR), intent(in) :: name(*)
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: n
|
||||
integer(HID_T) :: file_id
|
||||
integer(HID_T) :: group_id
|
||||
character(:), allocatable :: name_
|
||||
character(MAX_FILE_LEN) :: filename
|
||||
real(8) :: minmax(2) = [ZERO, INFINITY]
|
||||
type(VectorReal) :: temperature
|
||||
type(Nuclide), allocatable :: new_nuclides(:)
|
||||
|
||||
! Copy array of C_CHARs to normal Fortran string
|
||||
name_ = to_f_string(name)
|
||||
|
||||
err = 0
|
||||
if (.not. nuclide_dict % has(to_lower(name_))) then
|
||||
if (library_present(LIBRARY_NEUTRON, to_lower(name_))) then
|
||||
! allocate extra space in nuclides array
|
||||
n = n_nuclides
|
||||
allocate(new_nuclides(n + 1))
|
||||
new_nuclides(1:n) = nuclides(:)
|
||||
call move_alloc(FROM=new_nuclides, TO=nuclides)
|
||||
n = n + 1
|
||||
|
||||
filename = library_path(LIBRARY_NEUTRON, to_lower(name_))
|
||||
|
||||
! Open file and make sure version is sufficient
|
||||
file_id = file_open(filename, 'r')
|
||||
call check_data_version(file_id)
|
||||
|
||||
! Read nuclide data from HDF5
|
||||
group_id = open_group(file_id, name_)
|
||||
call nuclides(n) % from_hdf5(group_id, temperature, &
|
||||
temperature_method, temperature_tolerance, minmax, &
|
||||
master, n)
|
||||
call close_group(group_id)
|
||||
call file_close(file_id)
|
||||
|
||||
! Add entry to nuclide dictionary
|
||||
call nuclide_dict % set(to_lower(name_), n)
|
||||
n_nuclides = n
|
||||
|
||||
! Initialize nuclide grid
|
||||
call nuclides(n) % init_grid()
|
||||
else
|
||||
err = E_DATA
|
||||
call set_errmsg("Nuclide '" // trim(name_) // "' is not present &
|
||||
&in library.")
|
||||
end if
|
||||
end if
|
||||
|
||||
end function openmc_load_nuclide
|
||||
|
||||
|
||||
function openmc_nuclide_name(index, name) result(err) bind(C)
|
||||
! Return the name of a nuclide with a given index
|
||||
integer(C_INT), value, intent(in) :: index
|
||||
|
|
@ -803,4 +702,16 @@ contains
|
|||
end if
|
||||
end function openmc_nuclide_name
|
||||
|
||||
subroutine extend_nuclides() bind(C)
|
||||
integer :: n
|
||||
type(Nuclide), allocatable :: new_nuclides(:)
|
||||
|
||||
! allocate extra space in nuclides array
|
||||
n = n_nuclides
|
||||
allocate(new_nuclides(n + 1))
|
||||
new_nuclides(1:n) = nuclides(:)
|
||||
call move_alloc(FROM=new_nuclides, TO=nuclides)
|
||||
n = n + 1
|
||||
end subroutine
|
||||
|
||||
end module nuclide_header
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ contains
|
|||
|
||||
! display time elapsed for various sections
|
||||
write(ou,100) "Total time for initialization", time_initialize_elapsed()
|
||||
write(ou,100) " Reading cross sections", time_read_xs % elapsed
|
||||
write(ou,100) " Reading cross sections", time_read_xs_elapsed()
|
||||
write(ou,100) "Total time in simulation", time_inactive_elapsed() + &
|
||||
time_active_elapsed()
|
||||
write(ou,100) " Time in transport only", time_transport_elapsed()
|
||||
|
|
|
|||
|
|
@ -203,11 +203,6 @@ Particle::write_restart() const
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -115,15 +115,6 @@ module particle_header
|
|||
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
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ namespace data {
|
|||
xt::xtensor<double, 1> compton_profile_pz;
|
||||
|
||||
std::vector<PhotonInteraction> elements;
|
||||
std::unordered_map<std::string, int> element_map;
|
||||
|
||||
} // namespace data
|
||||
|
||||
|
|
@ -222,7 +223,7 @@ PhotonInteraction::PhotonInteraction(hid_t group, int i_element)
|
|||
}
|
||||
|
||||
// Truncate the bremsstrahlung data at the cutoff energy
|
||||
int photon = static_cast<int>(ParticleType::photon) - 1;
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
const auto& E {electron_energy};
|
||||
double cutoff = settings::energy_cutoff[photon];
|
||||
if (cutoff > E(0)) {
|
||||
|
|
@ -781,8 +782,7 @@ extern "C" void photon_from_hdf5(hid_t group)
|
|||
// the previous
|
||||
const auto& element {data::elements.back()};
|
||||
if (element.energy_.size() >= 1) {
|
||||
// TODO: off-by-one
|
||||
int photon = static_cast<int>(ParticleType::photon) - 1;
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
int n = element.energy_.size();
|
||||
data::energy_min[photon] = std::max(data::energy_min[photon],
|
||||
std::exp(element.energy_(1)));
|
||||
|
|
@ -791,6 +791,8 @@ extern "C" void photon_from_hdf5(hid_t group)
|
|||
}
|
||||
}
|
||||
|
||||
extern "C" int elements_size() { return data::element_map.size(); }
|
||||
|
||||
extern "C" void photon_calculate_xs(int i_element, double E)
|
||||
{
|
||||
data::elements[i_element - 1].calculate_xs(E);
|
||||
|
|
|
|||
|
|
@ -3,12 +3,9 @@ module photon_header
|
|||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use constants
|
||||
use dict_header, only: DictCharInt
|
||||
|
||||
integer :: n_elements ! Number of photon cross section tables
|
||||
|
||||
type(DictCharInt) :: element_dict
|
||||
|
||||
!===============================================================================
|
||||
! ELEMENTMICROXS contains cached microscopic photon cross sections for a
|
||||
! particular element at the current energy
|
||||
|
|
@ -42,7 +39,6 @@ contains
|
|||
|
||||
! Deallocate photon cross section data
|
||||
n_elements = 0
|
||||
call element_dict % clear()
|
||||
end subroutine free_memory_photon
|
||||
|
||||
function micro_photon_xs_ptr() result(ptr) bind(C)
|
||||
|
|
|
|||
125
src/physics.cpp
125
src/physics.cpp
|
|
@ -52,7 +52,7 @@ void collision(Particle* p)
|
|||
}
|
||||
|
||||
// Kill particle if energy falls below cutoff
|
||||
if (p->E < settings::energy_cutoff[p->type - 1]) {
|
||||
if (p->E < settings::energy_cutoff[p->type]) {
|
||||
p->alive = false;
|
||||
p->wgt = 0.0;
|
||||
p->last_wgt = 0.0;
|
||||
|
|
@ -74,9 +74,8 @@ void collision(Particle* p)
|
|||
|
||||
void sample_neutron_reaction(Particle* p)
|
||||
{
|
||||
int i_nuclide;
|
||||
int i_nuc_mat;
|
||||
sample_nuclide(p, SCORE_TOTAL, &i_nuclide, &i_nuc_mat);
|
||||
// Sample a nuclide within the material
|
||||
int i_nuclide = sample_nuclide(p);
|
||||
|
||||
// Save which nuclide particle had collision with
|
||||
// TODO: off-by-one
|
||||
|
|
@ -120,7 +119,7 @@ void sample_neutron_reaction(Particle* p)
|
|||
|
||||
// Sample a scattering reaction and determine the secondary energy of the
|
||||
// exiting neutron
|
||||
scatter(p, i_nuclide, i_nuc_mat);
|
||||
scatter(p, i_nuclide);
|
||||
|
||||
// Advance URR seed stream 'N' times after energy changes
|
||||
if (p->E != p->last_E) {
|
||||
|
|
@ -220,7 +219,7 @@ void sample_photon_reaction(Particle* p)
|
|||
// 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
|
||||
int photon = static_cast<int>(ParticleType::photon) - 1;
|
||||
int photon = static_cast<int>(ParticleType::photon);
|
||||
if (p->E < settings::energy_cutoff[photon]) {
|
||||
p->E = 0.0;
|
||||
p->alive = false;
|
||||
|
|
@ -420,63 +419,30 @@ void sample_positron_reaction(Particle* p)
|
|||
p->alive = false;
|
||||
}
|
||||
|
||||
void sample_nuclide(const Particle* p, int mt, int* i_nuclide, int* i_nuc_mat)
|
||||
int sample_nuclide(const Particle* p)
|
||||
{
|
||||
// Sample cumulative distribution function
|
||||
double cutoff;
|
||||
switch (mt) {
|
||||
case SCORE_TOTAL:
|
||||
cutoff = prn() * simulation::material_xs.total;
|
||||
break;
|
||||
case SCORE_SCATTER:
|
||||
cutoff = prn() * (simulation::material_xs.total -
|
||||
simulation::material_xs.absorption);
|
||||
break;
|
||||
case SCORE_FISSION:
|
||||
cutoff = prn() * simulation::material_xs.fission;
|
||||
break;
|
||||
}
|
||||
double cutoff = prn() * simulation::material_xs.total;
|
||||
|
||||
// Get pointers to nuclide/density arrays
|
||||
int* nuclides;
|
||||
double* densities;
|
||||
int n;
|
||||
openmc_material_get_densities(p->material, &nuclides, &densities, &n);
|
||||
// TODO: off-by-one
|
||||
const auto& mat {model::materials[p->material - 1]};
|
||||
int n = mat->nuclide_.size();
|
||||
|
||||
*i_nuc_mat = 0;
|
||||
double prob = 0.0;
|
||||
while (prob < cutoff) {
|
||||
// Check to make sure that a nuclide was sampled
|
||||
if (*i_nuc_mat > n) {
|
||||
p->write_restart();
|
||||
fatal_error("Did not sample any nuclide during collision.");
|
||||
}
|
||||
|
||||
// Find atom density
|
||||
// TODO: off-by-one
|
||||
*i_nuclide = nuclides[*i_nuc_mat] - 1;
|
||||
double atom_density = densities[*i_nuc_mat];
|
||||
|
||||
// Determine microscopic cross section
|
||||
double sigma;
|
||||
switch (mt) {
|
||||
case SCORE_TOTAL:
|
||||
sigma = atom_density * simulation::micro_xs[*i_nuclide].total;
|
||||
break;
|
||||
case SCORE_SCATTER:
|
||||
sigma = atom_density * (simulation::micro_xs[*i_nuclide].total -
|
||||
simulation::micro_xs[*i_nuclide].absorption);
|
||||
break;
|
||||
case SCORE_FISSION:
|
||||
sigma = atom_density * simulation::micro_xs[*i_nuclide].fission;
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < n; ++i) {
|
||||
// Get atom density
|
||||
int i_nuclide = mat->nuclide_[i];
|
||||
double atom_density = mat->atom_density_[i];
|
||||
|
||||
// Increment probability to compare to cutoff
|
||||
prob += sigma;
|
||||
|
||||
++(*i_nuc_mat);
|
||||
prob += atom_density * simulation::micro_xs[i_nuclide].total;
|
||||
if (prob >= cutoff) return i_nuclide;
|
||||
}
|
||||
|
||||
// If we reach here, no nuclide was sampled
|
||||
p->write_restart();
|
||||
throw std::runtime_error{"Did not sample any nuclide during collision."};
|
||||
}
|
||||
|
||||
int sample_element(Particle* p)
|
||||
|
|
@ -485,11 +451,8 @@ int sample_element(Particle* p)
|
|||
double cutoff = prn() * simulation::material_xs.total;
|
||||
|
||||
// Get pointers to elements, densities
|
||||
int* nuclide;
|
||||
double* density;
|
||||
int n;
|
||||
openmc_material_get_densities(p->material, &nuclide, &density, &n);
|
||||
int* element = material_element(p->material);
|
||||
const auto& mat {model::materials[p->material - 1]};
|
||||
int n = mat->nuclide_.size();
|
||||
|
||||
int i = 0;
|
||||
double prob = 0.0;
|
||||
|
|
@ -502,9 +465,8 @@ int sample_element(Particle* p)
|
|||
}
|
||||
|
||||
// Find atom density
|
||||
// TODO: off-by-one
|
||||
i_element = element[i] - 1;
|
||||
double atom_density = density[i];
|
||||
i_element = mat->element_[i];
|
||||
double atom_density = mat->atom_density_[i];
|
||||
|
||||
// Determine microscopic cross section
|
||||
double sigma = atom_density * simulation::micro_photon_xs[i_element].total;
|
||||
|
|
@ -627,7 +589,7 @@ void absorption(Particle* p, int i_nuclide)
|
|||
}
|
||||
}
|
||||
|
||||
void scatter(Particle* p, int i_nuclide, int i_nuc_mat)
|
||||
void scatter(Particle* p, int i_nuclide)
|
||||
{
|
||||
// copy incoming direction
|
||||
Direction u_old {p->coord[0].uvw};
|
||||
|
|
@ -712,21 +674,26 @@ void scatter(Particle* p, int i_nuclide, int i_nuc_mat)
|
|||
p->event = EVENT_SCATTER;
|
||||
|
||||
// Sample new outgoing angle for isotropic-in-lab scattering
|
||||
if (material_isotropic(p->material, i_nuc_mat)) {
|
||||
// Sample isotropic-in-lab outgoing direction
|
||||
double mu = 2.0*prn() - 1.0;
|
||||
double phi = 2.0*PI*prn();
|
||||
Direction u_new;
|
||||
u_new.x = mu;
|
||||
u_new.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
|
||||
u_new.z = std::sqrt(1.0 - mu*mu)*std::sin(phi);
|
||||
// TODO: off-by-one
|
||||
const auto& mat {model::materials[p->material - 1]};
|
||||
if (!mat->p0_.empty()) {
|
||||
int i_nuc_mat = mat->mat_nuclide_index_[i_nuclide];
|
||||
if (mat->p0_[i_nuc_mat]) {
|
||||
// Sample isotropic-in-lab outgoing direction
|
||||
double mu = 2.0*prn() - 1.0;
|
||||
double phi = 2.0*PI*prn();
|
||||
Direction u_new;
|
||||
u_new.x = mu;
|
||||
u_new.y = std::sqrt(1.0 - mu*mu)*std::cos(phi);
|
||||
u_new.z = std::sqrt(1.0 - mu*mu)*std::sin(phi);
|
||||
|
||||
p->mu = u_old.dot(u_new);
|
||||
p->mu = u_old.dot(u_new);
|
||||
|
||||
// Change direction of particle
|
||||
p->coord[0].uvw[0] = u_new.x;
|
||||
p->coord[0].uvw[1] = u_new.y;
|
||||
p->coord[0].uvw[2] = u_new.z;
|
||||
// Change direction of particle
|
||||
p->coord[0].uvw[0] = u_new.x;
|
||||
p->coord[0].uvw[1] = u_new.y;
|
||||
p->coord[0].uvw[2] = u_new.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1067,8 +1034,7 @@ void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Bank
|
|||
rx->products_[group].sample(E_in, site->E, mu);
|
||||
|
||||
// resample if energy is greater than maximum neutron energy
|
||||
// TODO: off-by-one
|
||||
constexpr int neutron = static_cast<int>(ParticleType::neutron) - 1;
|
||||
constexpr int neutron = static_cast<int>(ParticleType::neutron);
|
||||
if (site->E < data::energy_max[neutron]) break;
|
||||
|
||||
// check for large number of resamples
|
||||
|
|
@ -1093,8 +1059,7 @@ void sample_fission_neutron(int i_nuclide, const Reaction* rx, double E_in, Bank
|
|||
rx->products_[0].sample(E_in, site->E, mu);
|
||||
|
||||
// resample if energy is greater than maximum neutron energy
|
||||
// TODO: off-by-one
|
||||
constexpr int neutron = static_cast<int>(ParticleType::neutron) - 1;
|
||||
constexpr int neutron = static_cast<int>(ParticleType::neutron);
|
||||
if (site->E < data::energy_max[neutron]) break;
|
||||
|
||||
// check for large number of resamples
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ sample_reaction(Particle* p)
|
|||
// change when sampling fission sites. The following block handles all
|
||||
// absorption (including fission)
|
||||
|
||||
if (model::materials[p->material - 1]->fissionable) {
|
||||
if (model::materials[p->material - 1]->fissionable_) {
|
||||
if (settings::run_mode == RUN_MODE_EIGENVALUE) {
|
||||
create_fission_sites(
|
||||
p, simulation::fission_bank.data(), &simulation::n_bank,
|
||||
|
|
|
|||
|
|
@ -302,16 +302,7 @@ int reaction_product_emission_mode(Reaction* rx, int product)
|
|||
|
||||
int reaction_product_particle(Reaction* rx, int product)
|
||||
{
|
||||
switch (rx->products_[product - 1].particle_) {
|
||||
case ParticleType::neutron:
|
||||
return 1;
|
||||
case ParticleType::photon:
|
||||
return 2;
|
||||
case ParticleType::electron:
|
||||
return 3;
|
||||
case ParticleType::positron:
|
||||
return 4;
|
||||
}
|
||||
return static_cast<int>(rx->products_[product - 1].particle_);
|
||||
}
|
||||
|
||||
void reaction_product_sample(Reaction* rx, int product, double E_in, double* E_out, double* mu)
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
module sab_header
|
||||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use dict_header, only: DictCharInt
|
||||
use hdf5_interface
|
||||
|
||||
implicit none
|
||||
private
|
||||
|
||||
public :: free_memory_sab, sab_from_hdf5, sab_has_nuclide, sab_threshold
|
||||
|
||||
! S(a,b) tables
|
||||
integer, public :: n_sab_tables
|
||||
type(DictCharInt), public :: sab_dict
|
||||
|
||||
interface
|
||||
subroutine sab_from_hdf5(group_id, temperature, n) bind(C)
|
||||
import HID_T, C_DOUBLE, C_INT
|
||||
integer(HID_T), value :: group_id
|
||||
real(C_DOUBLE), intent(in) :: temperature
|
||||
integer(C_INT), value :: n
|
||||
end subroutine
|
||||
|
||||
subroutine sab_clear() bind(C)
|
||||
end subroutine
|
||||
|
||||
function sab_has_nuclide(i_sab, name) result(val) bind(C)
|
||||
import C_INT, C_CHAR, C_BOOL
|
||||
integer(C_INT), value :: i_sab
|
||||
character(kind=C_CHAR), intent(in) :: name(*)
|
||||
logical(C_BOOL) :: val
|
||||
end function
|
||||
|
||||
function sab_threshold(i_sab) result(threshold) bind(C)
|
||||
import C_INT, C_DOUBLE
|
||||
integer(C_INT), value :: i_sab
|
||||
real(C_DOUBLE) :: threshold
|
||||
end function
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY_SAB deallocates global arrays defined in this module
|
||||
!===============================================================================
|
||||
|
||||
subroutine free_memory_sab()
|
||||
n_sab_tables = 0
|
||||
call sab_clear()
|
||||
call sab_dict % clear()
|
||||
end subroutine free_memory_sab
|
||||
|
||||
end module sab_header
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include "openmc/error.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/settings.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
|
|
@ -850,10 +851,10 @@ ScattDataTabular::combine(const std::vector<ScattData*>& those_scatts,
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab,
|
||||
int n_mu)
|
||||
convert_legendre_to_tabular(ScattDataLegendre& leg, ScattDataTabular& tab)
|
||||
{
|
||||
// See if the user wants us to figure out how many points to use
|
||||
int n_mu = settings::legendre_to_tabular_points;
|
||||
if (n_mu == C_NONE) {
|
||||
// then we will use 2 pts if its P0, or the default if a higher order
|
||||
// TODO use an error minimization algorithm that also picks n_mu
|
||||
|
|
|
|||
|
|
@ -1,117 +0,0 @@
|
|||
module set_header
|
||||
|
||||
!===============================================================================
|
||||
! SET_HEADER module
|
||||
!
|
||||
! This module provides an implementation of sets based on the list
|
||||
! implementation in list_header. The underlying datatype is a list, so adding an
|
||||
! element just checks if the element is already in the list, and if not it's
|
||||
! added. This results in much worse performance than an implementation based on
|
||||
! hash tables or binary trees, but for our purposes, we don't expect to have
|
||||
! gigantic sets where performance is critical.
|
||||
!===============================================================================
|
||||
|
||||
use constants, only: MAX_WORD_LEN
|
||||
use list_header
|
||||
|
||||
implicit none
|
||||
|
||||
!===============================================================================
|
||||
! SET contains a list of elements and methods to add, remove, and perform other
|
||||
! basic tasks.
|
||||
!===============================================================================
|
||||
|
||||
type :: SetChar
|
||||
private
|
||||
type(ListChar) :: elements
|
||||
contains
|
||||
procedure :: add => set_add_char
|
||||
procedure :: clear => set_clear_char
|
||||
procedure :: contains => set_contains_char
|
||||
procedure :: get_item => set_get_item_char
|
||||
procedure :: remove => set_remove_char
|
||||
procedure :: size => set_size_char
|
||||
end type SetChar
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! SET_ADD adds an item to a set if it is not already present in the set
|
||||
!===============================================================================
|
||||
|
||||
subroutine set_add_char(this, data)
|
||||
class(SetChar) :: this
|
||||
character(*) :: data
|
||||
|
||||
if (.not. this % elements % contains(data)) then
|
||||
call this % elements % append(data)
|
||||
end if
|
||||
|
||||
end subroutine set_add_char
|
||||
|
||||
!===============================================================================
|
||||
! SET_CLEAR removes all items in a set
|
||||
!===============================================================================
|
||||
|
||||
subroutine set_clear_char(this)
|
||||
class(SetChar) :: this
|
||||
|
||||
call this % elements % clear()
|
||||
|
||||
end subroutine set_clear_char
|
||||
|
||||
!===============================================================================
|
||||
! SET_CONTAINS determines if a specified item is in a set
|
||||
!===============================================================================
|
||||
|
||||
function set_contains_char(this, data) result(in_set)
|
||||
class(SetChar) :: this
|
||||
character(*) :: data
|
||||
logical :: in_set
|
||||
|
||||
in_set = this % elements % contains(data)
|
||||
|
||||
end function set_contains_char
|
||||
|
||||
!===============================================================================
|
||||
! SET_GET_ITEM returns the i-th item in the set
|
||||
!===============================================================================
|
||||
|
||||
function set_get_item_char(this, i_list) result(data)
|
||||
|
||||
class(SetChar) :: this
|
||||
integer :: i_list
|
||||
character(MAX_WORD_LEN) :: data
|
||||
|
||||
data = this % elements % get_item(i_list)
|
||||
|
||||
end function set_get_item_char
|
||||
|
||||
!===============================================================================
|
||||
! SET_REMOVE removes the specified item from the set. If it is not in the set,
|
||||
! no action is taken.
|
||||
!===============================================================================
|
||||
|
||||
subroutine set_remove_char(this, data)
|
||||
|
||||
class(SetChar) :: this
|
||||
character(*) :: data
|
||||
|
||||
call this % elements % remove(data)
|
||||
|
||||
end subroutine set_remove_char
|
||||
|
||||
!===============================================================================
|
||||
! SET_SIZE returns the number of elements in the set
|
||||
!===============================================================================
|
||||
|
||||
function set_size_char(this) result(size)
|
||||
|
||||
class(SetChar) :: this
|
||||
integer :: size
|
||||
|
||||
size = this % elements % size()
|
||||
|
||||
end function set_size_char
|
||||
|
||||
end module set_header
|
||||
|
|
@ -399,13 +399,6 @@ void read_settings_xml()
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef _OPENMP
|
||||
if (dagmc && omp_get_max_threads() > 1) {
|
||||
warning("Forcing number of threads to 1 for DAGMC simulation.");
|
||||
omp_set_num_threads(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ==========================================================================
|
||||
// EXTERNAL SOURCE
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ module simulation
|
|||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use material_header, only: n_materials, materials
|
||||
use nuclide_header, only: micro_xs, n_nuclides
|
||||
use photon_header, only: micro_photon_xs, n_elements
|
||||
use tally_filter_header, only: filter_matches, n_filters, filter_match_pointer
|
||||
|
|
@ -20,11 +19,6 @@ contains
|
|||
|
||||
integer :: i
|
||||
|
||||
! Set up material nuclide index mapping
|
||||
do i = 1, n_materials
|
||||
call materials(i) % init_nuclide_index()
|
||||
end do
|
||||
|
||||
!$omp parallel
|
||||
! Allocate array for microscopic cross section cache
|
||||
allocate(micro_xs(n_nuclides))
|
||||
|
|
@ -46,12 +40,7 @@ contains
|
|||
|
||||
subroutine simulation_finalize_f() bind(C)
|
||||
|
||||
integer :: i ! loop index
|
||||
|
||||
! Free up simulation-specific memory
|
||||
do i = 1, n_materials
|
||||
deallocate(materials(i) % mat_nuclide_index)
|
||||
end do
|
||||
!$omp parallel
|
||||
deallocate(micro_xs, micro_photon_xs, filter_matches)
|
||||
!$omp end parallel
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "openmc/container_util.h"
|
||||
#include "openmc/eigenvalue.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/material.h"
|
||||
#include "openmc/message_passing.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/output.h"
|
||||
|
|
@ -91,6 +92,11 @@ int openmc_simulation_init()
|
|||
// Allocate tally results arrays if they're not allocated yet
|
||||
allocate_tally_results();
|
||||
|
||||
// Set up material nuclide index mapping
|
||||
for (auto& mat : model::materials) {
|
||||
mat->init_nuclide_index();
|
||||
}
|
||||
|
||||
// Call Fortran initialization
|
||||
simulation_init_f();
|
||||
set_micro_xs();
|
||||
|
|
@ -144,6 +150,9 @@ int openmc_simulation_finalize()
|
|||
}
|
||||
|
||||
// Deallocate Fortran variables, set tallies to inactive
|
||||
for (auto& mat : model::materials) {
|
||||
mat->mat_nuclide_index_.clear();
|
||||
}
|
||||
simulation_finalize_f();
|
||||
|
||||
// Increment total number of generations
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ module simulation_header
|
|||
! Number of lost particles
|
||||
integer(C_INT), bind(C) :: n_lost_particles
|
||||
|
||||
real(C_DOUBLE), bind(C) :: log_spacing ! spacing on logarithmic grid
|
||||
|
||||
! ============================================================================
|
||||
! SIMULATION VARIABLES
|
||||
|
||||
|
|
|
|||
|
|
@ -174,14 +174,11 @@ Bank SourceDistribution::sample() const
|
|||
// Determine material
|
||||
auto c = model::cells[cell_index - 1];
|
||||
int32_t mat_index = c->material_[instance];
|
||||
auto m = model::materials[mat_index];
|
||||
|
||||
if (mat_index == MATERIAL_VOID) {
|
||||
found = false;
|
||||
} else {
|
||||
bool fissionable;
|
||||
openmc_material_get_fissionable(mat_index + 1, &fissionable);
|
||||
if (!fissionable) found = false;
|
||||
if (!model::materials[mat_index]->fissionable_) found = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -212,10 +209,10 @@ Bank SourceDistribution::sample() const
|
|||
auto energy_ptr = dynamic_cast<Discrete*>(energy_.get());
|
||||
if (energy_ptr) {
|
||||
auto energies = xt::adapt(energy_ptr->x());
|
||||
if (xt::any(energies > data::energy_max[p-1])) {
|
||||
if (xt::any(energies > data::energy_max[p])) {
|
||||
fatal_error("Source energy above range of energies of at least "
|
||||
"one cross section table");
|
||||
} else if (xt::any(energies < data::energy_min[p-1])) {
|
||||
} else if (xt::any(energies < data::energy_min[p])) {
|
||||
fatal_error("Source energy below range of energies of at least "
|
||||
"one cross section table");
|
||||
}
|
||||
|
|
@ -226,7 +223,7 @@ Bank SourceDistribution::sample() const
|
|||
site.E = energy_->sample();
|
||||
|
||||
// Resample if energy falls outside minimum or maximum particle energy
|
||||
if (site.E < data::energy_max[p-1] && site.E > data::energy_min[p-1]) break;
|
||||
if (site.E < data::energy_max[p] && site.E > data::energy_min[p]) break;
|
||||
}
|
||||
|
||||
// Set delayed group
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ contains
|
|||
call write_dataset(runtime_group, "total initialization", &
|
||||
time_initialize_elapsed())
|
||||
call write_dataset(runtime_group, "reading cross sections", &
|
||||
time_read_xs % get_value())
|
||||
time_read_xs_elapsed())
|
||||
call write_dataset(runtime_group, "simulation", &
|
||||
time_inactive_elapsed() + time_active_elapsed())
|
||||
call write_dataset(runtime_group, "transport", &
|
||||
|
|
|
|||
288
src/summary.F90
288
src/summary.F90
|
|
@ -1,288 +0,0 @@
|
|||
module summary
|
||||
|
||||
use constants
|
||||
use endf, only: reaction_name
|
||||
use error, only: write_message
|
||||
use geometry_header
|
||||
use hdf5_interface
|
||||
use material_header, only: Material, n_materials, openmc_material_get_volume
|
||||
use message_passing
|
||||
use mgxs_interface
|
||||
use nuclide_header
|
||||
use output, only: time_stamp
|
||||
use settings, only: run_CE
|
||||
use surface_header
|
||||
use string, only: to_str
|
||||
use tally_header, only: TallyObject
|
||||
|
||||
implicit none
|
||||
private
|
||||
|
||||
public :: write_summary
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_SUMMARY
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_summary()
|
||||
|
||||
interface
|
||||
subroutine write_geometry(file_id) bind(C)
|
||||
import HID_T
|
||||
integer(HID_T), intent(in), value :: file_id
|
||||
end subroutine write_geometry
|
||||
end interface
|
||||
|
||||
integer(HID_T) :: file_id
|
||||
|
||||
! Display output message
|
||||
call write_message("Writing summary.h5 file...", 5)
|
||||
|
||||
! Create a new file using default properties.
|
||||
file_id = file_open("summary.h5", 'w')
|
||||
|
||||
call write_header(file_id)
|
||||
call write_nuclides(file_id)
|
||||
call write_geometry(file_id)
|
||||
call write_materials(file_id)
|
||||
|
||||
! Terminate access to the file.
|
||||
call file_close(file_id)
|
||||
|
||||
end subroutine write_summary
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_HEADER
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_header(file_id)
|
||||
integer(HID_T), intent(in) :: file_id
|
||||
|
||||
! Write filetype and version info
|
||||
call write_attribute(file_id, "filetype", "summary")
|
||||
call write_attribute(file_id, "version", VERSION_SUMMARY)
|
||||
call write_attribute(file_id, "openmc_version", VERSION)
|
||||
#ifdef GIT_SHA1
|
||||
call write_attribute(file_id, "git_sha1", GIT_SHA1)
|
||||
#endif
|
||||
|
||||
! Write current date and time
|
||||
call write_attribute(file_id, "date_and_time", time_stamp())
|
||||
|
||||
end subroutine write_header
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_NUCLIDES
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_nuclides(file_id)
|
||||
integer(HID_T), intent(in) :: file_id
|
||||
integer(HID_T) :: nuclide_group
|
||||
integer(HID_T) :: macro_group
|
||||
integer :: i
|
||||
character(kind=C_CHAR, len=20), allocatable :: nuc_names(:)
|
||||
character(kind=C_CHAR, len=20), allocatable :: macro_names(:)
|
||||
real(C_DOUBLE), allocatable :: awrs(:)
|
||||
integer :: num_nuclides
|
||||
integer :: num_macros
|
||||
integer :: j
|
||||
integer :: k
|
||||
|
||||
! Find how many of these nuclides are macroscopic objects
|
||||
if (run_CE) then
|
||||
! Then none are macroscopic
|
||||
num_nuclides = n_nuclides
|
||||
num_macros = 0
|
||||
else
|
||||
num_nuclides = 0
|
||||
num_macros = 0
|
||||
do i = 1, n_nuclides
|
||||
if (get_awr_c(i) /= MACROSCOPIC_AWR) then
|
||||
num_nuclides = num_nuclides + 1
|
||||
else
|
||||
num_macros = num_macros + 1
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! Build array of nuclide names and awrs while only sorting nuclides from
|
||||
! macroscopics
|
||||
if (num_nuclides > 0) then
|
||||
allocate(nuc_names(num_nuclides))
|
||||
allocate(awrs(num_nuclides))
|
||||
end if
|
||||
if (num_macros > 0) then
|
||||
allocate(macro_names(num_macros))
|
||||
end if
|
||||
|
||||
j = 1
|
||||
k = 1
|
||||
do i = 1, n_nuclides
|
||||
if (run_CE) then
|
||||
nuc_names(i) = nuclides(i) % name
|
||||
awrs(i) = nuclides(i) % awr
|
||||
else
|
||||
if (get_awr_c(i) /= MACROSCOPIC_AWR) then
|
||||
call get_name_c(i, len(nuc_names(j)), nuc_names(j))
|
||||
nuc_names(j) = trim(nuc_names(j))
|
||||
awrs(j) = get_awr_c(i)
|
||||
j = j + 1
|
||||
else
|
||||
call get_name_c(i, len(macro_names(k)), macro_names(k))
|
||||
macro_names(k) = trim(macro_names(k))
|
||||
k = k + 1
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
|
||||
nuclide_group = create_group(file_id, "nuclides")
|
||||
call write_attribute(nuclide_group, "n_nuclides", num_nuclides)
|
||||
macro_group = create_group(file_id, "macroscopics")
|
||||
call write_attribute(macro_group, "n_macroscopics", num_macros)
|
||||
! Write nuclide names and awrs
|
||||
if (num_nuclides > 0) then
|
||||
! Write useful data from nuclide objects
|
||||
call write_dataset(nuclide_group, "names", nuc_names)
|
||||
call write_dataset(nuclide_group, "awrs", awrs)
|
||||
end if
|
||||
if (num_macros > 0) then
|
||||
! Write useful data from macroscopic objects
|
||||
call write_dataset(macro_group, "names", macro_names)
|
||||
end if
|
||||
call close_group(nuclide_group)
|
||||
call close_group(macro_group)
|
||||
|
||||
|
||||
if (allocated(nuc_names)) deallocate(nuc_names, awrs)
|
||||
if (allocated(macro_names)) deallocate(macro_names)
|
||||
|
||||
end subroutine write_nuclides
|
||||
|
||||
!===============================================================================
|
||||
! WRITE_MATERIALS
|
||||
!===============================================================================
|
||||
|
||||
subroutine write_materials(file_id)
|
||||
integer(HID_T), intent(in) :: file_id
|
||||
|
||||
integer :: i
|
||||
integer :: j
|
||||
integer :: k
|
||||
integer :: n
|
||||
integer :: err
|
||||
character(kind=C_CHAR, len=20), allocatable :: nuc_names(:)
|
||||
character(kind=C_CHAR, len=20), allocatable :: macro_names(:)
|
||||
real(8) :: volume
|
||||
real(8), allocatable :: nuc_densities(:)
|
||||
integer :: num_nuclides
|
||||
integer :: num_macros
|
||||
integer(HID_T) :: materials_group
|
||||
integer(HID_T) :: material_group
|
||||
type(Material), pointer :: m
|
||||
|
||||
materials_group = create_group(file_id, "materials")
|
||||
|
||||
! write number of materials
|
||||
call write_dataset(file_id, "n_materials", n_materials)
|
||||
|
||||
! Write information on each material
|
||||
do i = 1, n_materials
|
||||
m => materials(i)
|
||||
material_group = create_group(materials_group, "material " // &
|
||||
trim(to_str(m%id())))
|
||||
|
||||
if (m % depletable) then
|
||||
call write_attribute(material_group, "depletable", 1)
|
||||
else
|
||||
call write_attribute(material_group, "depletable", 0)
|
||||
end if
|
||||
|
||||
err = openmc_material_get_volume(i, volume)
|
||||
if (err == 0 .and. volume > ZERO) then
|
||||
call write_attribute(material_group, "volume", volume)
|
||||
end if
|
||||
|
||||
! Write name for this material
|
||||
call write_dataset(material_group, "name", m % name)
|
||||
|
||||
! Write atom density with units
|
||||
call write_dataset(material_group, "atom_density", m % density)
|
||||
|
||||
if (run_CE) then
|
||||
num_nuclides = m % n_nuclides
|
||||
num_macros = 0
|
||||
else
|
||||
! Find the number of macroscopic and nuclide data in this material
|
||||
num_nuclides = 0
|
||||
num_macros = 0
|
||||
do j = 1, m % n_nuclides
|
||||
if (get_awr_c(m % nuclide(j)) /= MACROSCOPIC_AWR) then
|
||||
num_nuclides = num_nuclides + 1
|
||||
else
|
||||
num_macros = num_macros + 1
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! Copy ZAID or macro name for each nuclide to temporary array
|
||||
if (num_nuclides > 0) then
|
||||
allocate(nuc_names(num_nuclides))
|
||||
allocate(nuc_densities(num_nuclides))
|
||||
end if
|
||||
if (run_CE) then
|
||||
do j = 1, m % n_nuclides
|
||||
nuc_names(j) = nuclides(m%nuclide(j))%name
|
||||
nuc_densities(j) = m % atom_density(j)
|
||||
end do
|
||||
else
|
||||
if (num_macros > 0) then
|
||||
allocate(macro_names(num_macros))
|
||||
end if
|
||||
|
||||
k = 1
|
||||
n = 1
|
||||
do j = 1, m % n_nuclides
|
||||
if (get_awr_c(m % nuclide(j)) /= MACROSCOPIC_AWR) then
|
||||
call get_name_c(m % nuclide(j), len(nuc_names(k)), nuc_names(k))
|
||||
nuc_names(k) = trim(nuc_names(k))
|
||||
nuc_densities(k) = m % atom_density(j)
|
||||
k = k + 1
|
||||
else
|
||||
call get_name_c(m % nuclide(j), len(macro_names(n)), macro_names(n))
|
||||
macro_names(n) = trim(macro_names(n))
|
||||
n = n + 1
|
||||
end if
|
||||
end do
|
||||
end if
|
||||
|
||||
! Write temporary array to 'nuclides'
|
||||
if (num_nuclides > 0) then
|
||||
call write_dataset(material_group, "nuclides", nuc_names)
|
||||
! Deallocate temporary array
|
||||
deallocate(nuc_names)
|
||||
! Write nuclide atom densities
|
||||
call write_dataset(material_group, "nuclide_densities", nuc_densities)
|
||||
deallocate(nuc_densities)
|
||||
end if
|
||||
|
||||
! Write temporary array to 'macroscopics'
|
||||
if (num_macros > 0) then
|
||||
call write_dataset(material_group, "macroscopics", macro_names)
|
||||
! Deallocate temporary array
|
||||
deallocate(macro_names)
|
||||
end if
|
||||
|
||||
if (m%n_sab > 0) then
|
||||
call write_dataset(material_group, "sab_names", m%sab_names)
|
||||
end if
|
||||
|
||||
call close_group(material_group)
|
||||
end do
|
||||
|
||||
call close_group(materials_group)
|
||||
|
||||
end subroutine write_materials
|
||||
|
||||
end module summary
|
||||
|
|
@ -1,15 +1,93 @@
|
|||
#include "openmc/summary.h"
|
||||
|
||||
#include "openmc/cell.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/lattice.h"
|
||||
#include "openmc/material.h"
|
||||
#include "openmc/mgxs_interface.h"
|
||||
#include "openmc/nuclide.h"
|
||||
#include "openmc/output.h"
|
||||
#include "openmc/surface.h"
|
||||
#include "openmc/settings.h"
|
||||
|
||||
namespace openmc {
|
||||
|
||||
extern "C" void
|
||||
write_geometry(hid_t file_id) {
|
||||
void write_summary()
|
||||
{
|
||||
// Display output message
|
||||
write_message("Writing summary.h5 file...", 5);
|
||||
|
||||
auto geom_group = create_group(file_id, "geometry");
|
||||
// Create a new file using default properties.
|
||||
hid_t file = file_open("summary.h5", 'w');
|
||||
|
||||
write_header(file);
|
||||
write_nuclides(file);
|
||||
write_geometry(file);
|
||||
write_materials(file);
|
||||
|
||||
// Terminate access to the file.
|
||||
file_close(file);
|
||||
}
|
||||
|
||||
void write_header(hid_t file)
|
||||
{
|
||||
// Write filetype and version info
|
||||
write_attribute(file, "filetype", "summary");
|
||||
write_attribute(file, "version", VERSION_SUMMARY);
|
||||
write_attribute(file, "openmc_version", VERSION);
|
||||
#ifdef GIT_SHA1
|
||||
write_attribute(file, "git_sha1", GIT_SHA1);
|
||||
#endif
|
||||
|
||||
// Write current date and time
|
||||
write_attribute(file, "date_and_time", time_stamp());
|
||||
}
|
||||
|
||||
void write_nuclides(hid_t file)
|
||||
{
|
||||
// Build vectors of nuclide names and awrs while only sorting nuclides from
|
||||
// macroscopics
|
||||
std::vector<std::string> nuc_names;
|
||||
std::vector<std::string> macro_names;
|
||||
std::vector<double> awrs;
|
||||
|
||||
for (int i = 0; i < data::nuclides.size(); ++i) {
|
||||
if (settings::run_CE) {
|
||||
const auto& nuc {data::nuclides[i]};
|
||||
nuc_names.push_back(nuc->name_);
|
||||
awrs.push_back(nuc->awr_);
|
||||
} else {
|
||||
const auto& nuc {data::nuclides_MG[i]};
|
||||
if (nuc.awr != MACROSCOPIC_AWR) {
|
||||
nuc_names.push_back(nuc.name);
|
||||
awrs.push_back(nuc.awr);
|
||||
} else {
|
||||
macro_names.push_back(nuc.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hid_t nuclide_group = create_group(file, "nuclides");
|
||||
write_attribute(nuclide_group, "n_nuclides", nuc_names.size());
|
||||
hid_t macro_group = create_group(file, "macroscopics");
|
||||
write_attribute(macro_group, "n_macroscopics", macro_names.size());
|
||||
// Write nuclide names and awrs
|
||||
if (!nuc_names.empty()) {
|
||||
// Write useful data from nuclide objects
|
||||
write_dataset(nuclide_group, "names", nuc_names);
|
||||
write_dataset(nuclide_group, "awrs", awrs);
|
||||
}
|
||||
if (!macro_names.empty()) {
|
||||
// Write useful data from macroscopic objects
|
||||
write_dataset(macro_group, "names", macro_names);
|
||||
}
|
||||
close_group(nuclide_group);
|
||||
close_group(macro_group);
|
||||
}
|
||||
|
||||
void write_geometry(hid_t file)
|
||||
{
|
||||
auto geom_group = create_group(file, "geometry");
|
||||
|
||||
#ifdef DAGMC
|
||||
if (settings::dagmc) {
|
||||
|
|
@ -42,4 +120,16 @@ write_geometry(hid_t file_id) {
|
|||
close_group(geom_group);
|
||||
}
|
||||
|
||||
void write_materials(hid_t file)
|
||||
{
|
||||
// write number of materials
|
||||
write_dataset(file, "n_materials", model::materials.size());
|
||||
|
||||
hid_t materials_group = create_group(file, "materials");
|
||||
for (const auto& mat : model::materials) {
|
||||
mat->to_hdf5(materials_group);
|
||||
}
|
||||
close_group(materials_group);
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ void
|
|||
ParticleFilter::from_xml(pugi::xml_node node)
|
||||
{
|
||||
particles_ = get_node_array<int>(node, "bins");
|
||||
for (auto& p : particles_) --p;
|
||||
n_bins_ = particles_.size();
|
||||
}
|
||||
|
||||
|
|
@ -34,8 +35,7 @@ ParticleFilter::to_statepoint(hid_t filter_group) const
|
|||
std::string
|
||||
ParticleFilter::text_label(int bin) const
|
||||
{
|
||||
//TODO: off-by-one
|
||||
return "Particle " + std::to_string(particles_[bin-1]);
|
||||
return "Particle " + std::to_string(particles_[bin]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -43,6 +43,6 @@ ParticleFilter::text_label(int bin) const
|
|||
//==============================================================================
|
||||
|
||||
extern "C" int particle_filter_particles(ParticleFilter* filt, int i)
|
||||
{return filt->particles_[i-1];}
|
||||
{return filt->particles_[i];}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ module tally
|
|||
use dict_header, only: EMPTY
|
||||
use error, only: fatal_error
|
||||
use geometry_header
|
||||
use material_header
|
||||
use math, only: t_percentile
|
||||
use message_passing
|
||||
use mgxs_interface
|
||||
|
|
@ -422,13 +423,13 @@ contains
|
|||
|
||||
! Loop over all nuclides in the current material
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
! Accumulate the contribution from each nuclide
|
||||
score = score + micro_xs(i_nuc) % fission * nuclides(i_nuc) % &
|
||||
|
|
@ -581,13 +582,13 @@ contains
|
|||
|
||||
! Loop over all nuclides in the current material
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
! Loop over all delayed group bins and tally to them
|
||||
! individually
|
||||
|
|
@ -615,13 +616,13 @@ contains
|
|||
|
||||
! Loop over all nuclides in the current material
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
! Accumulate the contribution from each nuclide
|
||||
score = score + micro_xs(i_nuc) % fission * nuclides(i_nuc) %&
|
||||
|
|
@ -833,13 +834,13 @@ contains
|
|||
|
||||
! Loop over all nuclides in the current material
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
if (nuclides(i_nuc) % fissionable) then
|
||||
|
||||
|
|
@ -878,13 +879,13 @@ contains
|
|||
|
||||
! Loop over all nuclides in the current material
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
if (nuclides(i_nuc) % fissionable) then
|
||||
|
||||
|
|
@ -964,10 +965,10 @@ contains
|
|||
if (p % material == MATERIAL_VOID) then
|
||||
score = ZERO
|
||||
else
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
! Determine atom density and index of nuclide
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
! If nuclide is fissionable, accumulate kappa fission
|
||||
associate(nuc => nuclides(i_nuc))
|
||||
|
|
@ -1001,12 +1002,12 @@ contains
|
|||
else
|
||||
score = ZERO
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
if (micro_xs(i_nuc) % elastic == CACHE_INVALID) then
|
||||
call nuclides(i_nuc) % calculate_elastic_xs()
|
||||
end if
|
||||
|
|
@ -1075,9 +1076,9 @@ contains
|
|||
end associate
|
||||
else
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (score_bin == SCORE_FISS_Q_PROMPT) then
|
||||
|
|
@ -1122,13 +1123,11 @@ contains
|
|||
else
|
||||
score = ZERO
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
associate (mat => materials(p % material))
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
i_nuc = mat % nuclide(l)
|
||||
atom_density_ = mat % atom_density(l)
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
score = score + micro_xs(i_nuc) % reaction(m) * atom_density_ * flux
|
||||
end do
|
||||
end associate
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
|
|
@ -1176,12 +1175,12 @@ contains
|
|||
|
||||
else
|
||||
if (p % material /= MATERIAL_VOID) then
|
||||
do l = 1, materials(p % material) % n_nuclides
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
! Get atom density
|
||||
atom_density_ = materials(p % material) % atom_density(l)
|
||||
atom_density_ = material_atom_density(p % material, l)
|
||||
|
||||
! Get index in nuclides array
|
||||
i_nuc = materials(p % material) % nuclide(l)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
|
||||
m = nuclides(i_nuc) % reaction_index(score_bin)
|
||||
if (m /= 0) then
|
||||
|
|
@ -2045,23 +2044,18 @@ contains
|
|||
integer :: i ! loop index for nuclides in material
|
||||
integer :: i_nuclide ! index in nuclides array
|
||||
real(8) :: atom_density ! atom density of single nuclide in atom/b-cm
|
||||
type(Material), pointer :: mat
|
||||
|
||||
associate (t => tallies(i_tally) % obj)
|
||||
|
||||
! Get pointer to current material. We need this in order to determine what
|
||||
! nuclides are in the material
|
||||
mat => materials(p % material)
|
||||
|
||||
! ==========================================================================
|
||||
! SCORE ALL INDIVIDUAL NUCLIDE REACTION RATES
|
||||
|
||||
NUCLIDE_LOOP: do i = 1, mat % n_nuclides
|
||||
NUCLIDE_LOOP: do i = 1, material_nuclide_size(p % material)
|
||||
|
||||
! Determine index in nuclides array and atom density for i-th nuclide in
|
||||
! current material
|
||||
i_nuclide = mat % nuclide(i)
|
||||
atom_density = mat % atom_density(i)
|
||||
i_nuclide = material_nuclide(p % material, i)
|
||||
atom_density = material_atom_density(p % material, i)
|
||||
|
||||
! Determine score for each bin
|
||||
call score_general(p, i_tally, (i_nuclide-1)*t % n_score_bins(), filter_index, &
|
||||
|
|
@ -2320,6 +2314,7 @@ contains
|
|||
|
||||
type(TallyDerivative), pointer :: deriv
|
||||
integer :: l
|
||||
integer :: i_nuc
|
||||
logical :: scoring_diff_nuclide
|
||||
real(8) :: flux_deriv
|
||||
real(8) :: dsig_s, dsig_a, dsig_f, cum_dsig
|
||||
|
|
@ -2358,9 +2353,9 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id() == deriv % diff_material) then
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
score = score * (flux_deriv + ONE &
|
||||
/ materials(p % material) % density_gpcc)
|
||||
/ material_density_gpcc(p % material))
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
|
@ -2379,9 +2374,9 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id() == deriv % diff_material) then
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
score = score * (flux_deriv + ONE &
|
||||
/ materials(p % material) % density_gpcc)
|
||||
/ material_density_gpcc(p % material))
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
|
@ -2421,17 +2416,15 @@ contains
|
|||
|
||||
case (SCORE_TOTAL, SCORE_SCATTER, SCORE_ABSORPTION, SCORE_FISSION, &
|
||||
SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id() == deriv % diff_material &
|
||||
if (material_id(p % material) == deriv % diff_material &
|
||||
.and. p % event_nuclide == deriv % diff_nuclide) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == deriv % diff_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == deriv % diff_nuclide) exit
|
||||
end do
|
||||
|
||||
score = score * (flux_deriv &
|
||||
+ ONE / mat % atom_density(l))
|
||||
end associate
|
||||
+ ONE / material_atom_density(p % material, l))
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
|
@ -2443,7 +2436,7 @@ contains
|
|||
|
||||
case (ESTIMATOR_COLLISION)
|
||||
scoring_diff_nuclide = &
|
||||
(materials(p % material) % id() == deriv % diff_material) &
|
||||
(material_id(p % material) == deriv % diff_material) &
|
||||
.and. (i_nuclide == deriv % diff_nuclide)
|
||||
|
||||
select case (score_bin)
|
||||
|
|
@ -2453,7 +2446,7 @@ contains
|
|||
|
||||
case (SCORE_TOTAL)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % total /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % total &
|
||||
|
|
@ -2467,7 +2460,7 @@ contains
|
|||
|
||||
case (SCORE_SCATTER)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % total - material_xs % absorption /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ (micro_xs(deriv % diff_nuclide) % total &
|
||||
|
|
@ -2483,7 +2476,7 @@ contains
|
|||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % absorption /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % absorption &
|
||||
|
|
@ -2497,7 +2490,7 @@ contains
|
|||
|
||||
case (SCORE_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % fission /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % fission &
|
||||
|
|
@ -2511,7 +2504,7 @@ contains
|
|||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % nu_fission /= ZERO) then
|
||||
score = score * (flux_deriv &
|
||||
+ micro_xs(deriv % diff_nuclide) % nu_fission &
|
||||
|
|
@ -2557,12 +2550,11 @@ contains
|
|||
score = score * flux_deriv
|
||||
|
||||
case (SCORE_TOTAL)
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
if (material_id(p % material) == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % total > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == p % event_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == p % event_nuclide) exit
|
||||
end do
|
||||
|
||||
dsig_s = ZERO
|
||||
|
|
@ -2574,21 +2566,19 @@ contains
|
|||
end if
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ (dsig_s + dsig_a) * mat % atom_density(l) &
|
||||
+ (dsig_s + dsig_a) * material_atom_density(p % material, l) &
|
||||
/ material_xs % total)
|
||||
end associate
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
||||
case (SCORE_SCATTER)
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
if (material_id(p % material) == deriv % diff_material .and. &
|
||||
(micro_xs(p % event_nuclide) % total &
|
||||
- micro_xs(p % event_nuclide) % absorption) > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == p % event_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == p % event_nuclide) exit
|
||||
end do
|
||||
|
||||
dsig_s = ZERO
|
||||
|
|
@ -2598,20 +2588,18 @@ contains
|
|||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
end if
|
||||
end associate
|
||||
score = score * (flux_deriv + dsig_s * mat % atom_density(l) / &
|
||||
score = score * (flux_deriv + dsig_s * material_atom_density(p % material, l) / &
|
||||
(material_xs % total - material_xs % absorption))
|
||||
end associate
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
if (material_id(p % material) == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % absorption > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == p % event_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == p % event_nuclide) exit
|
||||
end do
|
||||
|
||||
dsig_a = ZERO
|
||||
|
|
@ -2621,20 +2609,18 @@ contains
|
|||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
end if
|
||||
end associate
|
||||
score = score * (flux_deriv + dsig_a * mat % atom_density(l) &
|
||||
score = score * (flux_deriv + dsig_a * material_atom_density(p % material, l) &
|
||||
/ material_xs % absorption)
|
||||
end associate
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
||||
case (SCORE_FISSION)
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
if (material_id(p % material) == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % fission > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == p % event_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == p % event_nuclide) exit
|
||||
end do
|
||||
|
||||
dsig_f = ZERO
|
||||
|
|
@ -2645,19 +2631,17 @@ contains
|
|||
end if
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ dsig_f * mat % atom_density(l) / material_xs % fission)
|
||||
end associate
|
||||
+ dsig_f * material_atom_density(p % material, l) / material_xs % fission)
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (materials(p % material) % id() == deriv % diff_material .and. &
|
||||
if (material_id(p % material) == deriv % diff_material .and. &
|
||||
micro_xs(p % event_nuclide) % nu_fission > ZERO) then
|
||||
associate(mat => materials(p % material))
|
||||
! Search for the index of the perturbed nuclide.
|
||||
do l = 1, mat % n_nuclides
|
||||
if (mat % nuclide(l) == p % event_nuclide) exit
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, l) == p % event_nuclide) exit
|
||||
end do
|
||||
|
||||
dsig_f = ZERO
|
||||
|
|
@ -2668,10 +2652,9 @@ contains
|
|||
end if
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ dsig_f * mat % atom_density(l) / material_xs % nu_fission&
|
||||
+ dsig_f * material_atom_density(p % material, l) / material_xs % nu_fission&
|
||||
* micro_xs(p % event_nuclide) % nu_fission &
|
||||
/ micro_xs(p % event_nuclide) % fission)
|
||||
end associate
|
||||
else
|
||||
score = score * flux_deriv
|
||||
end if
|
||||
|
|
@ -2690,25 +2673,24 @@ contains
|
|||
|
||||
case (SCORE_TOTAL)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % total > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
do l = 1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (multipole_in_range(nuc % ptr, p % last_E) .and. &
|
||||
micro_xs(mat % nuclide(l)) % total > ZERO) then
|
||||
micro_xs(i_nuc) % total > ZERO) then
|
||||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
cum_dsig = cum_dsig + (dsig_s + dsig_a) &
|
||||
* mat % atom_density(l)
|
||||
* material_atom_density(p % material, l)
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % total)
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
else if (material_id(p % material) == deriv % diff_material &
|
||||
.and. material_xs % total > ZERO) then
|
||||
dsig_s = ZERO
|
||||
dsig_a = ZERO
|
||||
|
|
@ -2726,25 +2708,24 @@ contains
|
|||
|
||||
case (SCORE_SCATTER)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
(material_xs % total - material_xs % absorption) > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
do l = 1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (multipole_in_range(nuc % ptr, p % last_E) .and. &
|
||||
(micro_xs(mat % nuclide(l)) % total &
|
||||
- micro_xs(mat % nuclide(l)) % absorption) > ZERO) then
|
||||
(micro_xs(i_nuc) % total &
|
||||
- micro_xs(i_nuc) % absorption) > ZERO) then
|
||||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
cum_dsig = cum_dsig + dsig_s * mat % atom_density(l)
|
||||
cum_dsig = cum_dsig + dsig_s * material_atom_density(p % material, l)
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end associate
|
||||
score = score * (flux_deriv + cum_dsig &
|
||||
/ (material_xs % total - material_xs % absorption))
|
||||
else if ( materials(p % material) % id() == deriv % diff_material &
|
||||
else if ( material_id(p % material) == deriv % diff_material &
|
||||
.and. (material_xs % total - material_xs % absorption) > ZERO)&
|
||||
then
|
||||
dsig_s = ZERO
|
||||
|
|
@ -2763,24 +2744,23 @@ contains
|
|||
|
||||
case (SCORE_ABSORPTION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % absorption > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
do l = 1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (multipole_in_range(nuc % ptr, p % last_E) .and. &
|
||||
micro_xs(mat % nuclide(l)) % absorption > ZERO) then
|
||||
micro_xs(i_nuc) % absorption > ZERO) then
|
||||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
cum_dsig = cum_dsig + dsig_a * mat % atom_density(l)
|
||||
cum_dsig = cum_dsig + dsig_a * material_atom_density(p % material, l)
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % absorption)
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
else if (material_id(p % material) == deriv % diff_material &
|
||||
.and. material_xs % absorption > ZERO) then
|
||||
dsig_a = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -2797,24 +2777,23 @@ contains
|
|||
|
||||
case (SCORE_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % fission > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
do l = 1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (multipole_in_range(nuc % ptr, p % last_E) .and. &
|
||||
micro_xs(mat % nuclide(l)) % fission > ZERO) then
|
||||
micro_xs(i_nuc) % fission > ZERO) then
|
||||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
cum_dsig = cum_dsig + dsig_f * mat % atom_density(l)
|
||||
cum_dsig = cum_dsig + dsig_f * material_atom_density(p % material, l)
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % fission)
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
else if (material_id(p % material) == deriv % diff_material &
|
||||
.and. material_xs % fission > ZERO) then
|
||||
dsig_f = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -2831,26 +2810,25 @@ contains
|
|||
|
||||
case (SCORE_NU_FISSION)
|
||||
if (i_nuclide == -1 .and. &
|
||||
materials(p % material) % id() == deriv % diff_material .and. &
|
||||
material_id(p % material) == deriv % diff_material .and. &
|
||||
material_xs % nu_fission > ZERO) then
|
||||
cum_dsig = ZERO
|
||||
associate(mat => materials(p % material))
|
||||
do l = 1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
do l = 1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (multipole_in_range(nuc % ptr, p % last_E) .and. &
|
||||
micro_xs(mat % nuclide(l)) % nu_fission > ZERO) then
|
||||
micro_xs(i_nuc) % nu_fission > ZERO) then
|
||||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
cum_dsig = cum_dsig + dsig_f * mat % atom_density(l) &
|
||||
* micro_xs(mat % nuclide(l)) % nu_fission &
|
||||
/ micro_xs(mat % nuclide(l)) % fission
|
||||
cum_dsig = cum_dsig + dsig_f * material_atom_density(p % material, l) &
|
||||
* micro_xs(i_nuc) % nu_fission &
|
||||
/ micro_xs(i_nuc) % fission
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end associate
|
||||
score = score * (flux_deriv &
|
||||
+ cum_dsig / material_xs % nu_fission)
|
||||
else if (materials(p % material) % id() == deriv % diff_material &
|
||||
else if (material_id(p % material) == deriv % diff_material &
|
||||
.and. material_xs % nu_fission > ZERO) then
|
||||
dsig_f = ZERO
|
||||
associate (nuc => nuclides(i_nuclide))
|
||||
|
|
@ -2900,32 +2878,27 @@ contains
|
|||
select case (deriv % variable)
|
||||
|
||||
case (DIFF_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
! phi is proportional to e^(-Sigma_tot * dist)
|
||||
! (1 / phi) * (d_phi / d_rho) = - (d_Sigma_tot / d_rho) * dist
|
||||
! (1 / phi) * (d_phi / d_rho) = - Sigma_tot / rho * dist
|
||||
deriv % flux_deriv = deriv % flux_deriv &
|
||||
- distance * material_xs % total / mat % density_gpcc
|
||||
- distance * material_xs % total / material_density_gpcc(p % material)
|
||||
end if
|
||||
end associate
|
||||
|
||||
case (DIFF_NUCLIDE_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
! phi is proportional to e^(-Sigma_tot * dist)
|
||||
! (1 / phi) * (d_phi / d_N) = - (d_Sigma_tot / d_N) * dist
|
||||
! (1 / phi) * (d_phi / d_N) = - sigma_tot * dist
|
||||
deriv % flux_deriv = deriv % flux_deriv &
|
||||
- distance * micro_xs(deriv % diff_nuclide) % total
|
||||
end if
|
||||
end associate
|
||||
|
||||
case (DIFF_TEMPERATURE)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
do l=1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
do l=1, material_nuclide_size(p % material)
|
||||
associate (nuc => nuclides(material_nuclide(p % material, l)))
|
||||
if (multipole_in_range(nuc % ptr, p % E)) then
|
||||
! phi is proportional to e^(-Sigma_tot * dist)
|
||||
! (1 / phi) * (d_phi / d_T) = - (d_Sigma_tot / d_T) * dist
|
||||
|
|
@ -2933,12 +2906,11 @@ contains
|
|||
call multipole_deriv_eval(nuc % ptr, p % E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
deriv % flux_deriv = deriv % flux_deriv &
|
||||
- distance * (dsig_s + dsig_a) * mat % atom_density(l)
|
||||
- distance * (dsig_s + dsig_a) * material_atom_density(p % material, l)
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
end if
|
||||
end associate
|
||||
end select
|
||||
!end associate
|
||||
end do
|
||||
|
|
@ -2964,7 +2936,7 @@ contains
|
|||
type(Particle), intent(in) :: p
|
||||
|
||||
type(TallyDerivative), pointer :: deriv
|
||||
integer :: i, j, l
|
||||
integer :: i, j, l, i_nuc
|
||||
real(8) :: dsig_s, dsig_a, dsig_f
|
||||
|
||||
! A void material cannot be perturbed so it will not affect flux derivatives
|
||||
|
|
@ -2976,26 +2948,23 @@ contains
|
|||
select case (deriv % variable)
|
||||
|
||||
case (DIFF_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
! phi is proportional to Sigma_s
|
||||
! (1 / phi) * (d_phi / d_rho) = (d_Sigma_s / d_rho) / Sigma_s
|
||||
! (1 / phi) * (d_phi / d_rho) = 1 / rho
|
||||
deriv % flux_deriv = deriv % flux_deriv &
|
||||
+ ONE / mat % density_gpcc
|
||||
+ ONE / material_density_gpcc(p % material)
|
||||
end if
|
||||
end associate
|
||||
|
||||
case (DIFF_NUCLIDE_DENSITY)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material &
|
||||
if (material_id(p % material) == deriv % diff_material &
|
||||
.and. p % event_nuclide == deriv % diff_nuclide) then
|
||||
! Find the index in this material for the diff_nuclide.
|
||||
do j = 1, mat % n_nuclides
|
||||
if (mat % nuclide(j) == deriv % diff_nuclide) exit
|
||||
do j = 1, material_nuclide_size(p % material)
|
||||
if (material_nuclide(p % material, j) == deriv % diff_nuclide) exit
|
||||
end do
|
||||
! Make sure we found the nuclide.
|
||||
if (mat % nuclide(j) /= deriv % diff_nuclide) then
|
||||
if (material_nuclide(p % material, j) /= deriv % diff_nuclide) then
|
||||
call fatal_error("Couldn't find the right nuclide.")
|
||||
end if
|
||||
! phi is proportional to Sigma_s
|
||||
|
|
@ -3003,16 +2972,15 @@ contains
|
|||
! (1 / phi) * (d_phi / d_N) = sigma_s / Sigma_s
|
||||
! (1 / phi) * (d_phi / d_N) = 1 / N
|
||||
deriv % flux_deriv = deriv % flux_deriv &
|
||||
+ ONE / mat % atom_density(j)
|
||||
+ ONE / material_atom_density(p % material, j)
|
||||
end if
|
||||
end associate
|
||||
|
||||
case (DIFF_TEMPERATURE)
|
||||
associate (mat => materials(p % material))
|
||||
if (mat % id() == deriv % diff_material) then
|
||||
do l=1, mat % n_nuclides
|
||||
associate (nuc => nuclides(mat % nuclide(l)))
|
||||
if (mat % nuclide(l) == p % event_nuclide .and. &
|
||||
if (material_id(p % material) == deriv % diff_material) then
|
||||
do l=1, material_nuclide_size(p % material)
|
||||
i_nuc = material_nuclide(p % material, l)
|
||||
associate (nuc => nuclides(i_nuc))
|
||||
if (i_nuc == p % event_nuclide .and. &
|
||||
multipole_in_range(nuc % ptr, p % last_E)) then
|
||||
! phi is proportional to Sigma_s
|
||||
! (1 / phi) * (d_phi / d_T) = (d_Sigma_s / d_T) / Sigma_s
|
||||
|
|
@ -3020,8 +2988,8 @@ contains
|
|||
call multipole_deriv_eval(nuc % ptr, p % last_E, &
|
||||
p % sqrtkT, dsig_s, dsig_a, dsig_f)
|
||||
deriv % flux_deriv = deriv % flux_deriv + dsig_s&
|
||||
/ (micro_xs(mat % nuclide(l)) % total &
|
||||
- micro_xs(mat % nuclide(l)) % absorption)
|
||||
/ (micro_xs(i_nuc) % total &
|
||||
- micro_xs(i_nuc) % absorption)
|
||||
! Note that this is an approximation! The real scattering
|
||||
! cross section is Sigma_s(E'->E, uvw'->uvw) =
|
||||
! Sigma_s(E') * P(E'->E, uvw'->uvw). We are assuming that
|
||||
|
|
@ -3033,7 +3001,6 @@ contains
|
|||
end associate
|
||||
end do
|
||||
end if
|
||||
end associate
|
||||
end select
|
||||
!end associate
|
||||
end do
|
||||
|
|
@ -3180,20 +3147,4 @@ contains
|
|||
end if
|
||||
end function openmc_tally_allocate
|
||||
|
||||
!===============================================================================
|
||||
! Functions for C++ interop
|
||||
!===============================================================================
|
||||
|
||||
function material_nuclide_index(i_material, i_nuclide) result(i) bind(C)
|
||||
integer(C_INT), value :: i_material, i_nuclide
|
||||
integer(C_INT) :: i
|
||||
i = materials(i_material) % mat_nuclide_index(i_nuclide)
|
||||
end function
|
||||
|
||||
function material_atom_density(i_material, i) result(dens) bind(C)
|
||||
integer(C_INT), value :: i_material, i
|
||||
real(C_DOUBLE) :: dens
|
||||
dens = materials(i_material) % atom_density(i)
|
||||
end function
|
||||
|
||||
end module tally
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ module tally_header
|
|||
use dict_header, only: DictIntInt
|
||||
use hdf5_interface, only: HID_T, HSIZE_T
|
||||
use message_passing, only: n_procs, master
|
||||
use nuclide_header, only: nuclide_dict
|
||||
use nuclide_header, only: nuclide_map_get
|
||||
use settings, only: reduce_tallies, run_mode
|
||||
use stl_vector, only: VectorInt
|
||||
use string, only: to_lower, to_f_string, str_to_int, to_str
|
||||
use string, only: to_lower, to_f_string, str_to_int, to_str, to_c_string
|
||||
use tally_filter_header, only: TallyFilterContainer, filters, n_filters
|
||||
use tally_filter
|
||||
|
||||
|
|
@ -883,6 +883,7 @@ contains
|
|||
|
||||
integer, allocatable :: bins(:)
|
||||
integer :: i
|
||||
integer :: idx
|
||||
character(C_CHAR), pointer :: string(:)
|
||||
character(len=:, kind=C_CHAR), allocatable :: nuclide_
|
||||
|
||||
|
|
@ -894,14 +895,15 @@ contains
|
|||
do i = 1, n
|
||||
! Convert C string to Fortran string
|
||||
call c_f_pointer(nuclides(i), string, [10])
|
||||
nuclide_ = to_lower(to_f_string(string))
|
||||
nuclide_ = to_f_string(string)
|
||||
|
||||
select case (nuclide_)
|
||||
case ('total')
|
||||
bins(i) = -1
|
||||
case default
|
||||
if (nuclide_dict % has(nuclide_)) then
|
||||
bins(i) = nuclide_dict % get(nuclide_)
|
||||
idx = nuclide_map_get(to_c_string(nuclide_))
|
||||
if (idx /= -1) then
|
||||
bins(i) = idx
|
||||
else
|
||||
err = E_DATA
|
||||
call set_errmsg("Nuclide '" // trim(to_f_string(string)) // &
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ namespace openmc {
|
|||
|
||||
namespace data {
|
||||
std::vector<std::unique_ptr<ThermalScattering>> thermal_scatt;
|
||||
std::unordered_map<std::string, int> thermal_scatt_map;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -602,7 +603,11 @@ sab_from_hdf5(hid_t group, const double* temperature, int n)
|
|||
return data::thermal_scatt.back().get();
|
||||
}
|
||||
|
||||
extern "C" void sab_clear() { data::thermal_scatt.clear(); }
|
||||
extern "C" void sab_clear()
|
||||
{
|
||||
data::thermal_scatt.clear();
|
||||
data::thermal_scatt_map.clear();
|
||||
}
|
||||
|
||||
extern "C" bool sab_has_nuclide(int i_sab, const char* name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ Timer time_bank_sendrecv;
|
|||
Timer time_finalize;
|
||||
Timer time_inactive;
|
||||
Timer time_initialize;
|
||||
Timer time_read_xs;
|
||||
Timer time_tallies;
|
||||
Timer time_total;
|
||||
Timer time_transport;
|
||||
|
|
@ -64,6 +65,7 @@ extern "C" double time_bank_sendrecv_elapsed() { return simulation::time_bank_se
|
|||
extern "C" double time_finalize_elapsed() { return simulation::time_finalize.elapsed(); }
|
||||
extern "C" double time_inactive_elapsed() { return simulation::time_inactive.elapsed(); }
|
||||
extern "C" double time_initialize_elapsed() { return simulation::time_initialize.elapsed(); }
|
||||
extern "C" double time_read_xs_elapsed() { return simulation::time_read_xs.elapsed(); }
|
||||
extern "C" double time_tallies_elapsed() { return simulation::time_tallies.elapsed(); }
|
||||
extern "C" double time_total_elapsed() { return simulation::time_total.elapsed(); }
|
||||
extern "C" double time_transport_elapsed() { return simulation::time_transport.elapsed(); }
|
||||
|
|
@ -81,6 +83,7 @@ void reset_timers()
|
|||
simulation::time_finalize.reset();
|
||||
simulation::time_inactive.reset();
|
||||
simulation::time_initialize.reset();
|
||||
simulation::time_read_xs.reset();
|
||||
simulation::time_tallies.reset();
|
||||
simulation::time_total.reset();
|
||||
simulation::time_transport.reset();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ module timer_header
|
|||
import C_DOUBLE
|
||||
real(C_DOUBLE) :: t
|
||||
end function
|
||||
function time_read_xs_elapsed() result(t) bind(C)
|
||||
import C_DOUBLE
|
||||
real(C_DOUBLE) :: t
|
||||
end function
|
||||
function time_tallies_elapsed() result(t) bind(C)
|
||||
import C_DOUBLE
|
||||
real(C_DOUBLE) :: t
|
||||
|
|
@ -72,8 +76,6 @@ module timer_header
|
|||
! ============================================================================
|
||||
! TIMING VARIABLES
|
||||
|
||||
type(Timer) :: time_read_xs ! timer for reading cross sections
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -137,12 +139,4 @@ contains
|
|||
self % elapsed = ZERO
|
||||
end subroutine timer_reset
|
||||
|
||||
!===============================================================================
|
||||
! RESET_TIMERS resets timers on the Fortran side
|
||||
!===============================================================================
|
||||
|
||||
subroutine reset_timers_f() bind(C)
|
||||
call time_read_xs % reset()
|
||||
end subroutine
|
||||
|
||||
end module timer_header
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module tracking
|
|||
use geometry, only: next_cell
|
||||
#endif
|
||||
|
||||
use material_header, only: materials, Material
|
||||
use material_header, only: material_calculate_xs
|
||||
use message_passing
|
||||
use mgxs_interface
|
||||
use nuclide_header
|
||||
|
|
@ -131,7 +131,7 @@ contains
|
|||
! If the material is the same as the last material and the
|
||||
! temperature hasn't changed, we don't need to lookup cross
|
||||
! sections again.
|
||||
call materials(p % material) % calculate_xs(p)
|
||||
call material_calculate_xs(p)
|
||||
end if
|
||||
else
|
||||
! Get the MG data
|
||||
|
|
@ -497,7 +497,7 @@ contains
|
|||
p % coord(1) % cell = i_cell-1 ! decrement for C++ indexing
|
||||
p % cell_instance = 1
|
||||
p % material = cells(i_cell) % material(1)
|
||||
p % sqrtKT = cells(i_cell) % sqrtKT(1)
|
||||
p % sqrtKT = cells(i_cell) % sqrtKT(0)
|
||||
return
|
||||
end if
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module volume_calc
|
|||
use hdf5_interface, only: file_open, file_close, write_attribute, &
|
||||
create_group, close_group, write_dataset, HID_T
|
||||
use output, only: header, time_stamp
|
||||
use material_header, only: materials
|
||||
use material_header
|
||||
use message_passing
|
||||
use nuclide_header, only: nuclides
|
||||
use particle_header
|
||||
|
|
@ -215,7 +215,7 @@ contains
|
|||
i_material = p % material
|
||||
if (i_material /= MATERIAL_VOID) then
|
||||
do i_domain = 1, size(this % domain_id)
|
||||
if (materials(i_material) % id() == this % domain_id(i_domain)) then
|
||||
if (material_id(i_material) == this % domain_id(i_domain)) then
|
||||
call check_hit(i_domain, i_material, indices, hits, n_mat)
|
||||
end if
|
||||
end do
|
||||
|
|
@ -328,16 +328,14 @@ contains
|
|||
i_material = master_indices(i_domain) % data(j)
|
||||
if (i_material == MATERIAL_VOID) cycle
|
||||
|
||||
associate (mat => materials(i_material))
|
||||
do k = 1, size(mat % nuclide)
|
||||
! Accumulate nuclide density
|
||||
i_nuclide = mat % nuclide(k)
|
||||
atoms(1, i_nuclide) = atoms(1, i_nuclide) + &
|
||||
mat % atom_density(k) * f
|
||||
atoms(2, i_nuclide) = atoms(2, i_nuclide) + &
|
||||
mat % atom_density(k)**2 * var_f
|
||||
end do
|
||||
end associate
|
||||
do k = 1, material_nuclide_size(i_material)
|
||||
! Accumulate nuclide density
|
||||
i_nuclide = material_nuclide(i_material, k)
|
||||
atoms(1, i_nuclide) = atoms(1, i_nuclide) + &
|
||||
material_atom_density(i_material, k) * f
|
||||
atoms(2, i_nuclide) = atoms(2, i_nuclide) + &
|
||||
material_atom_density(i_material, k)**2 * var_f
|
||||
end do
|
||||
end do
|
||||
|
||||
! Determine volume
|
||||
|
|
|
|||
56
src/wmp.cpp
56
src/wmp.cpp
|
|
@ -1,13 +1,20 @@
|
|||
#include "openmc/wmp.h"
|
||||
|
||||
#include "openmc/constants.h"
|
||||
#include "openmc/cross_sections.h"
|
||||
#include "openmc/hdf5_interface.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/nuclide.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <sstream>
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//========================================================================
|
||||
// WindowedeMultipole implementation
|
||||
//========================================================================
|
||||
|
||||
WindowedMultipole::WindowedMultipole(hid_t group)
|
||||
{
|
||||
// Get name of nuclide from group, removing leading '/'
|
||||
|
|
@ -184,4 +191,53 @@ WindowedMultipole::evaluate_deriv(double E, double sqrtkT)
|
|||
return std::make_tuple(sig_s, sig_a, sig_f);
|
||||
}
|
||||
|
||||
//========================================================================
|
||||
// Non-member functions
|
||||
//========================================================================
|
||||
|
||||
void check_wmp_version(hid_t file)
|
||||
{
|
||||
if (attribute_exists(file, "version")) {
|
||||
std::array<int, 2> version;
|
||||
read_attribute(file, "version", version);
|
||||
if (version[0] != WMP_VERSION[0]) {
|
||||
std::stringstream msg;
|
||||
msg << "WMP data format uses version " << version[0] << "." <<
|
||||
version[1] << " whereas your installation of OpenMC expects version "
|
||||
<< WMP_VERSION[0] << ".x data.";
|
||||
fatal_error(msg);
|
||||
}
|
||||
} else {
|
||||
fatal_error("WMP data does not indicate a version. Your installation of "
|
||||
"OpenMC expects version " + std::to_string(WMP_VERSION[0]) + ".x data.");
|
||||
}
|
||||
}
|
||||
|
||||
void read_multipole_data(int i_nuclide)
|
||||
{
|
||||
// Look for WMP data in cross_sections.xml
|
||||
const auto& nuc {data::nuclides[i_nuclide]};
|
||||
auto it = data::library_map.find({Library::Type::wmp, nuc->name_});
|
||||
|
||||
// If no WMP library for this nuclide, just return
|
||||
if (it == data::library_map.end()) return;
|
||||
|
||||
// Check if WMP library exists
|
||||
int idx = it->second;
|
||||
std::string& filename = data::libraries[idx].path_;
|
||||
|
||||
// Display message
|
||||
write_message("Reading " + nuc->name_ + " WMP data from " + filename, 6);
|
||||
|
||||
// Open file and make sure version is sufficient
|
||||
hid_t file = file_open(filename, 'r');
|
||||
check_wmp_version(file);
|
||||
|
||||
// Read nuclide data from HDF5
|
||||
hid_t group = open_group(file, nuc->name_.c_str());
|
||||
nuc->multipole_ = std::make_unique<WindowedMultipole>(group);
|
||||
close_group(group);
|
||||
file_close(file);
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
141
src/xsdata.cpp
141
src/xsdata.cpp
|
|
@ -13,7 +13,9 @@
|
|||
#include "openmc/constants.h"
|
||||
#include "openmc/error.h"
|
||||
#include "openmc/math_functions.h"
|
||||
#include "openmc/mgxs_interface.h"
|
||||
#include "openmc/random_lcg.h"
|
||||
#include "openmc/settings.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
|
@ -22,10 +24,11 @@ namespace openmc {
|
|||
// XsData class methods
|
||||
//==============================================================================
|
||||
|
||||
XsData::XsData(size_t energy_groups, size_t num_delayed_groups, bool fissionable,
|
||||
int scatter_format, int n_pol, int n_azi)
|
||||
XsData::XsData(bool fissionable, int scatter_format, int n_pol, int n_azi)
|
||||
{
|
||||
size_t n_ang = n_pol * n_azi;
|
||||
size_t n_dg = data::num_delayed_groups;
|
||||
size_t n_g = data::num_energy_groups;
|
||||
|
||||
// check to make sure scatter format is OK before we allocate
|
||||
if (scatter_format != ANGLE_HISTOGRAM && scatter_format != ANGLE_TABULAR &&
|
||||
|
|
@ -33,7 +36,7 @@ XsData::XsData(size_t energy_groups, size_t num_delayed_groups, bool fissionable
|
|||
fatal_error("Invalid scatter_format!");
|
||||
}
|
||||
// allocate all [temperature][angle][in group] quantities
|
||||
std::vector<size_t> shape = {n_ang, energy_groups};
|
||||
std::vector<size_t> shape {n_ang, n_g};
|
||||
total = xt::zeros<double>(shape);
|
||||
absorption = xt::zeros<double>(shape);
|
||||
inverse_velocity = xt::zeros<double>(shape);
|
||||
|
|
@ -45,20 +48,20 @@ XsData::XsData(size_t energy_groups, size_t num_delayed_groups, bool fissionable
|
|||
}
|
||||
|
||||
// allocate decay_rate; [temperature][angle][delayed group]
|
||||
shape[1] = num_delayed_groups;
|
||||
shape[1] = n_dg;
|
||||
decay_rate = xt::zeros<double>(shape);
|
||||
|
||||
if (fissionable) {
|
||||
shape = {n_ang, num_delayed_groups, energy_groups};
|
||||
shape = {n_ang, n_dg, n_g};
|
||||
// allocate delayed_nu_fission; [temperature][angle][delay group][in group]
|
||||
delayed_nu_fission = xt::zeros<double>(shape);
|
||||
|
||||
// chi_prompt; [temperature][angle][in group][out group]
|
||||
shape = {n_ang, energy_groups, energy_groups};
|
||||
shape = {n_ang, n_g, n_g};
|
||||
chi_prompt = xt::zeros<double>(shape);
|
||||
|
||||
// chi_delayed; [temperature][angle][delay group][in group][out group]
|
||||
shape = {n_ang, num_delayed_groups, energy_groups, energy_groups};
|
||||
shape = {n_ang, n_dg, n_g, n_g};
|
||||
chi_delayed = xt::zeros<double>(shape);
|
||||
}
|
||||
|
||||
|
|
@ -78,18 +81,15 @@ XsData::XsData(size_t energy_groups, size_t num_delayed_groups, bool fissionable
|
|||
|
||||
void
|
||||
XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
|
||||
int final_scatter_format, int order_data, int max_order,
|
||||
int legendre_to_tabular_points, bool is_isotropic, int n_pol, int n_azi)
|
||||
int final_scatter_format, int order_data, bool is_isotropic, int n_pol, int n_azi)
|
||||
{
|
||||
// Reconstruct the dimension information so it doesn't need to be passed
|
||||
size_t n_ang = n_pol * n_azi;
|
||||
size_t energy_groups = total.shape()[1];
|
||||
size_t delayed_groups = decay_rate.shape()[1];
|
||||
|
||||
// Set the fissionable-specific data
|
||||
if (fissionable) {
|
||||
fission_from_hdf5(xsdata_grp, n_ang, energy_groups, delayed_groups,
|
||||
is_isotropic);
|
||||
fission_from_hdf5(xsdata_grp, n_ang, is_isotropic);
|
||||
}
|
||||
// Get the non-fission-specific data
|
||||
read_nd_vector(xsdata_grp, "decay_rate", decay_rate);
|
||||
|
|
@ -97,8 +97,8 @@ XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
|
|||
read_nd_vector(xsdata_grp, "inverse-velocity", inverse_velocity);
|
||||
|
||||
// Get scattering data
|
||||
scatter_from_hdf5(xsdata_grp, n_ang, energy_groups, scatter_format,
|
||||
final_scatter_format, order_data, max_order, legendre_to_tabular_points);
|
||||
scatter_from_hdf5(xsdata_grp, n_ang, scatter_format,
|
||||
final_scatter_format, order_data);
|
||||
|
||||
// Check absorption to ensure it is not 0 since it is often the
|
||||
// denominator in tally methods
|
||||
|
|
@ -123,12 +123,15 @@ XsData::from_hdf5(hid_t xsdata_grp, bool fissionable, int scatter_format,
|
|||
|
||||
void
|
||||
XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups, bool is_isotropic)
|
||||
bool is_isotropic)
|
||||
{
|
||||
// Data is provided as nu-fission and chi with a beta for delayed info
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
size_t n_dg = data::num_delayed_groups;
|
||||
|
||||
// Get chi
|
||||
xt::xtensor<double, 2> temp_chi({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_chi({n_ang, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "chi", temp_chi, true);
|
||||
|
||||
// Normalize chi by summing over the outgoing groups for each incoming angle
|
||||
|
|
@ -141,7 +144,7 @@ XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
xt::all());
|
||||
|
||||
// Get nu-fission
|
||||
xt::xtensor<double, 2> temp_nufiss({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_nufiss({n_ang, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "nu-fission", temp_nufiss, true);
|
||||
|
||||
// Get beta (strategy will depend upon the number of dimensions in beta)
|
||||
|
|
@ -151,7 +154,7 @@ XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
int ndim_target = 1;
|
||||
if (!is_isotropic) ndim_target += 2;
|
||||
if (beta_ndims == ndim_target) {
|
||||
xt::xtensor<double, 2> temp_beta({n_ang, delayed_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_beta({n_ang, n_dg}, 0.);
|
||||
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
|
||||
|
||||
// Set prompt_nu_fission = (1. - beta_total)*nu_fission
|
||||
|
|
@ -162,8 +165,7 @@ XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
xt::view(temp_beta, xt::all(), xt::all(), xt::newaxis()) *
|
||||
xt::view(temp_nufiss, xt::all(), xt::newaxis(), xt::all());
|
||||
} else if (beta_ndims == ndim_target + 1) {
|
||||
xt::xtensor<double, 3> temp_beta({n_ang, delayed_groups, energy_groups},
|
||||
0.);
|
||||
xt::xtensor<double, 3> temp_beta({n_ang, n_dg, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
|
||||
|
||||
// Set prompt_nu_fission = (1. - beta_total)*nu_fission
|
||||
|
|
@ -176,20 +178,22 @@ XsData::fission_vector_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
}
|
||||
|
||||
void
|
||||
XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups)
|
||||
XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang)
|
||||
{
|
||||
// Data is provided separately as prompt + delayed nu-fission and chi
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
size_t n_dg = data::num_delayed_groups;
|
||||
|
||||
// Get chi-prompt
|
||||
xt::xtensor<double, 2> temp_chi_p({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_chi_p({n_ang, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "chi-prompt", temp_chi_p, true);
|
||||
|
||||
// Normalize chi by summing over the outgoing groups for each incoming angle
|
||||
temp_chi_p /= xt::view(xt::sum(temp_chi_p, {1}), xt::all(), xt::newaxis());
|
||||
|
||||
// Get chi-delayed
|
||||
xt::xtensor<double, 3> temp_chi_d({n_ang, delayed_groups, energy_groups}, 0.);
|
||||
xt::xtensor<double, 3> temp_chi_d({n_ang, n_dg, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "chi-delayed", temp_chi_d, true);
|
||||
|
||||
// Normalize chi by summing over the outgoing groups for each incoming angle
|
||||
|
|
@ -209,14 +213,15 @@ XsData::fission_vector_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
}
|
||||
|
||||
void
|
||||
XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups)
|
||||
XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
|
||||
{
|
||||
// No beta is provided and there is no prompt/delay distinction.
|
||||
// Therefore, the code only considers the data as prompt.
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
|
||||
// Get chi
|
||||
xt::xtensor<double, 2> temp_chi({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_chi({n_ang, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "chi", temp_chi, true);
|
||||
|
||||
// Normalize chi by summing over the outgoing groups for each incoming angle
|
||||
|
|
@ -232,13 +237,15 @@ XsData::fission_vector_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups, bool is_isotropic)
|
||||
XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang, bool is_isotropic)
|
||||
{
|
||||
// Data is provided as nu-fission and chi with a beta for delayed info
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
size_t n_dg = data::num_delayed_groups;
|
||||
|
||||
// Get nu-fission matrix
|
||||
xt::xtensor<double, 3> temp_matrix({n_ang, energy_groups, energy_groups}, 0.);
|
||||
xt::xtensor<double, 3> temp_matrix({n_ang, n_g, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true);
|
||||
|
||||
// Get beta (strategy will depend upon the number of dimensions in beta)
|
||||
|
|
@ -248,7 +255,7 @@ XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
int ndim_target = 1;
|
||||
if (!is_isotropic) ndim_target += 2;
|
||||
if (beta_ndims == ndim_target) {
|
||||
xt::xtensor<double, 2> temp_beta({n_ang, delayed_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_beta({n_ang, n_dg}, 0.);
|
||||
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
|
||||
|
||||
xt::xtensor<double, 1> temp_beta_sum({n_ang}, 0.);
|
||||
|
|
@ -274,10 +281,10 @@ XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
xt::view(temp_matrix, xt::all(), xt::newaxis(), xt::all(), xt::all());
|
||||
|
||||
} else if (beta_ndims == ndim_target + 1) {
|
||||
xt::xtensor<double, 3> temp_beta({n_ang, delayed_groups, energy_groups}, 0.);
|
||||
xt::xtensor<double, 3> temp_beta({n_ang, n_dg, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "beta", temp_beta, true);
|
||||
|
||||
xt::xtensor<double, 2> temp_beta_sum({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<double, 2> temp_beta_sum({n_ang, n_g}, 0.);
|
||||
temp_beta_sum = xt::sum(temp_beta, {1});
|
||||
|
||||
// prompt_nu_fission is the sum of this matrix over outgoing groups and
|
||||
|
|
@ -308,13 +315,15 @@ XsData::fission_matrix_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
}
|
||||
|
||||
void
|
||||
XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups, size_t delayed_groups)
|
||||
XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang)
|
||||
{
|
||||
// Data is provided separately as prompt + delayed nu-fission and chi
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
size_t n_dg = data::num_delayed_groups;
|
||||
|
||||
// Get the prompt nu-fission matrix
|
||||
xt::xtensor<double, 3> temp_matrix_p({n_ang, energy_groups, energy_groups}, 0.);
|
||||
xt::xtensor<double, 3> temp_matrix_p({n_ang, n_g, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "prompt-nu-fission", temp_matrix_p, true);
|
||||
|
||||
// prompt_nu_fission is the sum over outgoing groups
|
||||
|
|
@ -326,8 +335,7 @@ XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
xt::view(prompt_nu_fission, xt::all(), xt::all(), xt::newaxis());
|
||||
|
||||
// Get the delayed nu-fission matrix
|
||||
xt::xtensor<double, 4> temp_matrix_d({n_ang, delayed_groups, energy_groups,
|
||||
energy_groups}, 0.);
|
||||
xt::xtensor<double, 4> temp_matrix_d({n_ang, n_dg, n_g, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "delayed-nu-fission", temp_matrix_d, true);
|
||||
|
||||
// delayed_nu_fission is the sum over outgoing groups
|
||||
|
|
@ -340,14 +348,15 @@ XsData::fission_matrix_no_beta_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
}
|
||||
|
||||
void
|
||||
XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
size_t energy_groups)
|
||||
XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang)
|
||||
{
|
||||
// No beta is provided and there is no prompt/delay distinction.
|
||||
// Therefore, the code only considers the data as prompt.
|
||||
|
||||
size_t n_g = data::num_energy_groups;
|
||||
|
||||
// Get nu-fission matrix
|
||||
xt::xtensor<double, 3> temp_matrix({n_ang, energy_groups, energy_groups}, 0.);
|
||||
xt::xtensor<double, 3> temp_matrix({n_ang, n_g, n_g}, 0.);
|
||||
read_nd_vector(xsdata_grp, "nu-fission", temp_matrix, true);
|
||||
|
||||
// prompt_nu_fission is the sum over outgoing groups
|
||||
|
|
@ -362,8 +371,7 @@ XsData::fission_matrix_no_delayed_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
XsData::fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
||||
size_t delayed_groups, bool is_isotropic)
|
||||
XsData::fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, bool is_isotropic)
|
||||
{
|
||||
// Get the fission and kappa_fission data xs; these are optional
|
||||
read_nd_vector(xsdata_grp, "fission", fission);
|
||||
|
|
@ -373,33 +381,29 @@ XsData::fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
// as a nu-fission matrix or a set of chi and nu-fission vectors
|
||||
if (object_exists(xsdata_grp, "chi") ||
|
||||
object_exists(xsdata_grp, "chi-prompt")) {
|
||||
if (delayed_groups == 0) {
|
||||
fission_vector_no_delayed_from_hdf5(xsdata_grp, n_ang, energy_groups);
|
||||
if (data::num_delayed_groups == 0) {
|
||||
fission_vector_no_delayed_from_hdf5(xsdata_grp, n_ang);
|
||||
} else {
|
||||
if (object_exists(xsdata_grp, "beta")) {
|
||||
fission_vector_beta_from_hdf5(xsdata_grp, n_ang, energy_groups,
|
||||
delayed_groups, is_isotropic);
|
||||
fission_vector_beta_from_hdf5(xsdata_grp, n_ang, is_isotropic);
|
||||
} else {
|
||||
fission_vector_no_beta_from_hdf5(xsdata_grp, n_ang, energy_groups,
|
||||
delayed_groups);
|
||||
fission_vector_no_beta_from_hdf5(xsdata_grp, n_ang);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (delayed_groups == 0) {
|
||||
fission_matrix_no_delayed_from_hdf5(xsdata_grp, n_ang, energy_groups);
|
||||
if (data::num_delayed_groups == 0) {
|
||||
fission_matrix_no_delayed_from_hdf5(xsdata_grp, n_ang);
|
||||
} else {
|
||||
if (object_exists(xsdata_grp, "beta")) {
|
||||
fission_matrix_beta_from_hdf5(xsdata_grp, n_ang, energy_groups,
|
||||
delayed_groups, is_isotropic);
|
||||
fission_matrix_beta_from_hdf5(xsdata_grp, n_ang, is_isotropic);
|
||||
} else {
|
||||
fission_matrix_no_beta_from_hdf5(xsdata_grp, n_ang, energy_groups,
|
||||
delayed_groups);
|
||||
fission_matrix_no_beta_from_hdf5(xsdata_grp, n_ang);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combine prompt_nu_fission and delayed_nu_fission into nu_fission
|
||||
if (delayed_groups == 0) {
|
||||
if (data::num_delayed_groups == 0) {
|
||||
nu_fission = prompt_nu_fission;
|
||||
} else {
|
||||
nu_fission = prompt_nu_fission + xt::sum(delayed_nu_fission, {1});
|
||||
|
|
@ -409,9 +413,8 @@ XsData::fission_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
//==============================================================================
|
||||
|
||||
void
|
||||
XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
||||
int scatter_format, int final_scatter_format, int order_data,
|
||||
int max_order, int legendre_to_tabular_points)
|
||||
XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang,
|
||||
int scatter_format, int final_scatter_format, int order_data)
|
||||
{
|
||||
if (!object_exists(xsdata_grp, "scatter_data")) {
|
||||
fatal_error("Must provide scatter_data group!");
|
||||
|
|
@ -419,9 +422,10 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
hid_t scatt_grp = open_group(xsdata_grp, "scatter_data");
|
||||
|
||||
// Get the outgoing group boundary indices
|
||||
xt::xtensor<int, 2> gmin({n_ang, energy_groups}, 0.);
|
||||
size_t n_g = data::num_energy_groups;
|
||||
xt::xtensor<int, 2> gmin({n_ang, n_g}, 0.);
|
||||
read_nd_vector(scatt_grp, "g_min", gmin, true);
|
||||
xt::xtensor<int, 2> gmax({n_ang, energy_groups}, 0.);
|
||||
xt::xtensor<int, 2> gmax({n_ang, n_g}, 0.);
|
||||
read_nd_vector(scatt_grp, "g_max", gmax, true);
|
||||
|
||||
// Make gmin and gmax start from 0 vice 1 as they do in the library
|
||||
|
|
@ -432,7 +436,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
// data.
|
||||
size_t length = order_data * xt::sum(gmax - gmin + 1)();
|
||||
|
||||
double_4dvec input_scatt(n_ang, double_3dvec(energy_groups));
|
||||
double_4dvec input_scatt(n_ang, double_3dvec(n_g));
|
||||
xt::xtensor<double, 1> temp_arr({length}, 0.);
|
||||
read_nd_vector(scatt_grp, "scatter_matrix", temp_arr, true);
|
||||
|
||||
|
|
@ -440,7 +444,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
// strip off the superfluous orders if needed
|
||||
int order_dim;
|
||||
if (scatter_format == ANGLE_LEGENDRE) {
|
||||
order_dim = std::min(order_data - 1, max_order) + 1;
|
||||
order_dim = std::min(order_data - 1, settings::max_order) + 1;
|
||||
} else {
|
||||
order_dim = order_data;
|
||||
}
|
||||
|
|
@ -449,7 +453,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
// scatt data
|
||||
size_t temp_idx = 0;
|
||||
for (size_t a = 0; a < n_ang; a++) {
|
||||
for (size_t gin = 0; gin < energy_groups; gin++) {
|
||||
for (size_t gin = 0; gin < n_g; gin++) {
|
||||
input_scatt[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1);
|
||||
for (size_t i_gout = 0; i_gout < input_scatt[a][gin].size(); i_gout++) {
|
||||
input_scatt[a][gin][i_gout].resize(order_dim);
|
||||
|
|
@ -463,7 +467,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
}
|
||||
|
||||
// Get multiplication matrix
|
||||
double_3dvec temp_mult(n_ang, double_2dvec(energy_groups));
|
||||
double_3dvec temp_mult(n_ang, double_2dvec(n_g));
|
||||
if (object_exists(scatt_grp, "multiplicity_matrix")) {
|
||||
temp_arr.resize({length / order_data});
|
||||
read_nd_vector(scatt_grp, "multiplicity_matrix", temp_arr);
|
||||
|
|
@ -471,7 +475,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
// convert the flat temp_arr to a jagged array for passing to scatt data
|
||||
size_t temp_idx = 0;
|
||||
for (size_t a = 0; a < n_ang; a++) {
|
||||
for (size_t gin = 0; gin < energy_groups; gin++) {
|
||||
for (size_t gin = 0; gin < n_g; gin++) {
|
||||
temp_mult[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1);
|
||||
for (size_t i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) {
|
||||
temp_mult[a][gin][i_gout] = temp_arr[temp_idx++];
|
||||
|
|
@ -481,7 +485,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
} else {
|
||||
// Use a default: multiplicities are 1.0.
|
||||
for (size_t a = 0; a < n_ang; a++) {
|
||||
for (size_t gin = 0; gin < energy_groups; gin++) {
|
||||
for (size_t gin = 0; gin < n_g; gin++) {
|
||||
temp_mult[a][gin].resize(gmax(a, gin) - gmin(a, gin) + 1);
|
||||
for (size_t i_gout = 0; i_gout < temp_mult[a][gin].size(); i_gout++) {
|
||||
temp_mult[a][gin][i_gout] = 1.;
|
||||
|
|
@ -504,8 +508,7 @@ XsData::scatter_from_hdf5(hid_t xsdata_grp, size_t n_ang, size_t energy_groups,
|
|||
|
||||
// Now create a tabular version of legendre_scatt
|
||||
convert_legendre_to_tabular(legendre_scatt,
|
||||
*static_cast<ScattDataTabular*>(scatter[a].get()),
|
||||
legendre_to_tabular_points);
|
||||
*static_cast<ScattDataTabular*>(scatter[a].get()));
|
||||
|
||||
scatter_format = final_scatter_format;
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,5 +1,5 @@
|
|||
k-combined:
|
||||
1.115067E+00 5.423808E-02
|
||||
1.028803E+00 3.340602E-02
|
||||
tally 1:
|
||||
8.543144E+00
|
||||
1.530584E+01
|
||||
8.346847E+00
|
||||
1.453407E+01
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ def test_dagmc():
|
|||
water = openmc.Material()
|
||||
water.add_nuclide('H1', 2.0, 'ao')
|
||||
water.add_nuclide('O16', 1.0, 'ao')
|
||||
water.add_s_alpha_beta('c_H_in_H2O')
|
||||
water.set_density('g/cc', 1.0)
|
||||
water.add_s_alpha_beta('c_H_in_H2O')
|
||||
water.id = 41
|
||||
|
||||
mats = openmc.Materials([u235, water])
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
k-combined:
|
||||
8.447580E-01 1.806149E-02
|
||||
8.403447E-01 2.461538E-02
|
||||
|
|
|
|||
0
tests/regression_tests/uwuw/__init__.py
Normal file
0
tests/regression_tests/uwuw/__init__.py
Normal file
BIN
tests/regression_tests/uwuw/dagmc.h5m
Normal file
BIN
tests/regression_tests/uwuw/dagmc.h5m
Normal file
Binary file not shown.
23
tests/regression_tests/uwuw/inputs_true.dat
Normal file
23
tests/regression_tests/uwuw/inputs_true.dat
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<settings>
|
||||
<run_mode>eigenvalue</run_mode>
|
||||
<particles>100</particles>
|
||||
<batches>5</batches>
|
||||
<inactive>0</inactive>
|
||||
<source strength="1.0">
|
||||
<space type="box">
|
||||
<parameters>-4 -4 -4 4 4 4</parameters>
|
||||
</space>
|
||||
</source>
|
||||
<dagmc>true</dagmc>
|
||||
</settings>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<tallies>
|
||||
<filter id="1" type="cell">
|
||||
<bins>1</bins>
|
||||
</filter>
|
||||
<tally id="1">
|
||||
<filters>1</filters>
|
||||
<scores>total</scores>
|
||||
</tally>
|
||||
</tallies>
|
||||
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