mirror of
https://github.com/openmc-dev/openmc.git
synced 2026-07-27 21:55:41 -04:00
Merge branch 'develop' into photon-new
This commit is contained in:
commit
c912cdce70
264 changed files with 18402 additions and 17147 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
|
@ -97,4 +97,10 @@ examples/jupyter/plots
|
|||
.tox/
|
||||
.python-version
|
||||
.coverage
|
||||
htmlcov
|
||||
htmlcov
|
||||
|
||||
#macOS
|
||||
*.DS_Store
|
||||
|
||||
#Dynamic Library
|
||||
*.dylib
|
||||
|
|
|
|||
|
|
@ -5,14 +5,10 @@ project(openmc Fortran C CXX)
|
|||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_Fortran_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/include)
|
||||
|
||||
# Set module path
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules)
|
||||
|
||||
# Make sure Fortran module directory is included when building
|
||||
include_directories(${CMAKE_BINARY_DIR}/include)
|
||||
|
||||
#===============================================================================
|
||||
# Architecture specific definitions
|
||||
#===============================================================================
|
||||
|
|
@ -73,7 +69,7 @@ if(NOT DEFINED HDF5_PREFER_PARALLEL)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
find_package(HDF5 COMPONENTS Fortran_HL)
|
||||
find_package(HDF5 COMPONENTS HL)
|
||||
if(NOT HDF5_FOUND)
|
||||
message(FATAL_ERROR "Could not find HDF5")
|
||||
endif()
|
||||
|
|
@ -368,7 +364,6 @@ set(LIBOPENMC_FORTRAN_SRC
|
|||
src/message_passing.F90
|
||||
src/mgxs_data.F90
|
||||
src/mgxs_header.F90
|
||||
src/multipole.F90
|
||||
src/multipole_header.F90
|
||||
src/nuclide_header.F90
|
||||
src/output.F90
|
||||
|
|
@ -423,30 +418,43 @@ set(LIBOPENMC_FORTRAN_SRC
|
|||
src/tallies/tally_filter_distribcell.F90
|
||||
src/tallies/tally_filter_energy.F90
|
||||
src/tallies/tally_filter_energyfunc.F90
|
||||
src/tallies/tally_filter_legendre.F90
|
||||
src/tallies/tally_filter_material.F90
|
||||
src/tallies/tally_filter_mesh.F90
|
||||
src/tallies/tally_filter_meshsurface.F90
|
||||
src/tallies/tally_filter_mu.F90
|
||||
src/tallies/tally_filter_particle.F90
|
||||
src/tallies/tally_filter_polar.F90
|
||||
src/tallies/tally_filter_sph_harm.F90
|
||||
src/tallies/tally_filter_sptl_legendre.F90
|
||||
src/tallies/tally_filter_surface.F90
|
||||
src/tallies/tally_filter_universe.F90
|
||||
src/tallies/tally_filter_zernike.F90
|
||||
src/tallies/tally_header.F90
|
||||
src/tallies/trigger.F90
|
||||
src/tallies/trigger_header.F90
|
||||
)
|
||||
set(LIBOPENMC_CXX_SRC
|
||||
src/error.h
|
||||
src/hdf5_interface.h
|
||||
src/cell.cpp
|
||||
src/initialize.cpp
|
||||
src/finalize.cpp
|
||||
src/geometry_aux.cpp
|
||||
src/hdf5_interface.cpp
|
||||
src/lattice.cpp
|
||||
src/math_functions.cpp
|
||||
src/message_passing.cpp
|
||||
src/plot.cpp
|
||||
src/random_lcg.cpp
|
||||
src/random_lcg.h
|
||||
src/simulation.cpp
|
||||
src/state_point.cpp
|
||||
src/surface.cpp
|
||||
src/surface.h
|
||||
src/xml_interface.h
|
||||
src/pugixml/pugixml.cpp
|
||||
src/pugixml/pugixml.hpp)
|
||||
src/xml_interface.cpp
|
||||
src/pugixml/pugixml.cpp)
|
||||
add_library(libopenmc SHARED ${LIBOPENMC_FORTRAN_SRC} ${LIBOPENMC_CXX_SRC})
|
||||
set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc)
|
||||
add_executable(${program} src/main.F90)
|
||||
set_target_properties(libopenmc PROPERTIES
|
||||
OUTPUT_NAME openmc
|
||||
PUBLIC_HEADER include/openmc.h)
|
||||
add_executable(${program} src/main.cpp)
|
||||
|
||||
#===============================================================================
|
||||
# Add compiler/linker flags
|
||||
|
|
@ -455,11 +463,11 @@ add_executable(${program} src/main.F90)
|
|||
set_property(TARGET ${program} libopenmc pugixml_fortran
|
||||
PROPERTY LINKER_LANGUAGE Fortran)
|
||||
|
||||
target_include_directories(libopenmc PUBLIC ${HDF5_INCLUDE_DIRS})
|
||||
target_include_directories(libopenmc PUBLIC include ${HDF5_INCLUDE_DIRS})
|
||||
|
||||
# The executable and the faddeeva package use only one language. They can be
|
||||
# set via target_compile_options which accepts a list.
|
||||
target_compile_options(${program} PUBLIC ${f90flags})
|
||||
target_compile_options(${program} PUBLIC ${cxxflags})
|
||||
target_compile_options(faddeeva PRIVATE ${cflags})
|
||||
|
||||
# The libopenmc library has both F90 and C++ so the compile flags must be set
|
||||
|
|
@ -500,7 +508,9 @@ add_custom_command(TARGET libopenmc POST_BUILD
|
|||
install(TARGETS ${program} libopenmc
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib)
|
||||
ARCHIVE DESTINATION lib
|
||||
PUBLIC_HEADER DESTINATION include
|
||||
)
|
||||
install(DIRECTORY src/relaxng DESTINATION share/openmc)
|
||||
install(FILES man/man1/openmc.1 DESTINATION share/man/man1)
|
||||
install(FILES LICENSE DESTINATION "share/doc/${program}" RENAME copyright)
|
||||
|
|
|
|||
|
|
@ -46,10 +46,13 @@ Type Definitions
|
|||
Functions
|
||||
---------
|
||||
|
||||
.. c:function:: void openmc_calculate_volumes()
|
||||
.. c:function:: int openmc_calculate_volumes()
|
||||
|
||||
Run a stochastic volume calculation
|
||||
|
||||
:return: Return status (negative if an error occurred)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n)
|
||||
|
||||
Get the fill for a cell
|
||||
|
|
@ -192,11 +195,14 @@ Functions
|
|||
:return: Return status (negative if an error occurred)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: void openmc_finalize()
|
||||
.. c:function:: int openmc_finalize()
|
||||
|
||||
Finalize a simulation
|
||||
|
||||
.. c:function:: void openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance)
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance)
|
||||
|
||||
Determine the ID of the cell/material containing a given point
|
||||
|
||||
|
|
@ -207,6 +213,8 @@ Functions
|
|||
occurs, the ID is -1.
|
||||
:param int32_t* instance: If a cell is repeated in the geometry, the instance
|
||||
of the cell that was found and zero otherwise.
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_get_cell_index(int32_t id, int32_t* index)
|
||||
|
||||
|
|
@ -247,11 +255,12 @@ Functions
|
|||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_get_nuclide_index(char name[], int* index)
|
||||
.. c:function:: int openmc_get_nuclide_index(const char name[], int* index)
|
||||
|
||||
Get the index in the nuclides array for a nuclide with a given name
|
||||
|
||||
:param char[] name: Name of the nuclide
|
||||
:param name: Name of the nuclide
|
||||
:type name: const char[]
|
||||
:param int* index: Index in the nuclides array
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
|
@ -265,17 +274,24 @@ Functions
|
|||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: void openmc_hard_reset()
|
||||
.. c:function:: int openmc_hard_reset()
|
||||
|
||||
Reset tallies, timers, and pseudo-random number generator state
|
||||
|
||||
.. c:function:: void openmc_init(const int* intracomm)
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_init(int argc, char** argv, const void* intracomm)
|
||||
|
||||
Initialize OpenMC
|
||||
|
||||
:param int argc: Number of command-line arguments (including command)
|
||||
:param char** argv: Command-line arguments
|
||||
:param intracomm: MPI intracommunicator. If MPI is not being used, a null
|
||||
pointer should be passed.
|
||||
:type intracomm: const int*
|
||||
:type intracomm: const void*
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_load_nuclide(char name[])
|
||||
|
||||
|
|
@ -393,26 +409,41 @@ Functions
|
|||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: void openmc_plot_geometry()
|
||||
.. c:function:: int openmc_plot_geometry()
|
||||
|
||||
Run plotting mode.
|
||||
|
||||
.. c:function:: void openmc_reset()
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_reset()
|
||||
|
||||
Resets all tally scores
|
||||
|
||||
.. c:function:: void openmc_run()
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_run()
|
||||
|
||||
Run a simulation
|
||||
|
||||
.. c:function:: void openmc_simulation_finalize()
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_simulation_finalize()
|
||||
|
||||
Finalize a simulation.
|
||||
|
||||
.. c:function:: void openmc_simulation_init()
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_simulation_init()
|
||||
|
||||
Initialize a simulation. Must be called after openmc_init().
|
||||
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_source_bank(struct Bank** ptr, int64_t* n)
|
||||
|
||||
Return a pointer to the source bank array.
|
||||
|
|
@ -432,13 +463,15 @@ Functions
|
|||
:return: Return status (negative if an error occurred)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: void openmc_statepoint_write(const char filename[])
|
||||
.. c:function:: int openmc_statepoint_write(const char filename[])
|
||||
|
||||
Write a statepoint file
|
||||
|
||||
:param filename: Name of file to create. If a null pointer is passed, a
|
||||
filename is assigned automatically.
|
||||
:type filename: const char[]
|
||||
:return: Return status (negative if an error occurs)
|
||||
:rtype: int
|
||||
|
||||
.. c:function:: int openmc_tally_get_id(int32_t index, int32_t* id)
|
||||
|
||||
|
|
|
|||
|
|
@ -219,8 +219,8 @@ Curly braces
|
|||
|
||||
For a function definition, the opening and closing braces should each be on
|
||||
their own lines. This helps distinguish function code from the argument list.
|
||||
If the entire function fits on one line, then the braces can be on the same
|
||||
line. e.g.:
|
||||
If the entire function fits on one or two lines, then the braces can be on the
|
||||
same line. e.g.:
|
||||
|
||||
.. code-block:: C++
|
||||
|
||||
|
|
@ -238,6 +238,9 @@ line. e.g.:
|
|||
|
||||
int return_one() {return 1;}
|
||||
|
||||
int return_one()
|
||||
{return 1;}
|
||||
|
||||
For a conditional, the opening brace should be on the same line as the end of
|
||||
the conditional statement. If there is a following ``else if`` or ``else``
|
||||
statement, the closing brace should be on the same line as that following
|
||||
|
|
|
|||
13
docs/source/examples/expansion-filters.rst
Normal file
13
docs/source/examples/expansion-filters.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.. _notebook_expansion:
|
||||
|
||||
=====================
|
||||
Functional Expansions
|
||||
=====================
|
||||
|
||||
.. only:: html
|
||||
|
||||
.. notebook:: ../../../examples/jupyter/expansion-filters.ipynb
|
||||
|
||||
.. only:: latex
|
||||
|
||||
IPython notebooks must be viewed in the online HTML documentation.
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
.. _examples:
|
||||
|
||||
=================
|
||||
Example Notebooks
|
||||
=================
|
||||
========
|
||||
Examples
|
||||
========
|
||||
|
||||
The following series of Jupyter_ Notebooks provide examples for usage of OpenMC
|
||||
features via the :ref:`pythonapi`.
|
||||
|
||||
.. _Jupyter: https://jupyter.org/
|
||||
The following series of `Jupyter <https://jupyter.org/>`_ Notebooks provide
|
||||
examples for how to use various features of OpenMC by leveraging the
|
||||
:ref:`pythonapi`.
|
||||
|
||||
-----------
|
||||
Basic Usage
|
||||
|
|
@ -20,6 +19,7 @@ Basic Usage
|
|||
post-processing
|
||||
pandas-dataframes
|
||||
tally-arithmetic
|
||||
expansion-filters
|
||||
search
|
||||
triso
|
||||
candu
|
||||
|
|
|
|||
|
|
@ -28,12 +28,6 @@ Windowed Multipole Library Format
|
|||
":math:`r`" and ":math:`i`" identifiers, similar to how `h5py`_ does it.
|
||||
- **end_E** (*double*)
|
||||
Highest energy the windowed multipole part of the library is valid for.
|
||||
- **energy_points** (*double[]*)
|
||||
Energy grid for the pointwise library in the reaction group.
|
||||
- **fissionable** (*int*)
|
||||
1 if this nuclide has fission data. 0 if it does not.
|
||||
- **fit_order** (*int*)
|
||||
The order of the curve fit.
|
||||
- **formalism** (*int*)
|
||||
The formalism of the underlying data. Uses the `ENDF-6`_ format
|
||||
formalism numbers.
|
||||
|
|
@ -51,18 +45,6 @@ Windowed Multipole Library Format
|
|||
- **l_value** (*int[]*)
|
||||
The index for a corresponding pole. Equivalent to the :math:`l` quantum
|
||||
number of the resonance the pole comes from :math:`+1`.
|
||||
- **length** (*int*)
|
||||
Total count of poles in `data`.
|
||||
- **max_w** (*int*)
|
||||
Maximum number of poles in a window.
|
||||
- **MT_count** (*int*)
|
||||
Number of pointwise tables in the library.
|
||||
- **MT_list** (*int[]*)
|
||||
A list of available MT identifiers. See `ENDF-6`_ for meaning.
|
||||
- **n_grid** (*int*)
|
||||
Total length of the pointwise data.
|
||||
- **num_l** (*int*)
|
||||
Number of possible :math:`l` quantum states for this nuclide.
|
||||
- **pseudo_K0RS** (*double[]*)
|
||||
:math:`l` dependent value of
|
||||
|
||||
|
|
@ -90,13 +72,6 @@ Windowed Multipole Library Format
|
|||
The pole to start from for each window.
|
||||
- **w_end** (*int[]*)
|
||||
The pole to end at for each window.
|
||||
- **windows** (*int*)
|
||||
Number of windows.
|
||||
|
||||
**/nuclide/reactions/MT<i>**
|
||||
- **MT_sigma** (*double[]*) -- Cross section value for this reaction.
|
||||
- **Q_value** (*double*) -- Energy released in this reaction, in eV.
|
||||
- **threshold** (*int*) -- The first non-zero entry in ``MT_sigma``.
|
||||
|
||||
.. _h5py: http://docs.h5py.org/en/latest/
|
||||
.. _ENDF-6: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf
|
||||
|
|
|
|||
|
|
@ -93,29 +93,13 @@ or ``multi-group``.
|
|||
|
||||
*Default*: continuous-energy
|
||||
|
||||
---------------------
|
||||
``<entropy>`` Element
|
||||
---------------------
|
||||
--------------------------
|
||||
``<entropy_mesh>`` Element
|
||||
--------------------------
|
||||
|
||||
The ``<entropy>`` element describes a mesh that is used for calculating Shannon
|
||||
entropy. This mesh should cover all possible fissionable materials in the
|
||||
problem. It has the following attributes/sub-elements:
|
||||
|
||||
:dimension:
|
||||
The number of mesh cells in the x, y, and z directions, respectively.
|
||||
|
||||
*Default*: If this tag is not present, the number of mesh cells is
|
||||
automatically determined by the code.
|
||||
|
||||
:lower_left:
|
||||
The Cartesian coordinates of the lower-left corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:upper_right:
|
||||
The Cartesian coordinates of the upper-right corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
The ``<entropy_mesh>`` element indicates the ID of a mesh that is to be used for
|
||||
calculating Shannon entropy. The mesh should cover all possible fissionable
|
||||
materials in the problem and is specified using a :ref:`mesh_element`.
|
||||
|
||||
-----------------------------------
|
||||
``<generations_per_batch>`` Element
|
||||
|
|
@ -199,6 +183,36 @@ then, OpenMC will only use up to the :math:`P_1` data.
|
|||
.. note:: This element is not used in the continuous-energy
|
||||
:ref:`energy_mode`.
|
||||
|
||||
.. _mesh_element:
|
||||
|
||||
------------------
|
||||
``<mesh>`` Element
|
||||
------------------
|
||||
|
||||
The ``<mesh>`` element describes a mesh that is used either for calculating
|
||||
Shannon entropy, applying the uniform fission site method, or in tallies. For
|
||||
Shannon entropy meshes, the mesh should cover all possible fissionable materials
|
||||
in the problem. It has the following attributes/sub-elements:
|
||||
|
||||
:id:
|
||||
A unique integer that is used to identify the mesh.
|
||||
|
||||
:dimension:
|
||||
The number of mesh cells in the x, y, and z directions, respectively.
|
||||
|
||||
*Default*: If this tag is not present, the number of mesh cells is
|
||||
automatically determined by the code.
|
||||
|
||||
:lower_left:
|
||||
The Cartesian coordinates of the lower-left corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:upper_right:
|
||||
The Cartesian coordinates of the upper-right corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
|
||||
-----------------------
|
||||
``<no_reduce>`` Element
|
||||
-----------------------
|
||||
|
|
@ -765,30 +779,15 @@ has the following attributes/sub-elements:
|
|||
|
||||
|
||||
------------------------
|
||||
``<uniform_fs>`` Element
|
||||
``<ufs_mesh>`` Element
|
||||
------------------------
|
||||
|
||||
The ``<uniform_fs>`` element describes a mesh that is used for re-weighting
|
||||
source sites at every generation based on the uniform fission site methodology
|
||||
described in Kelly et al., "MC21 Analysis of the Nuclear Energy Agency Monte
|
||||
Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*, Knoxville,
|
||||
TN (2012). This mesh should cover all possible fissionable materials in the
|
||||
problem. It has the following attributes/sub-elements:
|
||||
|
||||
:dimension:
|
||||
The number of mesh cells in the x, y, and z directions, respectively.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:lower_left:
|
||||
The Cartesian coordinates of the lower-left corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
|
||||
:upper_right:
|
||||
The Cartesian coordinates of the upper-right corner of the mesh.
|
||||
|
||||
*Default*: None
|
||||
The ``<ufs_mesh>`` element indicates the ID of a mesh that is used for
|
||||
re-weighting source sites at every generation based on the uniform fission site
|
||||
methodology described in Kelly et al., "MC21 Analysis of the Nuclear Energy
|
||||
Agency Monte Carlo Performance Benchmark Problem," Proceedings of *Physor 2012*,
|
||||
Knoxville, TN (2012). The mesh should cover all possible fissionable materials
|
||||
in the problem and is specified using a :ref:`mesh_element`.
|
||||
|
||||
.. _verbosity:
|
||||
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ Normally, source data is stored in a state point file. However, it is possible
|
|||
to request that the source be written separately, in which case the format used
|
||||
is that documented here.
|
||||
|
||||
**/filetype** (*char[]*)
|
||||
**/**
|
||||
|
||||
String indicating the type of file.
|
||||
:Attributes: - **filetype** (*char[]*) -- String indicating the type of file.
|
||||
|
||||
**/source_bank** (Compound type)
|
||||
|
||||
Source bank information for each particle. The compound type has fields
|
||||
``wgt``, ``xyz``, ``uvw``, ``E``, and ``delayed_group``, which
|
||||
represent the weight, position, direction, energy, energy group, and
|
||||
delayed_group of the source particle, respectively.
|
||||
:Datasets:
|
||||
- **source_bank** (Compound type) -- Source bank information for each
|
||||
particle. The compound type has fields ``wgt``, ``xyz``, ``uvw``,
|
||||
``E``, and ``delayed_group``, which represent the weight, position,
|
||||
direction, energy, energy group, and delayed_group of the source
|
||||
particle, respectively.
|
||||
|
|
|
|||
|
|
@ -133,15 +133,8 @@ The current version of the statepoint file format is 17.0.
|
|||
- **derivative** (*int*) -- ID of the derivative applied to the
|
||||
tally.
|
||||
- **n_score_bins** (*int*) -- Number of scoring bins for a single
|
||||
nuclide. In general, this can be greater than the number of
|
||||
user-specified scores since each score might have multiple scoring
|
||||
bins, e.g., scatter-PN.
|
||||
nuclide.
|
||||
- **score_bins** (*char[][]*) -- Values of specified scores.
|
||||
- **n_user_scores** (*int*) -- Number of scores without accounting
|
||||
for those added by expansions, e.g. scatter-PN.
|
||||
- **moment_orders** (*char[][]*) -- Tallying moment orders for
|
||||
Legendre and spherical harmonic tally expansions (e.g., 'P2',
|
||||
'Y1,2', etc.).
|
||||
- **results** (*double[][][2]*) -- Accumulated sum and sum-of-squares
|
||||
for each bin of the i-th tally. The first dimension represents
|
||||
combinations of filter bins, the second dimensions represents
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
Summary File Format
|
||||
===================
|
||||
|
||||
The current version of the summary file format is 5.0.
|
||||
The current version of the summary file format is 6.0.
|
||||
|
||||
**/**
|
||||
|
||||
|
|
@ -104,8 +104,13 @@ The current version of the summary file format is 5.0.
|
|||
- **atom_density** (*double[]*) -- Total atom density of the material
|
||||
in atom/b-cm.
|
||||
- **nuclides** (*char[][]*) -- Array of nuclides present in the
|
||||
material, e.g., 'U235'.
|
||||
material, e.g., 'U235'. This data set is only present if nuclides
|
||||
are used.
|
||||
- **nuclide_densities** (*double[]*) -- Atom density of each nuclide.
|
||||
This data set is only present if 'nuclides' data set is present.
|
||||
- **macroscopics** (*char[][]*) -- Array of macroscopic data sets
|
||||
present in the material. This dataset is only present if
|
||||
macroscopic data sets are used in multi-group mode.
|
||||
- **sab_names** (*char[][]*) -- Names of
|
||||
S(:math:`\alpha,\beta`) tables assigned to the material.
|
||||
|
||||
|
|
@ -116,6 +121,13 @@ The current version of the summary file format is 5.0.
|
|||
:Datasets: - **names** (*char[][]*) -- Names of nuclides.
|
||||
- **awrs** (*float[]*) -- Atomic weight ratio of each nuclide.
|
||||
|
||||
**/macroscopics/**
|
||||
|
||||
:Attributes: - **n_macroscopics** (*int*) -- Number of macroscopic data sets
|
||||
in the problem.
|
||||
|
||||
:Datasets: - **names** (*char[][]*) -- Names of the macroscopic data sets.
|
||||
|
||||
**/tallies/tally <uid>/**
|
||||
|
||||
:Datasets: - **name** (*char[]*) -- Name of the tally.
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ Benchmarking
|
|||
Coupling and Multi-physics
|
||||
--------------------------
|
||||
|
||||
- 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**,
|
||||
264-274 (2017).
|
||||
|
||||
- Tianliang Hu, Liangzhu Cao, Hongchun Wu, Xianan Du, and Mingtao He, "`Coupled
|
||||
neutrons and thermal-hydraulics simulation of molten salt reactors based on
|
||||
OpenMC/TANSY <https://doi.org/10.1016/j.anucene.2017.05.002>`_,"
|
||||
|
|
@ -98,6 +103,11 @@ Coupling and Multi-physics
|
|||
Geometry and Visualization
|
||||
--------------------------
|
||||
|
||||
- Jin-Yang Li, Long Gu, Hu-Shan Xu, Nadezha Korepanova, Rui Yu, Yan-Lei Zhu, and
|
||||
Chang-Ping Qin, "`CAD modeling study on FLUKA and OpenMC for accelerator
|
||||
driven system simulation <https://doi.org/10.1016/j.anucene.2017.12.050>`_",
|
||||
*Ann. Nucl. Energy*, **114**, 329-341 (2018).
|
||||
|
||||
- Logan Abel, William Boyd, Benoit Forget, and Kord Smith, "Interactive
|
||||
Visualization of Multi-Group Cross Sections on High-Fidelity Spatial Meshes,"
|
||||
*Trans. Am. Nucl. Soc.*, **114**, 391-394 (2016).
|
||||
|
|
@ -114,6 +124,11 @@ Geometry and Visualization
|
|||
Miscellaneous
|
||||
-------------
|
||||
|
||||
- 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**,
|
||||
e0192020 (2018).
|
||||
|
||||
- Adam G. Nelson, Samuel Shaner, William Boyd, and Paul K. Romano,
|
||||
"Incorporation of a Multigroup Transport Capability in the OpenMC Monte Carlo
|
||||
Particle Transport Code," *Trans. Am. Nucl. Soc.*, **117**, 679-681 (2017).
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ Constructing Tallies
|
|||
openmc.CellbornFilter
|
||||
openmc.SurfaceFilter
|
||||
openmc.MeshFilter
|
||||
openmc.MeshSurfaceFilter
|
||||
openmc.EnergyFilter
|
||||
openmc.EnergyoutFilter
|
||||
openmc.MuFilter
|
||||
|
|
@ -117,6 +118,10 @@ Constructing Tallies
|
|||
openmc.DistribcellFilter
|
||||
openmc.DelayedGroupFilter
|
||||
openmc.EnergyFunctionFilter
|
||||
openmc.LegendreFilter
|
||||
openmc.SpatialLegendreFilter
|
||||
openmc.SphericalHarmonicsFilter
|
||||
openmc.ZernikeFilter
|
||||
openmc.Mesh
|
||||
openmc.Trigger
|
||||
openmc.TallyDerivative
|
||||
|
|
|
|||
|
|
@ -44,5 +44,8 @@ Classes
|
|||
EnergyFilter
|
||||
MaterialFilter
|
||||
Material
|
||||
Mesh
|
||||
MeshFilter
|
||||
MeshSurfaceFilter
|
||||
Nuclide
|
||||
Tally
|
||||
|
|
|
|||
|
|
@ -141,16 +141,10 @@ Prerequisites
|
|||
recommend that your HDF5 installation be built with parallel I/O
|
||||
features. An example of configuring HDF5_ is listed below::
|
||||
|
||||
FC=mpifort ./configure --enable-fortran --enable-parallel
|
||||
FC=mpifort ./configure --enable-parallel
|
||||
|
||||
You may omit ``--enable-parallel`` if you want to compile HDF5_ in serial.
|
||||
|
||||
.. important::
|
||||
|
||||
If you are building HDF5 version 1.8.x or earlier, you must include
|
||||
``--enable-fortran2003`` when configuring HDF5 or else OpenMC will not
|
||||
be able to compile.
|
||||
|
||||
.. admonition:: Optional
|
||||
:class: note
|
||||
|
||||
|
|
@ -416,7 +410,9 @@ Prerequisites
|
|||
The Python API works with Python 3.4+. In addition to Python itself, the API
|
||||
relies on a number of third-party packages. All prerequisites can be installed
|
||||
using Conda_ (recommended), pip_, or through the package manager in most Linux
|
||||
distributions.
|
||||
distributions. To run simulations in parallel using MPI, it is recommended to
|
||||
build mpi4py, HDF5, h5py from source, in that order, using the same compilers
|
||||
as for OpenMC.
|
||||
|
||||
.. admonition:: Required
|
||||
:class: error
|
||||
|
|
|
|||
|
|
@ -43,14 +43,22 @@ of an element, you specify the element itself. For example,
|
|||
Internally, OpenMC stores data on the atomic masses and natural abundances of
|
||||
all known isotopes and then uses this data to determine what isotopes should be
|
||||
added to the material. When the material is later exported to XML for use by the
|
||||
:ref:`scripts_openmc` executable, you'll see that any natural elements are
|
||||
:ref:`scripts_openmc` executable, you'll see that any natural elements were
|
||||
expanded to the naturally-occurring isotopes.
|
||||
|
||||
The :meth:`Material.add_element` method can also be used to add uranium at a
|
||||
specified enrichment through the `enrichment` argument. For example, the
|
||||
following would add 3.2% enriched uranium to a material::
|
||||
|
||||
mat.add_element('U', 1.0, enrichment=3.2)
|
||||
|
||||
In addition to U235 and U238, concentrations of U234 and U236 will be present
|
||||
and are determined through a correlation based on measured data.
|
||||
|
||||
Often, cross section libraries don't actually have all naturally-occurring
|
||||
isotopes for a given element. For example, in ENDF/B-VII.1, cross section
|
||||
evaluations are given for O16 and O17 but not for O18. If OpenMC is aware of
|
||||
what cross sections you will be using (either through the
|
||||
:attr:`Materials.cross_sections` attribute or the
|
||||
what cross sections you will be using (through the
|
||||
:envvar:`OPENMC_CROSS_SECTIONS` environment variable), it will attempt to only
|
||||
put isotopes in your model for which you have cross section data. In the case of
|
||||
oxygen in ENDF/B-VII.1, the abundance of O18 would end up being lumped with O16.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ region of phase space, as in:
|
|||
Thus, to specify a tally, we need to specify what regions of phase space should
|
||||
be included when deciding whether to score an event as well as what the scoring
|
||||
function (:math:`f` in the above equation) should be used. The regions of phase
|
||||
space are called *filters* and the scoring functions are simply called *scores*.
|
||||
space are generally called *filters* and the scoring functions are simply
|
||||
called *scores*.
|
||||
|
||||
The only cases when filters do not correspond directly with the regions of
|
||||
phase space are when expansion functions are applied in the integrand, such as
|
||||
for Legendre expansions of the scattering kernel.
|
||||
|
||||
-------
|
||||
Filters
|
||||
|
|
@ -69,10 +74,9 @@ Scores
|
|||
------
|
||||
|
||||
To specify the scoring functions, a list of strings needs to be given to the
|
||||
:attr:`Tally.scores` attribute. You can score the flux ('flux'), a reaction rate
|
||||
('total', 'fission', etc.), or even scattering moments (e.g., 'scatter-P3'). For
|
||||
example, to tally the elastic scattering rate and the fission neutron
|
||||
production, you'd assign::
|
||||
:attr:`Tally.scores` attribute. You can score the flux ('flux'), or a reaction
|
||||
rate ('total', 'fission', etc.). For example, to tally the elastic scattering
|
||||
rate and the fission neutron production, you'd assign::
|
||||
|
||||
tally.scores = ['elastic', 'nu-fission']
|
||||
|
||||
|
|
@ -98,12 +102,6 @@ The following tables show all valid scores:
|
|||
+======================+===================================================+
|
||||
|flux |Total flux. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|flux-YN |Spherical harmonic expansion of the direction of |
|
||||
| |motion :math:`\left(\Omega\right)` of the total |
|
||||
| |flux. This score will tally all of the harmonic |
|
||||
| |moments of order 0 to N. N must be between 0 and |
|
||||
| |10. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|
||||
.. table:: **Reaction scores: units are reactions per source particle.**
|
||||
|
||||
|
|
@ -118,43 +116,10 @@ The following tables show all valid scores:
|
|||
+----------------------+---------------------------------------------------+
|
||||
|fission |Total fission reaction rate. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|scatter |Total scattering rate. Can also be identified with |
|
||||
| |the "scatter-0" response type. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|scatter-N |Tally the N\ :sup:`th` \ scattering moment, where N|
|
||||
| |is the Legendre expansion order of the change in |
|
||||
| |particle angle :math:`\left(\mu\right)`. N must be |
|
||||
| |between 0 and 10. As an example, tallying the 2\ |
|
||||
| |:sup:`nd` \ scattering moment would be specified as|
|
||||
| |``<scores>scatter-2</scores>``. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|scatter-PN |Tally all of the scattering moments from order 0 to|
|
||||
| |N, where N is the Legendre expansion order of the |
|
||||
| |change in particle angle |
|
||||
| |:math:`\left(\mu\right)`. That is, "scatter-P1" is |
|
||||
| |equivalent to requesting tallies of "scatter-0" and|
|
||||
| |"scatter-1". Like for "scatter-N", N must be |
|
||||
| |between 0 and 10. As an example, tallying up to the|
|
||||
| |2\ :sup:`nd` \ scattering moment would be specified|
|
||||
| |as ``<scores> scatter-P2 </scores>``. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|scatter-YN |"scatter-YN" is similar to "scatter-PN" except an |
|
||||
| |additional expansion is performed for the incoming |
|
||||
| |particle direction :math:`\left(\Omega\right)` |
|
||||
| |using the real spherical harmonics. This is useful|
|
||||
| |for performing angular flux moment weighting of the|
|
||||
| |scattering moments. Like "scatter-PN", "scatter-YN"|
|
||||
| |will tally all of the moments from order 0 to N; N |
|
||||
| |again must be between 0 and 10. |
|
||||
|scatter |Total scattering rate. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|total |Total reaction rate. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|total-YN |The total reaction rate expanded via spherical |
|
||||
| |harmonics about the direction of motion of the |
|
||||
| |neutron, :math:`\Omega`. This score will tally all |
|
||||
| |of the harmonic moments of order 0 to N. N must be|
|
||||
| |between 0 and 10. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|(n,2nd) |(n,2nd) reaction rate. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|(n,2n) |(n,2n) reaction rate. |
|
||||
|
|
@ -248,10 +213,10 @@ The following tables show all valid scores:
|
|||
+----------------------+---------------------------------------------------+
|
||||
|nu-fission |Total production of neutrons due to fission. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|nu-scatter, |These scores are similar in functionality to their |
|
||||
|nu-scatter-N, |``scatter*`` equivalents except the total |
|
||||
|nu-scatter-PN, |production of neutrons due to scattering is scored |
|
||||
|nu-scatter-YN |vice simply the scattering rate. This accounts for |
|
||||
|nu-scatter, |This score is similar in functionality to the |
|
||||
| |``scatter`` score except the total production of |
|
||||
| |neutrons due to scattering is scored vice simply |
|
||||
| |the scattering rate. This accounts for |
|
||||
| |multiplicity from (n,2n), (n,3n), and (n,4n) |
|
||||
| |reactions. |
|
||||
+----------------------+---------------------------------------------------+
|
||||
|
|
@ -261,7 +226,7 @@ The following tables show all valid scores:
|
|||
+----------------------+---------------------------------------------------+
|
||||
|Score | Description |
|
||||
+======================+===================================================+
|
||||
|current |Used in combination with a mesh filter: |
|
||||
|current |Used in combination with a meshsurface filter: |
|
||||
| |Partial currents on the boundaries of each cell in |
|
||||
| |a mesh. It may not be used in conjunction with any |
|
||||
| |other score. Only energy and mesh filters may be |
|
||||
|
|
@ -269,7 +234,7 @@ The following tables show all valid scores:
|
|||
| |Used in combination with a surface filter: |
|
||||
| |Net currents on any surface previously defined in |
|
||||
| |the geometry. It may be used along with any other |
|
||||
| |filter, except mesh filters. |
|
||||
| |filter, except meshsurface filters. |
|
||||
| |Surfaces can alternatively be defined with cell |
|
||||
| |from and cell filters thereby resulting in tallying|
|
||||
| |partial currents. |
|
||||
|
|
|
|||
463
examples/jupyter/expansion-filters.ipynb
Normal file
463
examples/jupyter/expansion-filters.ipynb
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -287,18 +287,7 @@
|
|||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"execution_count": 11,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Run openmc in plotting mode\n",
|
||||
"openmc.plot_geometry(output=False)"
|
||||
|
|
@ -313,7 +302,7 @@
|
|||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAACBjSFJN\nAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAADFBMVEX///9yEhLpgJFNv8Tq\nQYT7AAAAAWJLR0QAiAUdSAAAAAd0SU1FB+EMBQIrDwapSyIAAALKSURBVGje7dpLcqQwDAbgHHE2\nYeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmNP+HDhw8fPnz48Kf6VH9G\n+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4zPji99z0/AJ4n1lfvJ6f\nnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6pA0wfln+ho/fwgYYn19C\n/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tNDbSGz7T0SBEWw4vLXzbQ\n6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X58wZaxWd1+fMGiuFvir8b\nvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV873hB8UnM3xzANtf8nb4\ndwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7T/ppARBvp48UwJnelT5S\nACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4//Jve+fhsH6Ctv7n8PTzj\nvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V32/o9+fl389Xnx+g5x/o\n+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6/4Le/6D3T/D9V67Y/ZsV\nQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/gPs/0P4TtP8F7r9J3AIO\n9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTuf4X7b+H+X7T/+BPuf3aM\n8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTItMDRUMjA6NDM6\nMTQtMDY6MDCrFYTfAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEyLTA0VDIwOjQzOjE0LTA2OjAw\n2kg8YwAAAABJRU5ErkJggg==\n",
|
||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6AgMAAAD1grKuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdC\nAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAxQTFRF\n////chIS6YCRTb/E6kGE+wAAAAFiS0dEAIgFHUgAAAAJcEhZcwAAAEgAAABIAEbJaz4AAALKSURB\nVGje7dpLcqQwDAbgHHE2YeEj+D4cwQucBUfo+3CEXoSp8OhuhF70T4qpKXmdr21LogK2Pj7A8QmN\nP+HDhw8fPnz48Kf6VH9G+66vy+je8k19jnf8C5dXIPv86ms56lPdjvaYbyodx3ze+XLE76cXFiD4\nzPji99z0/AJ4n1lfvJ6fnl0A6x+578efMSg1wPr172/jPO5yFXM+Ef78gdblM+WPHyguP//t1/g6\npA0wfln+ho/fwgYYn19C/xwDvwHGc9OvC+hs37DTrwuwfWanXxdQTC9Mvyygs3wjTL8uwPJpn/tN\nDbSGz7T0SBEWw4vLXzbQ6b6RoveIoO6TvPxlA63qs7z8ZQPF9F+SH22vbX8OQKf5Rtv+EgDNJ3X5\n8wZaxWd1+fMGiuFvir8bvjp8J/tGy/6jAmRvhW8fwL3vVT+o3grfPoB7r/IpALI3tz8FoJN84/NV\n873hB8UnM3xzANtf8nb4dwmg3grfFEDJO8JPE0i9Ff4pAYL3pI8mkHor/HMCeO9JH00g9SafEsh7\nT/ppARBvp48UwJnelT5SACd7O31TAlnvKx9SQCd7B58KgPO+8iMFuPWe9E8F8BveWX7bAjzX9y4/\n/Jve+fhsH6Ctv7n8PTzjvY/v9gEOHz58+PBX+6v/f/wPvnd54f3j6venE/yl769Xv7+j3x/o98/V\n32/o9+fl389Xnx+g5x/o+Qt6/oOeP6HnX+j5G3z+h54/ouefV5/foufP6Pk3ev4On/+j9w/o/Qd6\n/4Le/6D3T/D9V67Y/ZsVQBq+s+8f0ftP+P41axXguP9NWgDuu/Cdfv+N3r/D9/9TAID+A7T/Ae2/\ngPs/0P4TtP8F7r9J3AIO9P+g/Udw/9Oygbf7r9D+L7j/DO1/Q/vv4P4/tP8Q7n9E+y/h/k+0/xTu\nf4X7b+H+X7T/+BPuf3aM8OHDhw8fPnz4w/4vzcvgeY10sY0AAAAldEVYdGRhdGU6Y3JlYXRlADIw\nMTgtMDQtMDNUMjE6MTE6MzgtMDQ6MDD1dVTHAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA0LTAz\nVDIxOjExOjM4LTA0OjAwhCjsewAAAABJRU5ErkJggg==\n",
|
||||
"text/plain": [
|
||||
"<IPython.core.display.Image object>"
|
||||
]
|
||||
|
|
@ -392,21 +381,21 @@
|
|||
"mesh.dimension = [1, 1, 1]\n",
|
||||
"mesh.lower_left = [-0.63, -0.63, -100.]\n",
|
||||
"mesh.width = [1.26, 1.26, 200.]\n",
|
||||
"mesh_filter = openmc.MeshFilter(mesh)\n",
|
||||
"meshsurface_filter = openmc.MeshSurfaceFilter(mesh)\n",
|
||||
"\n",
|
||||
"# Instantiate thermal, fast, and total leakage tallies\n",
|
||||
"leak = openmc.Tally(name='leakage')\n",
|
||||
"leak.filters = [mesh_filter]\n",
|
||||
"leak.filters = [meshsurface_filter]\n",
|
||||
"leak.scores = ['current']\n",
|
||||
"tallies_file.append(leak)\n",
|
||||
"\n",
|
||||
"thermal_leak = openmc.Tally(name='thermal leakage')\n",
|
||||
"thermal_leak.filters = [mesh_filter, openmc.EnergyFilter([0., 0.625])]\n",
|
||||
"thermal_leak.filters = [meshsurface_filter, openmc.EnergyFilter([0., 0.625])]\n",
|
||||
"thermal_leak.scores = ['current']\n",
|
||||
"tallies_file.append(thermal_leak)\n",
|
||||
"\n",
|
||||
"fast_leak = openmc.Tally(name='fast leakage')\n",
|
||||
"fast_leak.filters = [mesh_filter, openmc.EnergyFilter([0.625, 20.0e6])]\n",
|
||||
"fast_leak.filters = [meshsurface_filter, openmc.EnergyFilter([0.625, 20.0e6])]\n",
|
||||
"fast_leak.scores = ['current']\n",
|
||||
"tallies_file.append(fast_leak)"
|
||||
]
|
||||
|
|
@ -504,11 +493,11 @@
|
|||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another EnergyFilter instance already exists with id=6.\n",
|
||||
"/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=6.\n",
|
||||
" warn(msg, IDWarning)\n",
|
||||
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=3.\n",
|
||||
"/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=3.\n",
|
||||
" warn(msg, IDWarning)\n",
|
||||
"/home/romano/openmc/openmc/mixin.py:61: IDWarning: Another CellFilter instance already exists with id=2.\n",
|
||||
"/home/liangjg/.local/lib/python3.5/site-packages/openmc-0.10.0-py3.5.egg/openmc/mixin.py:71: IDWarning: Another Filter instance already exists with id=2.\n",
|
||||
" warn(msg, IDWarning)\n"
|
||||
]
|
||||
}
|
||||
|
|
@ -563,24 +552,25 @@
|
|||
" %%%%%%%%%%%\n",
|
||||
"\n",
|
||||
" | The OpenMC Monte Carlo Code\n",
|
||||
" Copyright | 2011-2017 Massachusetts Institute of Technology\n",
|
||||
" Copyright | 2011-2018 Massachusetts Institute of Technology\n",
|
||||
" License | http://openmc.readthedocs.io/en/latest/license.html\n",
|
||||
" Version | 0.9.0\n",
|
||||
" Git SHA1 | 9b7cebf7bc34d60e0f1750c3d6cb103df11e8dc4\n",
|
||||
" Date/Time | 2017-12-04 20:43:15\n",
|
||||
" OpenMP Threads | 4\n",
|
||||
" Version | 0.10.0\n",
|
||||
" Git SHA1 | 47fbf8282ea94c138f75219bd10fdb31501d3fb7\n",
|
||||
" Date/Time | 2018-04-03 21:12:27\n",
|
||||
" MPI Processes | 1\n",
|
||||
" OpenMP Threads | 20\n",
|
||||
"\n",
|
||||
" Reading settings XML file...\n",
|
||||
" Reading cross sections XML file...\n",
|
||||
" Reading materials XML file...\n",
|
||||
" Reading geometry XML file...\n",
|
||||
" Building neighboring cells lists for each surface...\n",
|
||||
" Reading U235 from /home/romano/openmc/scripts/nndc_hdf5/U235.h5\n",
|
||||
" Reading U238 from /home/romano/openmc/scripts/nndc_hdf5/U238.h5\n",
|
||||
" Reading O16 from /home/romano/openmc/scripts/nndc_hdf5/O16.h5\n",
|
||||
" Reading H1 from /home/romano/openmc/scripts/nndc_hdf5/H1.h5\n",
|
||||
" Reading B10 from /home/romano/openmc/scripts/nndc_hdf5/B10.h5\n",
|
||||
" Reading Zr90 from /home/romano/openmc/scripts/nndc_hdf5/Zr90.h5\n",
|
||||
" Reading U235 from /home/liangjg/nucdata/nndc_hdf5/U235.h5\n",
|
||||
" Reading U238 from /home/liangjg/nucdata/nndc_hdf5/U238.h5\n",
|
||||
" Reading O16 from /home/liangjg/nucdata/nndc_hdf5/O16.h5\n",
|
||||
" Reading H1 from /home/liangjg/nucdata/nndc_hdf5/H1.h5\n",
|
||||
" Reading B10 from /home/liangjg/nucdata/nndc_hdf5/B10.h5\n",
|
||||
" Reading Zr90 from /home/liangjg/nucdata/nndc_hdf5/Zr90.h5\n",
|
||||
" Maximum neutron transport energy: 2.00000E+07 eV for U235\n",
|
||||
" Reading tallies XML file...\n",
|
||||
" Writing summary.h5 file...\n",
|
||||
|
|
@ -614,20 +604,20 @@
|
|||
"\n",
|
||||
" =======================> TIMING STATISTICS <=======================\n",
|
||||
"\n",
|
||||
" Total time for initialization = 5.6782E-01 seconds\n",
|
||||
" Reading cross sections = 5.3276E-01 seconds\n",
|
||||
" Total time in simulation = 6.4149E+00 seconds\n",
|
||||
" Time in transport only = 6.2767E+00 seconds\n",
|
||||
" Time in inactive batches = 6.8747E-01 seconds\n",
|
||||
" Time in active batches = 5.7274E+00 seconds\n",
|
||||
" Time synchronizing fission bank = 2.7492E-03 seconds\n",
|
||||
" Sampling source sites = 1.9584E-03 seconds\n",
|
||||
" SEND/RECV source sites = 7.4113E-04 seconds\n",
|
||||
" Time accumulating tallies = 1.0576E-04 seconds\n",
|
||||
" Total time for finalization = 2.2075E-03 seconds\n",
|
||||
" Total time elapsed = 7.0056E+00 seconds\n",
|
||||
" Calculation Rate (inactive) = 18182.5 neutrons/second\n",
|
||||
" Calculation Rate (active) = 6547.45 neutrons/second\n",
|
||||
" Total time for initialization = 4.9090E-01 seconds\n",
|
||||
" Reading cross sections = 4.2387E-01 seconds\n",
|
||||
" Total time in simulation = 1.4928E+00 seconds\n",
|
||||
" Time in transport only = 1.3545E+00 seconds\n",
|
||||
" Time in inactive batches = 1.3625E-01 seconds\n",
|
||||
" Time in active batches = 1.3565E+00 seconds\n",
|
||||
" Time synchronizing fission bank = 2.4053E-03 seconds\n",
|
||||
" Sampling source sites = 1.6466E-03 seconds\n",
|
||||
" SEND/RECV source sites = 5.6159E-04 seconds\n",
|
||||
" Time accumulating tallies = 3.3647E-04 seconds\n",
|
||||
" Total time for finalization = 1.6066E-02 seconds\n",
|
||||
" Total time elapsed = 2.0336E+00 seconds\n",
|
||||
" Calculation Rate (inactive) = 91743.2 neutrons/second\n",
|
||||
" Calculation Rate (active) = 27644.5 neutrons/second\n",
|
||||
"\n",
|
||||
" ============================> RESULTS <============================\n",
|
||||
"\n",
|
||||
|
|
@ -638,16 +628,6 @@
|
|||
" Leakage Fraction = 0.01717 +/- 0.00107\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"execution_count": 21,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
|
|
@ -741,8 +721,7 @@
|
|||
"\n",
|
||||
"# Get the leakage tally\n",
|
||||
"leak = sp.get_tally(name='leakage')\n",
|
||||
"leak = leak.summation(filter_type=openmc.SurfaceFilter, remove_filter=True)\n",
|
||||
"leak = leak.summation(filter_type=openmc.MeshFilter, remove_filter=True)\n",
|
||||
"leak = leak.summation(filter_type=openmc.MeshSurfaceFilter, remove_filter=True)\n",
|
||||
"\n",
|
||||
"# Compute k-infinity using tally arithmetic\n",
|
||||
"keff = fiss_rate / (abs_rate + leak)\n",
|
||||
|
|
@ -812,8 +791,7 @@
|
|||
"# Compute resonance escape probability using tally arithmetic\n",
|
||||
"therm_abs_rate = sp.get_tally(name='therm. abs. rate')\n",
|
||||
"thermal_leak = sp.get_tally(name='thermal leakage')\n",
|
||||
"thermal_leak = thermal_leak.summation(filter_type=openmc.SurfaceFilter, remove_filter=True)\n",
|
||||
"thermal_leak = thermal_leak.summation(filter_type=openmc.MeshFilter, remove_filter=True)\n",
|
||||
"thermal_leak = thermal_leak.summation(filter_type=openmc.MeshSurfaceFilter, remove_filter=True)\n",
|
||||
"res_esc = (therm_abs_rate + thermal_leak) / (abs_rate + thermal_leak)\n",
|
||||
"res_esc.get_pandas_dataframe()"
|
||||
]
|
||||
|
|
|
|||
49
examples/python/pincell_depletion/chain_simple.xml
Normal file
49
examples/python/pincell_depletion/chain_simple.xml
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0"?>
|
||||
<depletion_chain>
|
||||
<nuclide name="I135" decay_modes="1" reactions="1" half_life="2.36520E+04">
|
||||
<decay type="beta" target="Xe135" branching_ratio="1.0" />
|
||||
<reaction type="(n,gamma)" Q="0.0" target="Xe136" /> <!-- Not precisely true, but whatever -->
|
||||
</nuclide>
|
||||
<nuclide name="Xe135" decay_modes="1" reactions="1" half_life="3.29040E+04">
|
||||
<decay type=" beta" target="Cs135" branching_ratio="1.0" />
|
||||
<reaction type="(n,gamma)" Q="0.0" target="Xe136" />
|
||||
</nuclide>
|
||||
<nuclide name="Xe136" decay_modes="0" reactions="0" />
|
||||
<nuclide name="Cs135" decay_modes="0" reactions="0" />
|
||||
<nuclide name="Gd157" decay_modes="0" reactions="1" >
|
||||
<reaction type="(n,gamma)" Q="0.0" target="Nothing" />
|
||||
</nuclide>
|
||||
<nuclide name="Gd156" decay_modes="0" reactions="1">
|
||||
<reaction type="(n,gamma)" Q="0.0" target="Gd157" />
|
||||
</nuclide>
|
||||
<nuclide name="U234" decay_modes="0" reactions="1">
|
||||
<reaction type="fission" Q="191840000."/>
|
||||
<neutron_fission_yields>
|
||||
<energies>2.53000e-02</energies>
|
||||
<fission_yields energy="2.53000e-02">
|
||||
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
|
||||
<data>1.093250e-04 2.087260e-04 2.780820e-02 6.759540e-03 2.392300e-02 4.356330e-05</data>
|
||||
</fission_yields>
|
||||
</neutron_fission_yields>
|
||||
</nuclide>
|
||||
<nuclide name="U235" decay_modes="0" reactions="1">
|
||||
<reaction type="fission" Q="193410000."/>
|
||||
<neutron_fission_yields>
|
||||
<energies>2.53000e-02</energies>
|
||||
<fission_yields energy="2.53000e-02">
|
||||
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
|
||||
<data>6.142710e-5 1.483250e-04 0.0292737 0.002566345 0.0219242 4.9097e-6</data>
|
||||
</fission_yields>
|
||||
</neutron_fission_yields>
|
||||
</nuclide>
|
||||
<nuclide name="U238" decay_modes="0" reactions="1">
|
||||
<reaction type="fission" Q="197790000."/>
|
||||
<neutron_fission_yields>
|
||||
<energies>2.53000e-02</energies>
|
||||
<fission_yields energy="2.53000e-02">
|
||||
<products>Gd157 Gd156 I135 Xe135 Xe136 Cs135</products>
|
||||
<data>4.141120e-04 7.605360e-04 0.0135457 0.00026864 0.0024432 3.7100E-07</data>
|
||||
</fission_yields>
|
||||
</neutron_fission_yields>
|
||||
</nuclide>
|
||||
</depletion_chain>
|
||||
103
examples/python/pincell_depletion/restart_depletion.py
Normal file
103
examples/python/pincell_depletion/restart_depletion.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
import openmc
|
||||
import openmc.deplete
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
###############################################################################
|
||||
|
||||
# OpenMC simulation parameters
|
||||
batches = 100
|
||||
inactive = 10
|
||||
particles = 1000
|
||||
|
||||
# Depletion simulation parameters
|
||||
time_step = 1*24*60*60 # s
|
||||
final_time = 5*24*60*60 # s
|
||||
time_steps = np.full(final_time // time_step, time_step)
|
||||
|
||||
chain_file = './chain_simple.xml'
|
||||
power = 174 # W/cm, for 2D simulations only (use W for 3D)
|
||||
|
||||
###############################################################################
|
||||
# Load previous simulation results
|
||||
###############################################################################
|
||||
|
||||
# Load geometry from statepoint
|
||||
statepoint = 'statepoint.100.h5'
|
||||
with openmc.StatePoint(statepoint) as sp:
|
||||
geometry = sp.summary.geometry
|
||||
|
||||
# Load previous depletion results
|
||||
previous_results = openmc.deplete.ResultsList("depletion_results.h5")
|
||||
|
||||
###############################################################################
|
||||
# Transport calculation settings
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a Settings object, set all runtime parameters
|
||||
settings_file = openmc.Settings()
|
||||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
entropy_mesh = openmc.Mesh()
|
||||
entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50]
|
||||
entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50]
|
||||
entropy_mesh.dimension = [10, 10, 1]
|
||||
settings_file.entropy_mesh = entropy_mesh
|
||||
|
||||
###############################################################################
|
||||
# Initialize and run depletion calculation
|
||||
###############################################################################
|
||||
|
||||
op = openmc.deplete.Operator(geometry, settings_file, chain_file,
|
||||
previous_results)
|
||||
|
||||
# Perform simulation using the predictor algorithm
|
||||
openmc.deplete.integrator.predictor(op, time_steps, power)
|
||||
|
||||
###############################################################################
|
||||
# Read depletion calculation results
|
||||
###############################################################################
|
||||
|
||||
# Open results file
|
||||
results = openmc.deplete.ResultsList("depletion_results.h5")
|
||||
|
||||
# Obtain K_eff as a function of time
|
||||
time, keff = results.get_eigenvalue()
|
||||
|
||||
# Obtain U235 concentration as a function of time
|
||||
time, n_U235 = results.get_atoms('1', 'U235')
|
||||
|
||||
# Obtain Xe135 absorption as a function of time
|
||||
time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)')
|
||||
|
||||
###############################################################################
|
||||
# Generate plots
|
||||
###############################################################################
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), keff, label="K-effective")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("Keff")
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), n_U235, label="U 235")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("n U5 (-)")
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("RR (-)")
|
||||
plt.show()
|
||||
plt.close('all')
|
||||
175
examples/python/pincell_depletion/run_depletion.py
Normal file
175
examples/python/pincell_depletion/run_depletion.py
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
import openmc
|
||||
import openmc.deplete
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
###############################################################################
|
||||
# Simulation Input File Parameters
|
||||
###############################################################################
|
||||
|
||||
# OpenMC simulation parameters
|
||||
batches = 100
|
||||
inactive = 10
|
||||
particles = 1000
|
||||
|
||||
# Depletion simulation parameters
|
||||
time_step = 1*24*60*60 # s
|
||||
final_time = 5*24*60*60 # s
|
||||
time_steps = np.full(final_time // time_step, time_step)
|
||||
|
||||
chain_file = './chain_simple.xml'
|
||||
power = 174 # W/cm, for 2D simulations only (use W for 3D)
|
||||
|
||||
###############################################################################
|
||||
# Define materials
|
||||
###############################################################################
|
||||
|
||||
# Instantiate some Materials and register the appropriate Nuclides
|
||||
uo2 = openmc.Material(material_id=1, name='UO2 fuel at 2.4% wt enrichment')
|
||||
uo2.set_density('g/cm3', 10.29769)
|
||||
uo2.add_element('U', 1., enrichment=2.4)
|
||||
uo2.add_element('O', 2.)
|
||||
uo2.depletable = True
|
||||
|
||||
helium = openmc.Material(material_id=2, name='Helium for gap')
|
||||
helium.set_density('g/cm3', 0.001598)
|
||||
helium.add_element('He', 2.4044e-4)
|
||||
|
||||
zircaloy = openmc.Material(material_id=3, name='Zircaloy 4')
|
||||
zircaloy.set_density('g/cm3', 6.55)
|
||||
zircaloy.add_element('Sn', 0.014 , 'wo')
|
||||
zircaloy.add_element('Fe', 0.00165, 'wo')
|
||||
zircaloy.add_element('Cr', 0.001 , 'wo')
|
||||
zircaloy.add_element('Zr', 0.98335, 'wo')
|
||||
|
||||
borated_water = openmc.Material(material_id=4, name='Borated water')
|
||||
borated_water.set_density('g/cm3', 0.740582)
|
||||
borated_water.add_element('B', 4.0e-5)
|
||||
borated_water.add_element('H', 5.0e-2)
|
||||
borated_water.add_element('O', 2.4e-2)
|
||||
borated_water.add_s_alpha_beta('c_H_in_H2O')
|
||||
|
||||
###############################################################################
|
||||
# Create geometry
|
||||
###############################################################################
|
||||
|
||||
# Instantiate ZCylinder surfaces
|
||||
fuel_or = openmc.ZCylinder(surface_id=1, x0=0, y0=0, R=0.39218, name='Fuel OR')
|
||||
clad_ir = openmc.ZCylinder(surface_id=2, x0=0, y0=0, R=0.40005, name='Clad IR')
|
||||
clad_or = openmc.ZCylinder(surface_id=3, x0=0, y0=0, R=0.45720, name='Clad OR')
|
||||
left = openmc.XPlane(surface_id=4, x0=-0.62992, name='left')
|
||||
right = openmc.XPlane(surface_id=5, x0=0.62992, name='right')
|
||||
bottom = openmc.YPlane(surface_id=6, y0=-0.62992, name='bottom')
|
||||
top = openmc.YPlane(surface_id=7, y0=0.62992, name='top')
|
||||
|
||||
left.boundary_type = 'reflective'
|
||||
right.boundary_type = 'reflective'
|
||||
top.boundary_type = 'reflective'
|
||||
bottom.boundary_type = 'reflective'
|
||||
|
||||
# Instantiate Cells
|
||||
fuel = openmc.Cell(cell_id=1, name='cell 1')
|
||||
gap = openmc.Cell(cell_id=2, name='cell 2')
|
||||
clad = openmc.Cell(cell_id=3, name='cell 3')
|
||||
water = openmc.Cell(cell_id=4, name='cell 4')
|
||||
|
||||
# Use surface half-spaces to define regions
|
||||
fuel.region = -fuel_or
|
||||
gap.region = +fuel_or & -clad_ir
|
||||
clad.region = +clad_ir & -clad_or
|
||||
water.region = +clad_or & +left & -right & +bottom & -top
|
||||
|
||||
# Register Materials with Cells
|
||||
fuel.fill = uo2
|
||||
gap.fill = helium
|
||||
clad.fill = zircaloy
|
||||
water.fill = borated_water
|
||||
|
||||
# Instantiate Universe
|
||||
root = openmc.Universe(universe_id=0, name='root universe')
|
||||
|
||||
# Register Cells with Universe
|
||||
root.add_cells([fuel, gap, clad, water])
|
||||
|
||||
# Instantiate a Geometry, register the root Universe
|
||||
geometry = openmc.Geometry(root)
|
||||
|
||||
###############################################################################
|
||||
# Set volumes of depletable materials
|
||||
###############################################################################
|
||||
|
||||
# Compute cell areas
|
||||
area = {}
|
||||
area[fuel] = np.pi * fuel_or.coefficients['R'] ** 2
|
||||
|
||||
# Set materials volume for depletion. Set to an area for 2D simulations
|
||||
uo2.volume = area[fuel]
|
||||
|
||||
###############################################################################
|
||||
# Transport calculation settings
|
||||
###############################################################################
|
||||
|
||||
# Instantiate a Settings object, set all runtime parameters, and export to XML
|
||||
settings_file = openmc.Settings()
|
||||
settings_file.batches = batches
|
||||
settings_file.inactive = inactive
|
||||
settings_file.particles = particles
|
||||
|
||||
# Create an initial uniform spatial source distribution over fissionable zones
|
||||
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
|
||||
uniform_dist = openmc.stats.Box(bounds[:3], bounds[3:], only_fissionable=True)
|
||||
settings_file.source = openmc.source.Source(space=uniform_dist)
|
||||
|
||||
entropy_mesh = openmc.Mesh()
|
||||
entropy_mesh.lower_left = [-0.39218, -0.39218, -1.e50]
|
||||
entropy_mesh.upper_right = [0.39218, 0.39218, 1.e50]
|
||||
entropy_mesh.dimension = [10, 10, 1]
|
||||
settings_file.entropy_mesh = entropy_mesh
|
||||
|
||||
###############################################################################
|
||||
# Initialize and run depletion calculation
|
||||
###############################################################################
|
||||
|
||||
op = openmc.deplete.Operator(geometry, settings_file, chain_file)
|
||||
|
||||
# Perform simulation using the predictor algorithm
|
||||
openmc.deplete.integrator.predictor(op, time_steps, power)
|
||||
|
||||
###############################################################################
|
||||
# Read depletion calculation results
|
||||
###############################################################################
|
||||
|
||||
# Open results file
|
||||
results = openmc.deplete.ResultsList("depletion_results.h5")
|
||||
|
||||
# Obtain K_eff as a function of time
|
||||
time, keff = results.get_eigenvalue()
|
||||
|
||||
# Obtain U235 concentration as a function of time
|
||||
time, n_U235 = results.get_atoms('1', 'U235')
|
||||
|
||||
# Obtain Xe135 absorption as a function of time
|
||||
time, Xe_gam = results.get_reaction_rate('1', 'Xe135', '(n,gamma)')
|
||||
|
||||
###############################################################################
|
||||
# Generate plots
|
||||
###############################################################################
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), keff, label="K-effective")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("Keff")
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), n_U235, label="U 235")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("n U5 (-)")
|
||||
plt.show()
|
||||
|
||||
plt.figure()
|
||||
plt.plot(time/(24*60*60), Xe_gam, label="Xe135 absorption")
|
||||
plt.xlabel("Time (days)")
|
||||
plt.ylabel("RR (-)")
|
||||
plt.show()
|
||||
plt.close('all')
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
</mesh>
|
||||
|
||||
<filter id="1" type="mesh">
|
||||
<bins>1</bins>
|
||||
<bins>2</bins>
|
||||
</filter>
|
||||
|
||||
<filter id="2" type="energy">
|
||||
|
|
|
|||
110
include/openmc.h
110
include/openmc.h
|
|
@ -16,7 +16,8 @@ extern "C" {
|
|||
int delayed_group;
|
||||
};
|
||||
|
||||
void openmc_calculate_voumes();
|
||||
int openmc_calculate_volumes();
|
||||
int openmc_cell_filter_get_bins(int32_t index, int32_t** cells, int32_t* n);
|
||||
int openmc_cell_get_fill(int32_t index, int* type, int32_t** indices, int32_t* n);
|
||||
int openmc_cell_get_id(int32_t index, int32_t* id);
|
||||
int openmc_cell_set_fill(int32_t index, int type, int32_t n, const int32_t* indices);
|
||||
|
|
@ -27,21 +28,29 @@ extern "C" {
|
|||
int openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_extend_sources(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end);
|
||||
int openmc_filter_get_id(int32_t index, int32_t* id);
|
||||
int openmc_filter_get_type(int32_t index, char* type);
|
||||
int openmc_filter_set_id(int32_t index, int32_t id);
|
||||
void openmc_finalize();
|
||||
int openmc_filter_set_type(int32_t index, const char* type);
|
||||
int openmc_finalize();
|
||||
int openmc_find(double* xyz, int rtype, int32_t* id, int32_t* instance);
|
||||
int openmc_get_cell_index(int32_t id, int32_t* index);
|
||||
int openmc_get_filter_index(int32_t id, int32_t* index);
|
||||
void openmc_get_filter_next_id(int32_t* id);
|
||||
int openmc_get_keff(double k_combined[]);
|
||||
int openmc_get_material_index(int32_t id, int32_t* index);
|
||||
int openmc_get_nuclide_index(char name[], int* index);
|
||||
int openmc_get_mesh_index(int32_t id, int32_t* index);
|
||||
int openmc_get_nuclide_index(const char name[], int* index);
|
||||
int64_t openmc_get_seed();
|
||||
int openmc_get_tally_index(int32_t id, int32_t* index);
|
||||
void openmc_hard_reset();
|
||||
void openmc_init(const int* intracomm);
|
||||
int openmc_hard_reset();
|
||||
int openmc_init(int argc, char* argv[], const void* intracomm);
|
||||
int openmc_init_f(const int* intracomm);
|
||||
int openmc_legendre_filter_get_order(int32_t index, int* order);
|
||||
int openmc_legendre_filter_set_order(int32_t index, int order);
|
||||
int openmc_load_nuclide(char name[]);
|
||||
int openmc_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);
|
||||
|
|
@ -51,45 +60,73 @@ extern "C" {
|
|||
int openmc_material_set_id(int32_t index, int32_t id);
|
||||
int openmc_material_filter_get_bins(int32_t index, int32_t** bins, int32_t* n);
|
||||
int openmc_material_filter_set_bins(int32_t index, int32_t n, const int32_t* bins);
|
||||
int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh);
|
||||
int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh);
|
||||
int openmc_next_batch();
|
||||
int openmc_mesh_get_id(int32_t index, int32_t* id);
|
||||
int openmc_mesh_get_dimension(int32_t index, int** id, int* n);
|
||||
int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n);
|
||||
int openmc_mesh_set_id(int32_t index, int32_t id);
|
||||
int openmc_mesh_set_dimension(int32_t index, int n, const int* dims);
|
||||
int openmc_mesh_set_params(int32_t index, const double* ll, const double* ur, const double* width, int n);
|
||||
int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh);
|
||||
int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh);
|
||||
int openmc_next_batch(int* status);
|
||||
int openmc_nuclide_name(int index, char** name);
|
||||
void openmc_plot_geometry();
|
||||
void openmc_reset();
|
||||
void openmc_run();
|
||||
void openmc_simulation_finalize();
|
||||
void openmc_simulation_init();
|
||||
int openmc_particle_restart();
|
||||
int openmc_plot_geometry();
|
||||
int openmc_reset();
|
||||
int openmc_run();
|
||||
void openmc_set_seed(int64_t new_seed);
|
||||
int openmc_simulation_finalize();
|
||||
int openmc_simulation_init();
|
||||
int openmc_source_bank(struct Bank** ptr, int64_t* n);
|
||||
int openmc_source_set_strength(int32_t index, double strength);
|
||||
void openmc_statepoint_write(const char filename[]);
|
||||
int openmc_spatial_legendre_filter_get_order(int32_t index, int* order);
|
||||
int openmc_spatial_legendre_filter_get_params(int32_t index, int* axis, double* min, double* max);
|
||||
int openmc_spatial_legendre_filter_set_order(int32_t index, int order);
|
||||
int openmc_spatial_legendre_filter_set_params(int32_t index, const int* axis,
|
||||
const double* min, const double* max);
|
||||
int openmc_sphharm_filter_get_order(int32_t index, int* order);
|
||||
int openmc_sphharm_filter_get_cosine(int32_t index, char cosine[]);
|
||||
int openmc_sphharm_filter_set_order(int32_t index, int order);
|
||||
int openmc_sphharm_filter_set_cosine(int32_t index, const char cosine[]);
|
||||
int openmc_statepoint_write(const char filename[]);
|
||||
int openmc_tally_get_active(int32_t index, bool* active);
|
||||
int openmc_tally_get_id(int32_t index, int32_t* id);
|
||||
int openmc_tally_get_filters(int32_t index, int32_t** indices, int* n);
|
||||
int openmc_tally_get_n_realizations(int32_t index, int32_t* n);
|
||||
int openmc_tally_get_nuclides(int32_t index, int** nuclides, int* n);
|
||||
int openmc_tally_get_scores(int32_t index, int** scores, int* n);
|
||||
int openmc_tally_results(int32_t index, double** ptr, int shape_[3]);
|
||||
int openmc_tally_set_active(int32_t index, bool active);
|
||||
int openmc_tally_set_filters(int32_t index, int n, const int32_t* indices);
|
||||
int openmc_tally_set_id(int32_t index, int32_t id);
|
||||
int openmc_tally_set_nuclides(int32_t index, int n, const char** nuclides);
|
||||
int openmc_tally_set_scores(int32_t index, int n, const int* scores);
|
||||
int openmc_tally_set_scores(int32_t index, int n, const char** scores);
|
||||
int openmc_tally_set_type(int32_t index, const char* type);
|
||||
int openmc_zernike_filter_get_order(int32_t index, int* order);
|
||||
int openmc_zernike_filter_get_params(int32_t index, double* x, double* y, double* r);
|
||||
int openmc_zernike_filter_set_order(int32_t index, int order);
|
||||
int openmc_zernike_filter_set_params(int32_t index, const double* x,
|
||||
const double* y, const double* r);
|
||||
|
||||
// Error codes
|
||||
extern int E_UNASSIGNED;
|
||||
extern int E_ALLOCATE;
|
||||
extern int E_OUT_OF_BOUNDS;
|
||||
extern int E_INVALID_SIZE;
|
||||
extern int E_INVALID_ARGUMENT;
|
||||
extern int E_INVALID_TYPE;
|
||||
extern int E_INVALID_ID;
|
||||
extern int E_GEOMETRY;
|
||||
extern int E_DATA;
|
||||
extern int E_PHYSICS;
|
||||
extern int E_WARNING;
|
||||
extern int OPENMC_E_UNASSIGNED;
|
||||
extern int OPENMC_E_ALLOCATE;
|
||||
extern int OPENMC_E_OUT_OF_BOUNDS;
|
||||
extern int OPENMC_E_INVALID_SIZE;
|
||||
extern int OPENMC_E_INVALID_ARGUMENT;
|
||||
extern int OPENMC_E_INVALID_TYPE;
|
||||
extern int OPENMC_E_INVALID_ID;
|
||||
extern int OPENMC_E_GEOMETRY;
|
||||
extern int OPENMC_E_DATA;
|
||||
extern int OPENMC_E_PHYSICS;
|
||||
extern int OPENMC_E_WARNING;
|
||||
|
||||
// Global variables
|
||||
extern char openmc_err_msg[256];
|
||||
extern double keff;
|
||||
extern double keff_std;
|
||||
extern double openmc_keff;
|
||||
extern double openmc_keff_std;
|
||||
extern int32_t n_batches;
|
||||
extern int32_t n_cells;
|
||||
extern int32_t n_filters;
|
||||
|
|
@ -105,9 +142,24 @@ extern "C" {
|
|||
extern int32_t n_surfaces;
|
||||
extern int32_t n_tallies;
|
||||
extern int32_t n_universes;
|
||||
extern int run_mode;
|
||||
extern bool simulation_initialized;
|
||||
extern int verbosity;
|
||||
extern int openmc_run_mode;
|
||||
extern bool openmc_simulation_initialized;
|
||||
extern int openmc_verbosity;
|
||||
|
||||
// Variables that are shared by necessity (can be removed from public header
|
||||
// later)
|
||||
extern bool openmc_master;
|
||||
extern int openmc_n_procs;
|
||||
extern int openmc_n_threads;
|
||||
extern int openmc_rank;
|
||||
extern int64_t openmc_work;
|
||||
|
||||
// Run modes
|
||||
constexpr int RUN_MODE_FIXEDSOURCE {1};
|
||||
constexpr int RUN_MODE_EIGENVALUE {2};
|
||||
constexpr int RUN_MODE_PLOTTING {3};
|
||||
constexpr int RUN_MODE_PARTICLE {4};
|
||||
constexpr int RUN_MODE_VOLUME {5};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from openmc.surface import *
|
|||
from openmc.universe import *
|
||||
from openmc.lattice import *
|
||||
from openmc.filter import *
|
||||
from openmc.filter_expansion import *
|
||||
from openmc.trigger import *
|
||||
from openmc.tally_derivative import *
|
||||
from openmc.tallies import *
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ from .core import *
|
|||
from .nuclide import *
|
||||
from .material import *
|
||||
from .cell import *
|
||||
from .mesh import *
|
||||
from .filter import *
|
||||
from .tally import *
|
||||
from .settings import settings
|
||||
from .math import *
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ from weakref import WeakValueDictionary
|
|||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from . import _dll
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler, AllocationError, InvalidIDError
|
||||
from .error import _error_handler
|
||||
from .material import Material
|
||||
|
||||
__all__ = ['Cell', 'cells']
|
||||
|
|
@ -44,7 +45,7 @@ class Cell(_FortranObjectWithID):
|
|||
|
||||
This class exposes a cell that is stored internally in the OpenMC
|
||||
library. To obtain a view of a cell with a given ID, use the
|
||||
:data:`openmc.capi.nuclides` mapping.
|
||||
:data:`openmc.capi.cells` mapping.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -115,14 +116,15 @@ class Cell(_FortranObjectWithID):
|
|||
def fill(self, fill):
|
||||
if isinstance(fill, Iterable):
|
||||
n = len(fill)
|
||||
indices = (c_int*n)(*(m._index for m in fill))
|
||||
_dll.openmc_cell_set_fill(self._index, 1, 1, indices)
|
||||
indices = (c_int32*n)(*(m._index if m is not None else -1
|
||||
for m in fill))
|
||||
_dll.openmc_cell_set_fill(self._index, 1, n, indices)
|
||||
elif isinstance(fill, Material):
|
||||
materials = [fill]
|
||||
indices = (c_int*1)(fill._index)
|
||||
indices = (c_int32*1)(fill._index)
|
||||
_dll.openmc_cell_set_fill(self._index, 1, 1, indices)
|
||||
elif fill is None:
|
||||
indices = (c_int32*1)(-1)
|
||||
_dll.openmc_cell_set_fill(self._index, 1, 1, indices)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
def set_temperature(self, T, instance=None):
|
||||
"""Set the temperature of a cell
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
from contextlib import contextmanager
|
||||
from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p,
|
||||
POINTER, Structure)
|
||||
from ctypes import (CDLL, c_int, c_int32, c_int64, c_double, c_char_p, c_char,
|
||||
POINTER, Structure, c_void_p, create_string_buffer)
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError
|
||||
from . import _dll
|
||||
from .error import _error_handler, AllocationError
|
||||
from .error import _error_handler
|
||||
import openmc.capi
|
||||
|
||||
|
||||
|
|
@ -19,29 +20,41 @@ class _Bank(Structure):
|
|||
('delayed_group', c_int)]
|
||||
|
||||
|
||||
_dll.openmc_calculate_volumes.restype = None
|
||||
_dll.openmc_finalize.restype = None
|
||||
_dll.openmc_calculate_volumes.restype = c_int
|
||||
_dll.openmc_calculate_volumes.errcheck = _error_handler
|
||||
_dll.openmc_finalize.restype = c_int
|
||||
_dll.openmc_finalize.errcheck = _error_handler
|
||||
_dll.openmc_find.argtypes = [POINTER(c_double*3), c_int, POINTER(c_int32),
|
||||
POINTER(c_int32)]
|
||||
_dll.openmc_find.restype = c_int
|
||||
_dll.openmc_find.errcheck = _error_handler
|
||||
_dll.openmc_hard_reset.restype = None
|
||||
_dll.openmc_init.argtypes = [POINTER(c_int)]
|
||||
_dll.openmc_init.restype = None
|
||||
_dll.openmc_hard_reset.restype = c_int
|
||||
_dll.openmc_hard_reset.errcheck = _error_handler
|
||||
_dll.openmc_init.argtypes = [c_int, POINTER(POINTER(c_char)), c_void_p]
|
||||
_dll.openmc_init.restype = c_int
|
||||
_dll.openmc_init.errcheck = _error_handler
|
||||
_dll.openmc_get_keff.argtypes = [POINTER(c_double*2)]
|
||||
_dll.openmc_get_keff.restype = c_int
|
||||
_dll.openmc_get_keff.errcheck = _error_handler
|
||||
_dll.openmc_next_batch.argtypes = [POINTER(c_int)]
|
||||
_dll.openmc_next_batch.restype = c_int
|
||||
_dll.openmc_plot_geometry.restype = None
|
||||
_dll.openmc_run.restype = None
|
||||
_dll.openmc_reset.restype = None
|
||||
_dll.openmc_next_batch.errcheck = _error_handler
|
||||
_dll.openmc_plot_geometry.restype = c_int
|
||||
_dll.openmc_plot_geometry.restype = _error_handler
|
||||
_dll.openmc_run.restype = c_int
|
||||
_dll.openmc_run.errcheck = _error_handler
|
||||
_dll.openmc_reset.restype = c_int
|
||||
_dll.openmc_reset.errcheck = _error_handler
|
||||
_dll.openmc_source_bank.argtypes = [POINTER(POINTER(_Bank)), POINTER(c_int64)]
|
||||
_dll.openmc_source_bank.restype = c_int
|
||||
_dll.openmc_source_bank.errcheck = _error_handler
|
||||
_dll.openmc_simulation_init.restype = None
|
||||
_dll.openmc_simulation_finalize.restype = None
|
||||
_dll.openmc_simulation_init.restype = c_int
|
||||
_dll.openmc_simulation_init.errcheck = _error_handler
|
||||
_dll.openmc_simulation_finalize.restype = c_int
|
||||
_dll.openmc_simulation_finalize.errcheck = _error_handler
|
||||
_dll.openmc_statepoint_write.argtypes = [POINTER(c_char_p)]
|
||||
_dll.openmc_statepoint_write.restype = None
|
||||
_dll.openmc_statepoint_write.restype = c_int
|
||||
_dll.openmc_statepoint_write.errcheck = _error_handler
|
||||
|
||||
|
||||
def calculate_volumes():
|
||||
|
|
@ -102,25 +115,42 @@ def hard_reset():
|
|||
_dll.openmc_hard_reset()
|
||||
|
||||
|
||||
def init(intracomm=None):
|
||||
def init(args=None, intracomm=None):
|
||||
"""Initialize OpenMC
|
||||
|
||||
Parameters
|
||||
----------
|
||||
args : list of str
|
||||
Command-line arguments
|
||||
intracomm : mpi4py.MPI.Intracomm or None
|
||||
MPI intracommunicator
|
||||
|
||||
"""
|
||||
if intracomm is not None:
|
||||
# If an mpi4py communicator was passed, convert it to an integer to
|
||||
# be passed to openmc_init
|
||||
try:
|
||||
intracomm = intracomm.py2f()
|
||||
except AttributeError:
|
||||
pass
|
||||
_dll.openmc_init(c_int(intracomm))
|
||||
if args is not None:
|
||||
args = ['openmc'] + list(args)
|
||||
argc = len(args)
|
||||
|
||||
# Create the argv array. Note that it is actually expected to be of
|
||||
# length argc + 1 with the final item being a null pointer.
|
||||
argv = (POINTER(c_char) * (argc + 1))()
|
||||
for i, arg in enumerate(args):
|
||||
argv[i] = create_string_buffer(arg.encode())
|
||||
else:
|
||||
_dll.openmc_init(None)
|
||||
argc = 0
|
||||
argv = None
|
||||
|
||||
if intracomm is not None:
|
||||
# If an mpi4py communicator was passed, convert it to void* to be passed
|
||||
# to openmc_init
|
||||
try:
|
||||
from mpi4py import MPI
|
||||
except ImportError:
|
||||
intracomm = None
|
||||
else:
|
||||
address = MPI._addressof(intracomm)
|
||||
intracomm = c_void_p(address)
|
||||
|
||||
_dll.openmc_init(argc, argv, intracomm)
|
||||
|
||||
|
||||
def iter_batches():
|
||||
|
|
@ -147,13 +177,13 @@ def iter_batches():
|
|||
"""
|
||||
while True:
|
||||
# Run next batch
|
||||
retval = next_batch()
|
||||
status = next_batch()
|
||||
|
||||
# Provide opportunity for user to perform action between batches
|
||||
yield
|
||||
|
||||
# End the iteration
|
||||
if retval < 0:
|
||||
if status != 0:
|
||||
break
|
||||
|
||||
|
||||
|
|
@ -174,18 +204,25 @@ def keff():
|
|||
return tuple(k)
|
||||
else:
|
||||
# Otherwise, return the tracklength estimator
|
||||
mean = c_double.in_dll(_dll, 'keff').value
|
||||
std_dev = c_double.in_dll(_dll, 'keff_std').value if n > 1 else np.inf
|
||||
mean = c_double.in_dll(_dll, 'openmc_keff').value
|
||||
std_dev = c_double.in_dll(_dll, 'openmc_keff_std').value \
|
||||
if n > 1 else np.inf
|
||||
return (mean, std_dev)
|
||||
|
||||
|
||||
def next_batch():
|
||||
"""Run next batch."""
|
||||
retval = _dll.openmc_next_batch()
|
||||
if retval == -3:
|
||||
raise AllocationError('Simulation has not been initialized. You must call '
|
||||
'openmc.capi.simulation_init() first.')
|
||||
return retval
|
||||
"""Run next batch.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Status after running a batch (0=normal, 1=reached maximum number of
|
||||
batches, 2=tally triggers reached)
|
||||
|
||||
"""
|
||||
status = c_int()
|
||||
_dll.openmc_next_batch(status)
|
||||
return status.value
|
||||
|
||||
|
||||
def plot_geometry():
|
||||
|
|
|
|||
|
|
@ -1,45 +1,10 @@
|
|||
from ctypes import c_int, c_char
|
||||
from warnings import warn
|
||||
|
||||
import openmc.exceptions as exc
|
||||
from . import _dll
|
||||
|
||||
|
||||
class OpenMCError(Exception):
|
||||
"""Root exception class for OpenMC."""
|
||||
|
||||
|
||||
class GeometryError(OpenMCError):
|
||||
"""Geometry-related error"""
|
||||
|
||||
|
||||
class InvalidIDError(OpenMCError):
|
||||
"""Use of an ID that is invalid."""
|
||||
|
||||
|
||||
class AllocationError(OpenMCError):
|
||||
"""Error related to memory allocation."""
|
||||
|
||||
|
||||
class OutOfBoundsError(OpenMCError):
|
||||
"""Index in array out of bounds."""
|
||||
|
||||
|
||||
class DataError(OpenMCError):
|
||||
"""Error relating to nuclear data."""
|
||||
|
||||
|
||||
class PhysicsError(OpenMCError):
|
||||
"""Error relating to performing physics."""
|
||||
|
||||
|
||||
class InvalidArgumentError(OpenMCError):
|
||||
"""Argument passed was invalid."""
|
||||
|
||||
|
||||
class InvalidTypeError(OpenMCError):
|
||||
"""Tried to perform an operation on the wrong type."""
|
||||
|
||||
|
||||
def _error_handler(err, func, args):
|
||||
"""Raise exception according to error code."""
|
||||
|
||||
|
|
@ -52,23 +17,23 @@ def _error_handler(err, func, args):
|
|||
msg = errmsg.value.decode()
|
||||
|
||||
# Raise exception type corresponding to error code
|
||||
if err == errcode('e_allocate'):
|
||||
raise AllocationError(msg)
|
||||
elif err == errcode('e_out_of_bounds'):
|
||||
raise OutOfBoundsError(msg)
|
||||
elif err == errcode('e_invalid_argument'):
|
||||
raise InvalidArgumentError(msg)
|
||||
elif err == errcode('e_invalid_type'):
|
||||
raise InvalidTypeError(msg)
|
||||
if err == errcode('e_invalid_id'):
|
||||
raise InvalidIDError(msg)
|
||||
elif err == errcode('e_geometry'):
|
||||
raise GeometryError(msg)
|
||||
elif err == errcode('e_data'):
|
||||
raise DataError(msg)
|
||||
elif err == errcode('e_physics'):
|
||||
raise PhysicsError(msg)
|
||||
elif err == errcode('e_warning'):
|
||||
if err == errcode('OPENMC_E_ALLOCATE'):
|
||||
raise exc.AllocationError(msg)
|
||||
elif err == errcode('OPENMC_E_OUT_OF_BOUNDS'):
|
||||
raise exc.OutOfBoundsError(msg)
|
||||
elif err == errcode('OPENMC_E_INVALID_ARGUMENT'):
|
||||
raise exc.InvalidArgumentError(msg)
|
||||
elif err == errcode('OPENMC_E_INVALID_TYPE'):
|
||||
raise exc.InvalidTypeError(msg)
|
||||
if err == errcode('OPENMC_E_INVALID_ID'):
|
||||
raise exc.InvalidIDError(msg)
|
||||
elif err == errcode('OPENMC_E_GEOMETRY'):
|
||||
raise exc.GeometryError(msg)
|
||||
elif err == errcode('OPENMC_E_DATA'):
|
||||
raise exc.DataError(msg)
|
||||
elif err == errcode('OPENMC_E_PHYSICS'):
|
||||
raise exc.PhysicsError(msg)
|
||||
elif err == errcode('OPENMC_E_WARNING'):
|
||||
warn(msg)
|
||||
elif err < 0:
|
||||
raise OpenMCError("Unknown error encountered (code {}).".format(err))
|
||||
raise exc.OpenMCError("Unknown error encountered (code {}).".format(err))
|
||||
|
|
|
|||
|
|
@ -6,20 +6,27 @@ from weakref import WeakValueDictionary
|
|||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from . import _dll
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler, AllocationError, InvalidIDError
|
||||
from .error import _error_handler
|
||||
from .material import Material
|
||||
from .mesh import Mesh
|
||||
|
||||
|
||||
__all__ = ['Filter', 'AzimuthalFilter', 'CellFilter',
|
||||
'CellbornFilter', 'CellfromFilter', 'DistribcellFilter',
|
||||
'DelayedGroupFilter', 'EnergyFilter', 'EnergyoutFilter',
|
||||
'EnergyFunctionFilter', 'MaterialFilter', 'MeshFilter',
|
||||
'MuFilter', 'PolarFilter', 'SurfaceFilter',
|
||||
'UniverseFilter', 'filters']
|
||||
'EnergyFunctionFilter', 'LegendreFilter', 'MaterialFilter', 'MeshFilter',
|
||||
'MeshSurfaceFilter', 'MuFilter', 'PolarFilter', 'SphericalHarmonicsFilter',
|
||||
'SpatialLegendreFilter', 'SurfaceFilter',
|
||||
'UniverseFilter', 'ZernikeFilter', 'filters']
|
||||
|
||||
# Tally functions
|
||||
_dll.openmc_cell_filter_get_bins.argtypes = [
|
||||
c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)]
|
||||
_dll.openmc_cell_filter_get_bins.restype = c_int
|
||||
_dll.openmc_cell_filter_get_bins.errcheck = _error_handler
|
||||
_dll.openmc_energy_filter_get_bins.argtypes = [
|
||||
c_int32, POINTER(POINTER(c_double)), POINTER(c_int32)]
|
||||
_dll.openmc_energy_filter_get_bins.restype = c_int
|
||||
|
|
@ -45,6 +52,12 @@ _dll.openmc_filter_set_type.errcheck = _error_handler
|
|||
_dll.openmc_get_filter_index.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_get_filter_index.restype = c_int
|
||||
_dll.openmc_get_filter_index.errcheck = _error_handler
|
||||
_dll.openmc_legendre_filter_get_order.argtypes = [c_int32, POINTER(c_int)]
|
||||
_dll.openmc_legendre_filter_get_order.restype = c_int
|
||||
_dll.openmc_legendre_filter_get_order.errcheck = _error_handler
|
||||
_dll.openmc_legendre_filter_set_order.argtypes = [c_int32, c_int]
|
||||
_dll.openmc_legendre_filter_set_order.restype = c_int
|
||||
_dll.openmc_legendre_filter_set_order.errcheck = _error_handler
|
||||
_dll.openmc_material_filter_get_bins.argtypes = [
|
||||
c_int32, POINTER(POINTER(c_int32)), POINTER(c_int32)]
|
||||
_dll.openmc_material_filter_get_bins.restype = c_int
|
||||
|
|
@ -52,10 +65,36 @@ _dll.openmc_material_filter_get_bins.errcheck = _error_handler
|
|||
_dll.openmc_material_filter_set_bins.argtypes = [c_int32, c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_material_filter_set_bins.restype = c_int
|
||||
_dll.openmc_material_filter_set_bins.errcheck = _error_handler
|
||||
_dll.openmc_mesh_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_mesh_filter_get_mesh.restype = c_int
|
||||
_dll.openmc_mesh_filter_get_mesh.errcheck = _error_handler
|
||||
_dll.openmc_mesh_filter_set_mesh.argtypes = [c_int32, c_int32]
|
||||
_dll.openmc_mesh_filter_set_mesh.restype = c_int
|
||||
_dll.openmc_mesh_filter_set_mesh.errcheck = _error_handler
|
||||
|
||||
_dll.openmc_meshsurface_filter_get_mesh.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_meshsurface_filter_get_mesh.restype = c_int
|
||||
_dll.openmc_meshsurface_filter_get_mesh.errcheck = _error_handler
|
||||
_dll.openmc_meshsurface_filter_set_mesh.argtypes = [c_int32, c_int32]
|
||||
_dll.openmc_meshsurface_filter_set_mesh.restype = c_int
|
||||
_dll.openmc_meshsurface_filter_set_mesh.errcheck = _error_handler
|
||||
_dll.openmc_spatial_legendre_filter_get_order.argtypes = [c_int32, POINTER(c_int)]
|
||||
_dll.openmc_spatial_legendre_filter_get_order.restype = c_int
|
||||
_dll.openmc_spatial_legendre_filter_get_order.errcheck = _error_handler
|
||||
_dll.openmc_spatial_legendre_filter_set_order.argtypes = [c_int32, c_int]
|
||||
_dll.openmc_spatial_legendre_filter_set_order.restype = c_int
|
||||
_dll.openmc_spatial_legendre_filter_set_order.errcheck = _error_handler
|
||||
_dll.openmc_sphharm_filter_get_order.argtypes = [c_int32, POINTER(c_int)]
|
||||
_dll.openmc_sphharm_filter_get_order.restype = c_int
|
||||
_dll.openmc_sphharm_filter_get_order.errcheck = _error_handler
|
||||
_dll.openmc_sphharm_filter_set_order.argtypes = [c_int32, c_int]
|
||||
_dll.openmc_sphharm_filter_set_order.restype = c_int
|
||||
_dll.openmc_sphharm_filter_set_order.errcheck = _error_handler
|
||||
_dll.openmc_zernike_filter_get_order.argtypes = [c_int32, POINTER(c_int)]
|
||||
_dll.openmc_zernike_filter_get_order.restype = c_int
|
||||
_dll.openmc_zernike_filter_get_order.errcheck = _error_handler
|
||||
_dll.openmc_zernike_filter_set_order.argtypes = [c_int32, c_int]
|
||||
_dll.openmc_zernike_filter_set_order.restype = c_int
|
||||
_dll.openmc_zernike_filter_set_order.errcheck = _error_handler
|
||||
|
||||
class Filter(_FortranObjectWithID):
|
||||
__instances = WeakValueDictionary()
|
||||
|
|
@ -128,7 +167,7 @@ class EnergyFilter(Filter):
|
|||
self._index, len(energies), energies_p)
|
||||
|
||||
|
||||
class EnergyoutFilter(Filter):
|
||||
class EnergyoutFilter(EnergyFilter):
|
||||
filter_type = 'energyout'
|
||||
|
||||
|
||||
|
|
@ -139,6 +178,13 @@ class AzimuthalFilter(Filter):
|
|||
class CellFilter(Filter):
|
||||
filter_type = 'cell'
|
||||
|
||||
@property
|
||||
def bins(self):
|
||||
cells = POINTER(c_int32)()
|
||||
n = c_int32()
|
||||
_dll.openmc_cell_filter_get_bins(self._index, cells, n)
|
||||
return as_array(cells, (n.value,))
|
||||
|
||||
|
||||
class CellbornFilter(Filter):
|
||||
filter_type = 'cellborn'
|
||||
|
|
@ -160,6 +206,25 @@ class EnergyFunctionFilter(Filter):
|
|||
filter_type = 'energyfunction'
|
||||
|
||||
|
||||
class LegendreFilter(Filter):
|
||||
filter_type = 'legendre'
|
||||
|
||||
def __init__(self, order=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if order is not None:
|
||||
self.order = order
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
temp_order = c_int()
|
||||
_dll.openmc_legendre_filter_get_order(self._index, temp_order)
|
||||
return temp_order.value
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
_dll.openmc_legendre_filter_set_order(self._index, order)
|
||||
|
||||
|
||||
class MaterialFilter(Filter):
|
||||
filter_type = 'material'
|
||||
|
||||
|
|
@ -187,6 +252,40 @@ class MaterialFilter(Filter):
|
|||
class MeshFilter(Filter):
|
||||
filter_type = 'mesh'
|
||||
|
||||
def __init__(self, mesh=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if mesh is not None:
|
||||
self.mesh = mesh
|
||||
|
||||
@property
|
||||
def mesh(self):
|
||||
index_mesh = c_int32()
|
||||
_dll.openmc_mesh_filter_get_mesh(self._index, index_mesh)
|
||||
return Mesh(index=index_mesh.value)
|
||||
|
||||
@mesh.setter
|
||||
def mesh(self, mesh):
|
||||
_dll.openmc_mesh_filter_set_mesh(self._index, mesh._index)
|
||||
|
||||
|
||||
class MeshSurfaceFilter(Filter):
|
||||
filter_type = 'meshsurface'
|
||||
|
||||
def __init__(self, mesh=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if mesh is not None:
|
||||
self.mesh = mesh
|
||||
|
||||
@property
|
||||
def mesh(self):
|
||||
index_mesh = c_int32()
|
||||
_dll.openmc_meshsurface_filter_get_mesh(self._index, index_mesh)
|
||||
return Mesh(index=index_mesh.value)
|
||||
|
||||
@mesh.setter
|
||||
def mesh(self, mesh):
|
||||
_dll.openmc_meshsurface_filter_set_mesh(self._index, mesh._index)
|
||||
|
||||
|
||||
class MuFilter(Filter):
|
||||
filter_type = 'mu'
|
||||
|
|
@ -196,6 +295,44 @@ class PolarFilter(Filter):
|
|||
filter_type = 'polar'
|
||||
|
||||
|
||||
class SphericalHarmonicsFilter(Filter):
|
||||
filter_type = 'sphericalharmonics'
|
||||
|
||||
def __init__(self, order=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if order is not None:
|
||||
self.order = order
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
temp_order = c_int()
|
||||
_dll.openmc_sphharm_filter_get_order(self._index, temp_order)
|
||||
return temp_order.value
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
_dll.openmc_sphharm_filter_set_order(self._index, order)
|
||||
|
||||
|
||||
class SpatialLegendreFilter(Filter):
|
||||
filter_type = 'spatiallegendre'
|
||||
|
||||
def __init__(self, order=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if order is not None:
|
||||
self.order = order
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
temp_order = c_int()
|
||||
_dll.openmc_spatial_legendre_filter_get_order(self._index, temp_order)
|
||||
return temp_order.value
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
_dll.openmc_spatial_legendre_filter_set_order(self._index, order)
|
||||
|
||||
|
||||
class SurfaceFilter(Filter):
|
||||
filter_type = 'surface'
|
||||
|
||||
|
|
@ -204,6 +341,25 @@ class UniverseFilter(Filter):
|
|||
filter_type = 'universe'
|
||||
|
||||
|
||||
class ZernikeFilter(Filter):
|
||||
filter_type = 'zernike'
|
||||
|
||||
def __init__(self, order=None, uid=None, new=True, index=None):
|
||||
super().__init__(uid, new, index)
|
||||
if order is not None:
|
||||
self.order = order
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
temp_order = c_int()
|
||||
_dll.openmc_zernike_filter_get_order(self._index, temp_order)
|
||||
return temp_order.value
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
_dll.openmc_zernike_filter_set_order(self._index, order)
|
||||
|
||||
|
||||
_FILTER_TYPE_MAP = {
|
||||
'azimuthal': AzimuthalFilter,
|
||||
'cell': CellFilter,
|
||||
|
|
@ -214,12 +370,17 @@ _FILTER_TYPE_MAP = {
|
|||
'energy': EnergyFilter,
|
||||
'energyout': EnergyoutFilter,
|
||||
'energyfunction': EnergyFunctionFilter,
|
||||
'legendre': LegendreFilter,
|
||||
'material': MaterialFilter,
|
||||
'mesh': MeshFilter,
|
||||
'meshsurface': MeshSurfaceFilter,
|
||||
'mu': MuFilter,
|
||||
'polar': PolarFilter,
|
||||
'sphericalharmonics': SphericalHarmonicsFilter,
|
||||
'spatiallegendre': SpatialLegendreFilter,
|
||||
'surface': SurfaceFilter,
|
||||
'universe': UniverseFilter,
|
||||
'zernike': ZernikeFilter
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ from weakref import WeakValueDictionary
|
|||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from . import _dll, Nuclide
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler, AllocationError, InvalidIDError
|
||||
from .error import _error_handler
|
||||
|
||||
|
||||
__all__ = ['Material', 'materials']
|
||||
|
|
@ -89,6 +90,9 @@ class Material(_FortranObjectWithID):
|
|||
index = index.value
|
||||
else:
|
||||
index = mapping[uid]._index
|
||||
elif index == -1:
|
||||
# Special value indicates void material
|
||||
return None
|
||||
|
||||
if index not in cls.__instances:
|
||||
instance = super(Material, cls).__new__(cls)
|
||||
|
|
|
|||
250
openmc/capi/math.py
Normal file
250
openmc/capi/math.py
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
from ctypes import (c_int, c_double, POINTER)
|
||||
|
||||
import numpy as np
|
||||
from numpy.ctypeslib import ndpointer
|
||||
|
||||
from . import _dll
|
||||
|
||||
|
||||
_dll.t_percentile_c.restype = c_double
|
||||
_dll.t_percentile_c.argtypes = [c_double, c_int]
|
||||
|
||||
_dll.calc_pn_c.restype = None
|
||||
_dll.calc_pn_c.argtypes = [c_int, c_double, ndpointer(c_double)]
|
||||
|
||||
_dll.evaluate_legendre_c.restype = c_double
|
||||
_dll.evaluate_legendre_c.argtypes = [c_int, POINTER(c_double), c_double]
|
||||
|
||||
_dll.calc_rn_c.restype = None
|
||||
_dll.calc_rn_c.argtypes = [c_int, ndpointer(c_double), ndpointer(c_double)]
|
||||
|
||||
_dll.calc_zn_c.restype = None
|
||||
_dll.calc_zn_c.argtypes = [c_int, c_double, c_double, ndpointer(c_double)]
|
||||
|
||||
_dll.rotate_angle_c.restype = None
|
||||
_dll.rotate_angle_c.argtypes = [ndpointer(c_double), c_double,
|
||||
POINTER(c_double)]
|
||||
_dll.maxwell_spectrum_c.restype = c_double
|
||||
_dll.maxwell_spectrum_c.argtypes = [c_double]
|
||||
|
||||
_dll.watt_spectrum_c.restype = c_double
|
||||
_dll.watt_spectrum_c.argtypes = [c_double, c_double]
|
||||
|
||||
_dll.broaden_wmp_polynomials_c.restype = None
|
||||
_dll.broaden_wmp_polynomials_c.argtypes = [c_double, c_double, c_int,
|
||||
ndpointer(c_double)]
|
||||
|
||||
|
||||
def t_percentile(p, df):
|
||||
""" Calculate the percentile of the Student's t distribution with a
|
||||
specified probability level and number of degrees of freedom
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p : float
|
||||
Probability level
|
||||
df : int
|
||||
Degrees of freedom
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Corresponding t-value
|
||||
|
||||
"""
|
||||
|
||||
return _dll.t_percentile_c(p, df)
|
||||
|
||||
|
||||
def calc_pn(n, x):
|
||||
""" Calculate the n-th order Legendre polynomial at the value of x.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
Legendre order
|
||||
x : float
|
||||
Independent variable to evaluate the Legendre at
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Corresponding Legendre polynomial result
|
||||
|
||||
"""
|
||||
|
||||
pnx = np.empty(n + 1, dtype=np.float64)
|
||||
_dll.calc_pn_c(n, x, pnx)
|
||||
return pnx
|
||||
|
||||
|
||||
def evaluate_legendre(data, x):
|
||||
""" Finds the value of f(x) given a set of Legendre coefficients
|
||||
and the value of x.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : iterable of float
|
||||
Legendre coefficients
|
||||
x : float
|
||||
Independent variable to evaluate the Legendre at
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Corresponding Legendre expansion result
|
||||
|
||||
"""
|
||||
|
||||
data_arr = np.array(data, dtype=np.float64)
|
||||
return _dll.evaluate_legendre_c(len(data),
|
||||
data_arr.ctypes.data_as(POINTER(c_double)),
|
||||
x)
|
||||
|
||||
|
||||
def calc_rn(n, uvw):
|
||||
""" Calculate the n-th order real Spherical Harmonics for a given angle;
|
||||
all Rn,m values are provided for all n (where -n <= m <= n).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
Harmonics order
|
||||
uvw : iterable of float
|
||||
Independent variable to evaluate the Legendre at
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Corresponding real harmonics value
|
||||
|
||||
"""
|
||||
|
||||
num_nm = (n + 1) * (n + 1)
|
||||
rn = np.empty(num_nm, dtype=np.float64)
|
||||
uvw_arr = np.array(uvw, dtype=np.float64)
|
||||
_dll.calc_rn_c(n, uvw_arr, rn)
|
||||
return rn
|
||||
|
||||
|
||||
def calc_zn(n, rho, phi):
|
||||
""" Calculate the n-th order modified Zernike polynomial moment for a
|
||||
given angle (rho, theta) location in the unit disk. The normalization of
|
||||
the polynomials is such that the integral of Z_pq*Z_pq over the unit disk
|
||||
is exactly pi
|
||||
|
||||
Parameters
|
||||
----------
|
||||
n : int
|
||||
Maximum order
|
||||
rho : float
|
||||
Radial location in the unit disk
|
||||
phi : float
|
||||
Theta (radians) location in the unit disk
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Corresponding resulting list of coefficients
|
||||
|
||||
"""
|
||||
|
||||
num_bins = ((n + 1) * (n + 2)) // 2
|
||||
zn = np.zeros(num_bins, dtype=np.float64)
|
||||
_dll.calc_zn_c(n, rho, phi, zn)
|
||||
return zn
|
||||
|
||||
|
||||
def rotate_angle(uvw0, mu, phi=None):
|
||||
""" Rotates direction cosines through a polar angle whose cosine is
|
||||
mu and through an azimuthal angle sampled uniformly.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
uvw0 : iterable of float
|
||||
Original direction cosine
|
||||
mu : float
|
||||
Polar angle cosine to rotate
|
||||
phi : float, optional
|
||||
Azimuthal angle; if None, one will be sampled uniformly
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Rotated direction cosine
|
||||
|
||||
"""
|
||||
|
||||
uvw0_arr = np.array(uvw0, dtype=np.float64)
|
||||
|
||||
if phi is None:
|
||||
_dll.rotate_angle_c(uvw0_arr, mu, None)
|
||||
else:
|
||||
_dll.rotate_angle_c(uvw0_arr, mu, c_double(phi))
|
||||
uvw = uvw0_arr
|
||||
|
||||
return uvw
|
||||
|
||||
|
||||
def maxwell_spectrum(T):
|
||||
""" Samples an energy from the Maxwell fission distribution based
|
||||
on a direct sampling scheme.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
T : float
|
||||
Spectrum parameter
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Sampled outgoing energy
|
||||
|
||||
"""
|
||||
|
||||
return _dll.maxwell_spectrum_c(T)
|
||||
|
||||
|
||||
def watt_spectrum(a, b):
|
||||
""" Samples an energy from the Watt energy-dependent fission spectrum.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
a : float
|
||||
Spectrum parameter a
|
||||
b : float
|
||||
Spectrum parameter b
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
Sampled outgoing energy
|
||||
|
||||
"""
|
||||
|
||||
return _dll.watt_spectrum_c(a, b)
|
||||
|
||||
|
||||
def broaden_wmp_polynomials(E, dopp, n):
|
||||
""" Doppler broadens the windowed multipole curvefit. The curvefit is a
|
||||
polynomial of the form a/E + b/sqrt(E) + c + d sqrt(E) ...
|
||||
|
||||
Parameters
|
||||
----------
|
||||
E : float
|
||||
Energy to evaluate at
|
||||
dopp : float
|
||||
sqrt(atomic weight ratio / kT), with kT given in eV
|
||||
n : int
|
||||
Number of components to the polynomial
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Resultant leading coefficients
|
||||
|
||||
"""
|
||||
|
||||
factors = np.zeros(n, dtype=np.float64)
|
||||
_dll.broaden_wmp_polynomials_c(E, dopp, n, factors)
|
||||
return factors
|
||||
183
openmc/capi/mesh.py
Normal file
183
openmc/capi/mesh.py
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
from collections.abc import Mapping, Iterable
|
||||
from ctypes import c_int, c_int32, c_double, POINTER
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from . import _dll
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler
|
||||
from .material import Material
|
||||
|
||||
__all__ = ['Mesh', 'meshes']
|
||||
|
||||
# Mesh functions
|
||||
_dll.openmc_extend_meshes.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)]
|
||||
_dll.openmc_extend_meshes.restype = c_int
|
||||
_dll.openmc_extend_meshes.errcheck = _error_handler
|
||||
_dll.openmc_mesh_get_id.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_mesh_get_id.restype = c_int
|
||||
_dll.openmc_mesh_get_id.errcheck = _error_handler
|
||||
_dll.openmc_mesh_get_dimension.argtypes = [c_int32, POINTER(POINTER(c_int)), POINTER(c_int)]
|
||||
_dll.openmc_mesh_get_dimension.restype = c_int
|
||||
_dll.openmc_mesh_get_dimension.errcheck = _error_handler
|
||||
_dll.openmc_mesh_get_params.argtypes = [
|
||||
c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)),
|
||||
POINTER(POINTER(c_double)), POINTER(c_int)]
|
||||
_dll.openmc_mesh_get_params.restype = c_int
|
||||
_dll.openmc_mesh_get_params.errcheck = _error_handler
|
||||
_dll.openmc_mesh_set_id.argtypes = [c_int32, c_int32]
|
||||
_dll.openmc_mesh_set_id.restype = c_int
|
||||
_dll.openmc_mesh_set_id.errcheck = _error_handler
|
||||
_dll.openmc_mesh_set_dimension.argtypes = [c_int32, c_int, POINTER(c_int)]
|
||||
_dll.openmc_mesh_set_dimension.restype = c_int
|
||||
_dll.openmc_mesh_set_dimension.errcheck = _error_handler
|
||||
_dll.openmc_mesh_set_params.argtypes = [
|
||||
c_int32, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)]
|
||||
_dll.openmc_mesh_set_params.restype = c_int
|
||||
_dll.openmc_mesh_set_params.errcheck = _error_handler
|
||||
_dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_get_mesh_index.restype = c_int
|
||||
_dll.openmc_get_mesh_index.errcheck = _error_handler
|
||||
|
||||
|
||||
class Mesh(_FortranObjectWithID):
|
||||
"""Mesh stored internally.
|
||||
|
||||
This class exposes a mesh that is stored internally in the OpenMC
|
||||
library. To obtain a view of a mesh with a given ID, use the
|
||||
:data:`openmc.capi.meshes` mapping.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
index : int
|
||||
Index in the `meshes` array.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id : int
|
||||
ID of the mesh
|
||||
dimension : iterable of int
|
||||
The number of mesh cells in each direction.
|
||||
lower_left : numpy.ndarray
|
||||
The lower-left corner of the structured mesh. If only two coordinate are
|
||||
given, it is assumed that the mesh is an x-y mesh.
|
||||
upper_right : numpy.ndarray
|
||||
The upper-right corner of the structrued mesh. If only two coordinate
|
||||
are given, it is assumed that the mesh is an x-y mesh.
|
||||
width : numpy.ndarray
|
||||
The width of mesh cells in each direction.
|
||||
|
||||
"""
|
||||
__instances = WeakValueDictionary()
|
||||
|
||||
def __new__(cls, uid=None, new=True, index=None):
|
||||
mapping = meshes
|
||||
if index is None:
|
||||
if new:
|
||||
# Determine ID to assign
|
||||
if uid is None:
|
||||
uid = max(mapping, default=0) + 1
|
||||
else:
|
||||
if uid in mapping:
|
||||
raise AllocationError('A mesh with ID={} has already '
|
||||
'been allocated.'.format(uid))
|
||||
|
||||
index = c_int32()
|
||||
_dll.openmc_extend_meshes(1, index, None)
|
||||
index = index.value
|
||||
else:
|
||||
index = mapping[uid]._index
|
||||
|
||||
if index not in cls.__instances:
|
||||
instance = super().__new__(cls)
|
||||
instance._index = index
|
||||
if uid is not None:
|
||||
instance.id = uid
|
||||
cls.__instances[index] = instance
|
||||
|
||||
return cls.__instances[index]
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
mesh_id = c_int32()
|
||||
_dll.openmc_mesh_get_id(self._index, mesh_id)
|
||||
return mesh_id.value
|
||||
|
||||
@id.setter
|
||||
def id(self, mesh_id):
|
||||
_dll.openmc_mesh_set_id(self._index, mesh_id)
|
||||
|
||||
@property
|
||||
def dimension(self):
|
||||
dims = POINTER(c_int)()
|
||||
n = c_int()
|
||||
_dll.openmc_mesh_get_dimension(self._index, dims, n)
|
||||
return tuple(as_array(dims, (n.value,)))
|
||||
|
||||
@dimension.setter
|
||||
def dimension(self, dimension):
|
||||
n = len(dimension)
|
||||
dimension = (c_int*n)(*dimension)
|
||||
_dll.openmc_mesh_set_dimension(self._index, n, dimension)
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
return self._get_parameters()[0]
|
||||
|
||||
@property
|
||||
def upper_right(self):
|
||||
return self._get_parameters()[1]
|
||||
|
||||
@property
|
||||
def width(self):
|
||||
return self._get_parameters()[2]
|
||||
|
||||
def _get_parameters(self):
|
||||
ll = POINTER(c_double)()
|
||||
ur = POINTER(c_double)()
|
||||
w = POINTER(c_double)()
|
||||
n = c_int()
|
||||
_dll.openmc_mesh_get_params(self._index, ll, ur, w, n)
|
||||
return (
|
||||
as_array(ll, (n.value,)),
|
||||
as_array(ur, (n.value,)),
|
||||
as_array(w, (n.value,))
|
||||
)
|
||||
|
||||
def set_parameters(self, lower_left=None, upper_right=None, width=None):
|
||||
if lower_left is not None:
|
||||
n = len(lower_left)
|
||||
lower_left = (c_double*n)(*lower_left)
|
||||
if upper_right is not None:
|
||||
n = len(upper_right)
|
||||
upper_right = (c_double*n)(*upper_right)
|
||||
if width is not None:
|
||||
n = len(width)
|
||||
width = (c_double*n)(*width)
|
||||
_dll.openmc_mesh_set_params(self._index, n, lower_left, upper_right, width)
|
||||
|
||||
|
||||
class _MeshMapping(Mapping):
|
||||
def __getitem__(self, key):
|
||||
index = c_int32()
|
||||
try:
|
||||
_dll.openmc_get_mesh_index(key, index)
|
||||
except (AllocationError, InvalidIDError) as e:
|
||||
# __contains__ expects a KeyError to work correctly
|
||||
raise KeyError(str(e))
|
||||
return Mesh(index=index.value)
|
||||
|
||||
def __iter__(self):
|
||||
for i in range(len(self)):
|
||||
yield Mesh(index=i + 1).id
|
||||
|
||||
def __len__(self):
|
||||
return c_int32.in_dll(_dll, 'n_meshes').value
|
||||
|
||||
def __repr__(self):
|
||||
return repr(dict(self))
|
||||
|
||||
meshes = _MeshMapping()
|
||||
|
|
@ -5,9 +5,10 @@ from weakref import WeakValueDictionary
|
|||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
|
||||
from openmc.exceptions import DataError, AllocationError
|
||||
from . import _dll
|
||||
from .core import _FortranObject
|
||||
from .error import _error_handler, DataError, AllocationError
|
||||
from .error import _error_handler
|
||||
|
||||
|
||||
__all__ = ['Nuclide', 'nuclides', 'load_nuclide']
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ class _Settings(object):
|
|||
generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch')
|
||||
inactive = _DLLGlobal(c_int32, 'n_inactive')
|
||||
particles = _DLLGlobal(c_int64, 'n_particles')
|
||||
verbosity = _DLLGlobal(c_int, 'verbosity')
|
||||
verbosity = _DLLGlobal(c_int, 'openmc_verbosity')
|
||||
|
||||
@property
|
||||
def run_mode(self):
|
||||
i = c_int.in_dll(_dll, 'run_mode').value
|
||||
i = c_int.in_dll(_dll, 'openmc_run_mode').value
|
||||
try:
|
||||
return _RUN_MODES[i]
|
||||
except KeyError:
|
||||
|
|
@ -32,7 +32,7 @@ class _Settings(object):
|
|||
|
||||
@run_mode.setter
|
||||
def run_mode(self, mode):
|
||||
current_idx = c_int.in_dll(_dll, 'run_mode')
|
||||
current_idx = c_int.in_dll(_dll, 'openmc_run_mode')
|
||||
for idx, mode_value in _RUN_MODES.items():
|
||||
if mode_value == mode:
|
||||
current_idx.value = idx
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
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, c_bool, POINTER
|
||||
from weakref import WeakValueDictionary
|
||||
|
||||
import numpy as np
|
||||
from numpy.ctypeslib import as_array
|
||||
import scipy.stats
|
||||
|
||||
from openmc.exceptions import AllocationError, InvalidIDError
|
||||
from openmc.data.reaction import REACTION_NAME
|
||||
from . import _dll, Nuclide
|
||||
from .core import _FortranObjectWithID
|
||||
from .error import _error_handler, AllocationError, InvalidIDError
|
||||
from .error import _error_handler
|
||||
from .filter import _get_filter
|
||||
|
||||
|
||||
|
|
@ -25,6 +26,9 @@ _dll.openmc_get_tally_index.errcheck = _error_handler
|
|||
_dll.openmc_global_tallies.argtypes = [POINTER(POINTER(c_double))]
|
||||
_dll.openmc_global_tallies.restype = c_int
|
||||
_dll.openmc_global_tallies.errcheck = _error_handler
|
||||
_dll.openmc_tally_get_active.argtypes = [c_int32, POINTER(c_bool)]
|
||||
_dll.openmc_tally_get_active.restype = c_int
|
||||
_dll.openmc_tally_get_active.errcheck = _error_handler
|
||||
_dll.openmc_tally_get_id.argtypes = [c_int32, POINTER(c_int32)]
|
||||
_dll.openmc_tally_get_id.restype = c_int
|
||||
_dll.openmc_tally_get_id.errcheck = _error_handler
|
||||
|
|
@ -47,6 +51,9 @@ _dll.openmc_tally_results.argtypes = [
|
|||
c_int32, POINTER(POINTER(c_double)), POINTER(c_int*3)]
|
||||
_dll.openmc_tally_results.restype = c_int
|
||||
_dll.openmc_tally_results.errcheck = _error_handler
|
||||
_dll.openmc_tally_set_active.argtypes = [c_int32, c_bool]
|
||||
_dll.openmc_tally_set_active.restype = c_int
|
||||
_dll.openmc_tally_set_active.errcheck = _error_handler
|
||||
_dll.openmc_tally_set_filters.argtypes = [c_int32, c_int, POINTER(c_int32)]
|
||||
_dll.openmc_tally_set_filters.restype = c_int
|
||||
_dll.openmc_tally_set_filters.errcheck = _error_handler
|
||||
|
|
@ -66,10 +73,10 @@ _dll.openmc_tally_set_type.errcheck = _error_handler
|
|||
|
||||
_SCORES = {
|
||||
-1: 'flux', -2: 'total', -3: 'scatter', -4: 'nu-scatter',
|
||||
-9: 'absorption', -10: 'fission', -11: 'nu-fission', -12: 'kappa-fission',
|
||||
-13: 'current', -18: 'events', -19: 'delayed-nu-fission',
|
||||
-20: 'prompt-nu-fission', -21: 'inverse-velocity', -22: 'fission-q-prompt',
|
||||
-23: 'fission-q-recoverable', -24: 'decay-rate'
|
||||
-5: 'absorption', -6: 'fission', -7: 'nu-fission', -8: 'kappa-fission',
|
||||
-9: 'current', -10: 'events', -11: 'delayed-nu-fission',
|
||||
-12: 'prompt-nu-fission', -13: 'inverse-velocity', -14: 'fission-q-prompt',
|
||||
-15: 'fission-q-recoverable', -16: 'decay-rate'
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -177,6 +184,16 @@ class Tally(_FortranObjectWithID):
|
|||
|
||||
return cls.__instances[index]
|
||||
|
||||
@property
|
||||
def active(self):
|
||||
active = c_bool()
|
||||
_dll.openmc_tally_get_active(self._index, active)
|
||||
return active.value
|
||||
|
||||
@active.setter
|
||||
def active(self, active):
|
||||
_dll.openmc_tally_set_active(self._index, active)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
tally_id = c_int32()
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ class Cell(IDManagerMixin):
|
|||
"""
|
||||
if volume_calc.domain_type == 'cell':
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id][0]
|
||||
self._volume = volume_calc.volumes[self.id].n
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this cell.')
|
||||
|
|
@ -335,7 +335,7 @@ class Cell(IDManagerMixin):
|
|||
volume = self.volume
|
||||
for name, atoms in self._atoms.items():
|
||||
nuclide = openmc.Nuclide(name)
|
||||
density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm
|
||||
density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm
|
||||
nuclides[name] = (nuclide, density)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ def check_less_than(name, value, maximum, equality=False):
|
|||
maximum : object
|
||||
Maximum value to check against
|
||||
equality : bool, optional
|
||||
Whether equality is allowed. Defaluts to False.
|
||||
Whether equality is allowed. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -223,7 +223,7 @@ def check_greater_than(name, value, minimum, equality=False):
|
|||
minimum : object
|
||||
Minimum value to check against
|
||||
equality : bool, optional
|
||||
Whether equality is allowed. Defaluts to False.
|
||||
Whether equality is allowed. Defaults to False.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -265,8 +265,9 @@ def check_filetype_version(obj, expected_type, expected_version):
|
|||
if this_version[0] != expected_version:
|
||||
raise IOError('{} file has a version of {} which is not '
|
||||
'consistent with the version expected by OpenMC, {}'
|
||||
.format(this_filetype, '.'.join(this_version),
|
||||
expected_version))
|
||||
.format(this_filetype,
|
||||
'.'.join(str(v) for v in this_version),
|
||||
expected_version))
|
||||
except AttributeError:
|
||||
raise IOError('Could not read {} file. This most likely means the {} '
|
||||
'file was produced by a different version of OpenMC than '
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import numpy as np
|
|||
|
||||
from openmc.mixin import EqualityMixin
|
||||
import openmc.checkvalue as cv
|
||||
from .data import ATOMIC_SYMBOL
|
||||
from .data import ATOMIC_SYMBOL, gnd_name
|
||||
from .endf import ENDF_FLOAT_RE
|
||||
|
||||
|
||||
|
|
@ -88,9 +88,7 @@ def get_metadata(zaid, metastable_scheme='nndc'):
|
|||
|
||||
# Determine name
|
||||
element = ATOMIC_SYMBOL[Z]
|
||||
name = '{}{}'.format(element, mass_number)
|
||||
if metastable > 0:
|
||||
name += '_m{}'.format(metastable)
|
||||
name = gnd_name(Z, mass_number, metastable)
|
||||
|
||||
return (name, element, Z, mass_number, metastable)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
import itertools
|
||||
from math import sqrt
|
||||
import os
|
||||
import re
|
||||
from warnings import warn
|
||||
|
||||
from numpy import sqrt
|
||||
|
||||
|
||||
# Isotopic abundances from Meija J, Coplen T B, et al, "Isotopic compositions
|
||||
# of the elements 2013 (IUPAC Technical Report)", Pure. Appl. Chem. 88 (3),
|
||||
|
|
@ -136,23 +135,24 @@ ATOMIC_NUMBER = {value: key for key, value in ATOMIC_SYMBOL.items()}
|
|||
|
||||
_ATOMIC_MASS = {}
|
||||
|
||||
_GND_NAME_RE = re.compile(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)')
|
||||
|
||||
|
||||
def atomic_mass(isotope):
|
||||
"""Return atomic mass of isotope in atomic mass units.
|
||||
|
||||
Atomic mass data comes from the Atomic Mass Evaluation 2012, published in
|
||||
Chinese Physics C 36 (2012), 1287--1602.
|
||||
Atomic mass data comes from the `Atomic Mass Evaluation 2012
|
||||
<https://www-nds.iaea.org/amdc/ame2012/AME2012-1.pdf>`_.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
isotope : str
|
||||
Name of isotope, e.g. 'Pu239'
|
||||
Name of isotope, e.g., 'Pu239'
|
||||
|
||||
Returns
|
||||
-------
|
||||
float or None
|
||||
Atomic mass of isotope in atomic mass units. If the isotope listed does
|
||||
not have a known atomic mass, None is returned.
|
||||
float
|
||||
Atomic mass of isotope in [amu]
|
||||
|
||||
"""
|
||||
if not _ATOMIC_MASS:
|
||||
|
|
@ -183,7 +183,7 @@ def atomic_mass(isotope):
|
|||
if '_' in isotope:
|
||||
isotope = isotope[:isotope.find('_')]
|
||||
|
||||
return _ATOMIC_MASS.get(isotope.lower())
|
||||
return _ATOMIC_MASS[isotope.lower()]
|
||||
|
||||
|
||||
def atomic_weight(element):
|
||||
|
|
@ -199,16 +199,19 @@ def atomic_weight(element):
|
|||
|
||||
Returns
|
||||
-------
|
||||
float or None
|
||||
Atomic weight of element in atomic mass units. If the element listed does
|
||||
not exist, None is returned.
|
||||
float
|
||||
Atomic weight of element in [amu]
|
||||
|
||||
"""
|
||||
weight = 0.
|
||||
for nuclide, abundance in NATURAL_ABUNDANCE.items():
|
||||
if re.match(r'{}\d+'.format(element), nuclide):
|
||||
weight += atomic_mass(nuclide) * abundance
|
||||
return None if weight == 0. else weight
|
||||
if weight > 0.:
|
||||
return weight
|
||||
else:
|
||||
raise ValueError("No naturally-occurring isotopes for element '{}'."
|
||||
.format(element))
|
||||
|
||||
|
||||
def water_density(temperature, pressure=0.1013):
|
||||
|
|
@ -234,7 +237,7 @@ def water_density(temperature, pressure=0.1013):
|
|||
Returns
|
||||
-------
|
||||
float
|
||||
Water density in units of [g / cm^3]
|
||||
Water density in units of [g/cm^3]
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -352,8 +355,7 @@ def zam(name):
|
|||
|
||||
"""
|
||||
try:
|
||||
symbol, A, state = re.match(r'([A-Zn][a-z]*)(\d+)((?:_[em]\d+)?)',
|
||||
name).groups()
|
||||
symbol, A, state = _GND_NAME_RE.match(name).groups()
|
||||
except AttributeError:
|
||||
raise ValueError("'{}' does not appear to be a nuclide name in GND "
|
||||
"format.".format(name))
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ import re
|
|||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
try:
|
||||
from uncertainties import ufloat, unumpy, UFloat
|
||||
except ImportError:
|
||||
ufloat = UFloat = namedtuple('UFloat', ['nominal_value', 'std_dev'])
|
||||
from uncertainties import ufloat, unumpy, UFloat
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from openmc.mixin import EqualityMixin
|
||||
|
|
|
|||
|
|
@ -23,9 +23,14 @@ class DataLibrary(EqualityMixin):
|
|||
def __init__(self):
|
||||
self.libraries = []
|
||||
|
||||
def get_by_material(self, value):
|
||||
def get_by_material(self, name):
|
||||
"""Return the library dictionary containing a given material.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of material, e.g. 'Am241'
|
||||
|
||||
Returns
|
||||
-------
|
||||
library : dict or None
|
||||
|
|
@ -34,7 +39,7 @@ class DataLibrary(EqualityMixin):
|
|||
|
||||
"""
|
||||
for library in self.libraries:
|
||||
if value in library['materials']:
|
||||
if name in library['materials']:
|
||||
return library
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ _RM_RF = 3 # Residue fission
|
|||
|
||||
# Multi-level Breit Wigner indices
|
||||
_MLBW_RT = 1 # Residue total
|
||||
_MLBW_RX = 2 # Residue compettitive
|
||||
_MLBW_RX = 2 # Residue competitive
|
||||
_MLBW_RA = 3 # Residue absorption
|
||||
_MLBW_RF = 4 # Residue fission
|
||||
|
||||
|
|
@ -141,6 +141,12 @@ def _broaden_wmp_polynomials(E, dopp, n):
|
|||
class WindowedMultipole(EqualityMixin):
|
||||
"""Resonant cross sections represented in the windowed multipole format.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
formalism : {'MLBW', 'RM'}
|
||||
The R-matrix formalism used to reconstruct resonances. Either 'MLBW'
|
||||
for multi-level Breit Wigner or 'RM' for Reich-Moore.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
num_l : Integral
|
||||
|
|
@ -195,11 +201,9 @@ class WindowedMultipole(EqualityMixin):
|
|||
a/E + b/sqrt(E) + c + d sqrt(E) + ...
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.num_l = None
|
||||
self.fit_order = None
|
||||
self.fissionable = None
|
||||
self.formalism = None
|
||||
def __init__(self, formalism):
|
||||
self._num_l = None
|
||||
self.formalism = formalism
|
||||
self.spacing = None
|
||||
self.sqrtAWR = None
|
||||
self.start_E = None
|
||||
|
|
@ -218,11 +222,15 @@ class WindowedMultipole(EqualityMixin):
|
|||
|
||||
@property
|
||||
def fit_order(self):
|
||||
return self._fit_order
|
||||
return self.curvefit.shape[1] - 1
|
||||
|
||||
@property
|
||||
def fissionable(self):
|
||||
return self._fissionable
|
||||
if self.formalism == 'RM':
|
||||
return self.data.shape[1] == 4
|
||||
else:
|
||||
# Assume self.formalism == 'MLBW'
|
||||
return self.data.shape[1] == 5
|
||||
|
||||
@property
|
||||
def formalism(self):
|
||||
|
|
@ -272,35 +280,10 @@ class WindowedMultipole(EqualityMixin):
|
|||
def curvefit(self):
|
||||
return self._curvefit
|
||||
|
||||
@num_l.setter
|
||||
def num_l(self, num_l):
|
||||
if num_l is not None:
|
||||
cv.check_type('num_l', num_l, Integral)
|
||||
cv.check_greater_than('num_l', num_l, 1, equality=True)
|
||||
cv.check_less_than('num_l', num_l, 4, equality=True)
|
||||
# There is an if block in _evaluate that assumes num_l <= 4.
|
||||
self._num_l = num_l
|
||||
|
||||
@fit_order.setter
|
||||
def fit_order(self, fit_order):
|
||||
if fit_order is not None:
|
||||
cv.check_type('fit_order', fit_order, Integral)
|
||||
cv.check_greater_than('fit_order', fit_order, 2, equality=True)
|
||||
# _broaden_wmp_polynomials assumes the curve fit has at least 3
|
||||
# terms.
|
||||
self._fit_order = fit_order
|
||||
|
||||
@fissionable.setter
|
||||
def fissionable(self, fissionable):
|
||||
if fissionable is not None:
|
||||
cv.check_type('fissionable', fissionable, bool)
|
||||
self._fissionable = fissionable
|
||||
|
||||
@formalism.setter
|
||||
def formalism(self, formalism):
|
||||
if formalism is not None:
|
||||
cv.check_type('formalism', formalism, str)
|
||||
cv.check_value('formalism', formalism, ('MLBW', 'RM'))
|
||||
cv.check_type('formalism', formalism, str)
|
||||
cv.check_value('formalism', formalism, ('MLBW', 'RM'))
|
||||
self._formalism = formalism
|
||||
|
||||
@spacing.setter
|
||||
|
|
@ -337,9 +320,20 @@ class WindowedMultipole(EqualityMixin):
|
|||
cv.check_type('data', data, np.ndarray)
|
||||
if len(data.shape) != 2:
|
||||
raise ValueError('Multipole data arrays must be 2D')
|
||||
if data.shape[1] not in (3, 4, 5): # 3 or 4 for RM, 4 or 5 for MLBW
|
||||
raise ValueError('The second dimension of multipole data arrays'
|
||||
' must have a length of 3, 4 or 5')
|
||||
if self.formalism == 'RM':
|
||||
if data.shape[1] not in (3, 4):
|
||||
raise ValueError('For the Reich-Moore formalism, '
|
||||
'data.shape[1] must be 3 or 4. One value for the pole.'
|
||||
' One each for the total and absorption residues. '
|
||||
'Possibly one more for a fission residue.')
|
||||
else:
|
||||
# Assume self.formalism == 'MLBW'
|
||||
if data.shape[1] not in (4, 5):
|
||||
raise ValueError('For the Multi-level Breit-Wigner '
|
||||
'formalism, data.shape[1] must be 4 or 5. One value '
|
||||
'for the pole. One each for the total, competitive, '
|
||||
'and absorption residues. Possibly one more for a '
|
||||
'fission residue.')
|
||||
if not np.issubdtype(data.dtype, complex):
|
||||
raise TypeError('Multipole data arrays must be complex dtype')
|
||||
self._data = data
|
||||
|
|
@ -363,6 +357,12 @@ class WindowedMultipole(EqualityMixin):
|
|||
if not np.issubdtype(l_value.dtype, int):
|
||||
raise TypeError('Multipole l_value arrays must be integer'
|
||||
' dtype')
|
||||
|
||||
self._num_l = len(np.unique(l_value))
|
||||
|
||||
else:
|
||||
self._num_l = None
|
||||
|
||||
self._l_value = l_value
|
||||
|
||||
@w_start.setter
|
||||
|
|
@ -428,6 +428,7 @@ class WindowedMultipole(EqualityMixin):
|
|||
format.
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(group_or_filename, h5py.Group):
|
||||
group = group_or_filename
|
||||
else:
|
||||
|
|
@ -442,20 +443,12 @@ class WindowedMultipole(EqualityMixin):
|
|||
'Python API expects version ' + WMP_VERSION)
|
||||
group = h5file['nuclide']
|
||||
|
||||
out = cls()
|
||||
|
||||
# Read scalar values. Note that group['max_w'] is ignored.
|
||||
|
||||
length = group['length'].value
|
||||
windows = group['windows'].value
|
||||
out.num_l = group['num_l'].value
|
||||
out.fit_order = group['fit_order'].value
|
||||
out.fissionable = bool(group['fissionable'].value)
|
||||
# Read scalars.
|
||||
|
||||
if group['formalism'].value == _FORM_MLBW:
|
||||
out.formalism = 'MLBW'
|
||||
out = cls('MLBW')
|
||||
elif group['formalism'].value == _FORM_RM:
|
||||
out.formalism = 'RM'
|
||||
out = cls('RM')
|
||||
else:
|
||||
raise ValueError('Unrecognized/Unsupported R-matrix formalism')
|
||||
|
||||
|
|
@ -466,39 +459,36 @@ class WindowedMultipole(EqualityMixin):
|
|||
|
||||
# Read arrays.
|
||||
|
||||
err = "WMP '{}' array shape is not consistent with the '{}' value"
|
||||
err = "WMP '{}' array shape is not consistent with the '{}' array shape"
|
||||
|
||||
out.data = group['data'].value
|
||||
if out.data.shape[0] != length:
|
||||
raise ValueError(err.format('data', 'length'))
|
||||
|
||||
out.l_value = group['l_value'].value
|
||||
if out.l_value.shape[0] != out.data.shape[0]:
|
||||
raise ValueError(err.format('l_value', 'data'))
|
||||
|
||||
out.pseudo_k0RS = group['pseudo_K0RS'].value
|
||||
if out.pseudo_k0RS.shape[0] != out.num_l:
|
||||
raise ValueError(err.format('pseudo_k0RS', 'num_l'))
|
||||
|
||||
out.l_value = group['l_value'].value
|
||||
if out.l_value.shape[0] != length:
|
||||
raise ValueError(err.format('l_value', 'length'))
|
||||
raise ValueError(err.format('pseudo_k0RS', 'l_value'))
|
||||
|
||||
out.w_start = group['w_start'].value
|
||||
if out.w_start.shape[0] != windows:
|
||||
raise ValueError(err.format('w_start', 'windows'))
|
||||
|
||||
out.w_end = group['w_end'].value
|
||||
if out.w_end.shape[0] != windows:
|
||||
raise ValueError(err.format('w_end', 'windows'))
|
||||
if out.w_end.shape[0] != out.w_start.shape[0]:
|
||||
raise ValueError(err.format('w_end', 'w_start'))
|
||||
|
||||
out.broaden_poly = group['broaden_poly'].value.astype(np.bool)
|
||||
if out.broaden_poly.shape[0] != windows:
|
||||
raise ValueError(err.format('broaden_poly', 'windows'))
|
||||
if out.broaden_poly.shape[0] != out.w_start.shape[0]:
|
||||
raise ValueError(err.format('broaden_poly', 'w_start'))
|
||||
|
||||
out.curvefit = group['curvefit'].value
|
||||
if out.curvefit.shape[0] != windows:
|
||||
raise ValueError(err.format('curvefit', 'windows'))
|
||||
if out.curvefit.shape[1] != out.fit_order + 1:
|
||||
raise ValueError(err.format('curvefit', 'fit_order'))
|
||||
if out.curvefit.shape[0] != out.w_start.shape[0]:
|
||||
raise ValueError(err.format('curvefit', 'w_start'))
|
||||
|
||||
# Note that all the file 3 data (group['reactions/MT...']) are ignored.
|
||||
# _broaden_wmp_polynomials assumes the curve fit has at least 3 terms.
|
||||
if out.fit_order < 2:
|
||||
raise ValueError("Windowed multipole is only supported for "
|
||||
"curvefits with 3 or more terms.")
|
||||
|
||||
return out
|
||||
|
||||
|
|
@ -661,3 +651,47 @@ class WindowedMultipole(EqualityMixin):
|
|||
|
||||
fun = np.vectorize(lambda x: self._evaluate(x, T))
|
||||
return fun(E)
|
||||
|
||||
def export_to_hdf5(self, path, libver='earliest'):
|
||||
"""Export windowed multipole data to an HDF5 file.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path : str
|
||||
Path to write HDF5 file to
|
||||
libver : {'earliest', 'latest'}
|
||||
Compatibility mode for the HDF5 file. 'latest' will produce files
|
||||
that are less backwards compatible but have performance benefits.
|
||||
|
||||
"""
|
||||
|
||||
# Open file and write version.
|
||||
with h5py.File(path, 'w', libver=libver) as f:
|
||||
f.create_dataset('version', (1, ), dtype='S10')
|
||||
f['version'][:] = WMP_VERSION.encode('ASCII')
|
||||
|
||||
# Make a nuclide group.
|
||||
g = f.create_group('nuclide')
|
||||
|
||||
# Write scalars.
|
||||
if self.formalism == 'MLBW':
|
||||
g.create_dataset('formalism',
|
||||
data=np.array(_FORM_MLBW, dtype=np.int32))
|
||||
else:
|
||||
# Assume RM.
|
||||
g.create_dataset('formalism',
|
||||
data=np.array(_FORM_RM, dtype=np.int32))
|
||||
g.create_dataset('spacing', data=np.array(self.spacing))
|
||||
g.create_dataset('sqrtAWR', data=np.array(self.sqrtAWR))
|
||||
g.create_dataset('start_E', data=np.array(self.start_E))
|
||||
g.create_dataset('end_E', data=np.array(self.end_E))
|
||||
|
||||
# Write arrays.
|
||||
g.create_dataset('data', data=self.data)
|
||||
g.create_dataset('l_value', data=self.l_value)
|
||||
g.create_dataset('pseudo_K0RS', data=self.pseudo_k0RS)
|
||||
g.create_dataset('w_start', data=self.w_start)
|
||||
g.create_dataset('w_end', data=self.w_end)
|
||||
g.create_dataset('broaden_poly',
|
||||
data=self.broaden_poly.astype(np.int8))
|
||||
g.create_dataset('curvefit', data=self.curvefit)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class IncidentNeutron(EqualityMixin):
|
|||
sublibrary. Instances of this class are not normally instantiated by the
|
||||
user but rather created using the factory methods
|
||||
:meth:`IncidentNeutron.from_hdf5`, :meth:`IncidentNeutron.from_ace`, and
|
||||
:math:`IncidentNeutron.from_endf`.
|
||||
:meth:`IncidentNeutron.from_endf`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -47,11 +47,42 @@ def cecm(operator, timesteps, power, print_out=True):
|
|||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
chain = operator.chain
|
||||
t = 0.0
|
||||
|
||||
# Initialize time
|
||||
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:
|
||||
i_res = len(operator.prev_res)
|
||||
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep reaction rates
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], p)]
|
||||
# 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[0] *= ratio_power[0]
|
||||
|
||||
# Deplete for first half of timestep
|
||||
x_middle = deplete(chain, x[0], op_results[0], dt/2, print_out)
|
||||
|
|
@ -61,10 +92,11 @@ def cecm(operator, timesteps, power, print_out=True):
|
|||
op_results.append(operator(x_middle, p))
|
||||
|
||||
# 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)
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], i)
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
|
||||
# Advance time, update vector
|
||||
t += dt
|
||||
|
|
@ -75,4 +107,4 @@ def cecm(operator, timesteps, power, print_out=True):
|
|||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], len(timesteps))
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
|
|
|||
|
|
@ -42,14 +42,41 @@ def predictor(operator, timesteps, power, print_out=True):
|
|||
# Generate initial conditions
|
||||
with operator as vec:
|
||||
chain = operator.chain
|
||||
t = 0.0
|
||||
for i, (dt, p) in enumerate(zip(timesteps, power)):
|
||||
# Get beginning-of-timestep reaction rates
|
||||
x = [copy.deepcopy(vec)]
|
||||
op_results = [operator(x[0], p)]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], i)
|
||||
# Initialize time
|
||||
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:
|
||||
i_res = len(operator.prev_res) - 1
|
||||
|
||||
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)]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t + dt], p, i_res + i)
|
||||
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]
|
||||
|
||||
# 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]
|
||||
|
||||
# Deplete for full timestep
|
||||
x_end = deplete(chain, x[0], op_results[0], dt, print_out)
|
||||
|
|
@ -63,4 +90,4 @@ def predictor(operator, timesteps, power, print_out=True):
|
|||
op_results = [operator(x[0], power[-1])]
|
||||
|
||||
# Create results, write to disk
|
||||
Results.save(operator, x, op_results, [t, t], len(timesteps))
|
||||
Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps))
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@ class Operator(TransportOperator):
|
|||
chain_file : str, optional
|
||||
Path to the depletion chain XML file. Defaults to the
|
||||
:envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists.
|
||||
prev_results : ResultsList, optional
|
||||
Results from a previous depletion calculation. If this argument is
|
||||
specified, the depletion calculation will start from the latest state
|
||||
in the previous results.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -75,7 +79,7 @@ class Operator(TransportOperator):
|
|||
OpenMC settings object
|
||||
dilute_initial : float
|
||||
Initial atom density to add for nuclides that are zero in initial
|
||||
condition to ensure they exist in the decay chain. Only done for
|
||||
condition to ensure they exist in the decay chain. Only done for
|
||||
nuclides with reaction rates. Defaults to 1.0e3.
|
||||
output_dir : pathlib.Path
|
||||
Path to output directory to save results.
|
||||
|
|
@ -94,14 +98,25 @@ class Operator(TransportOperator):
|
|||
All burnable material IDs
|
||||
local_mats : list of str
|
||||
All burnable material IDs being managed by a single process
|
||||
prev_res : ResultsList
|
||||
Results from a previous depletion calculation
|
||||
|
||||
"""
|
||||
def __init__(self, geometry, settings, chain_file=None):
|
||||
def __init__(self, geometry, settings, chain_file=None, prev_results=None):
|
||||
super().__init__(chain_file)
|
||||
self.round_number = False
|
||||
self.settings = settings
|
||||
self.geometry = geometry
|
||||
|
||||
if prev_results != None:
|
||||
# Reload volumes into geometry
|
||||
prev_results[-1].transfer_volumes(geometry)
|
||||
|
||||
# Store previous results in operator
|
||||
self.prev_res = prev_results
|
||||
else:
|
||||
self.prev_res = None
|
||||
|
||||
# Clear out OpenMC, create task lists, distribute
|
||||
openmc.reset_auto_ids()
|
||||
self.burnable_mats, volume, nuclides = self._get_burnable_mats()
|
||||
|
|
@ -109,16 +124,19 @@ class Operator(TransportOperator):
|
|||
|
||||
# Determine which nuclides have incident neutron data
|
||||
self.nuclides_with_data = self._get_nuclides_with_data()
|
||||
self._burnable_nucs = [nuc for nuc in self.nuclides_with_data
|
||||
if nuc in self.chain]
|
||||
|
||||
# Extract number densities from the geometry
|
||||
self._extract_number(self.local_mats, volume, nuclides)
|
||||
# Select nuclides with data that are also in the chain
|
||||
self._burnable_nucs = [nuc.name for nuc in self.chain.nuclides
|
||||
if nuc.name in self.nuclides_with_data]
|
||||
|
||||
# Extract number densities from the geometry / previous depletion run
|
||||
self._extract_number(self.local_mats, volume, nuclides, self.prev_res)
|
||||
|
||||
# Create reaction rates array
|
||||
self.reaction_rates = ReactionRates(
|
||||
self.local_mats, self._burnable_nucs, self.chain.reactions)
|
||||
|
||||
|
||||
def __call__(self, vec, power, print_out=True):
|
||||
"""Runs a simulation.
|
||||
|
||||
|
|
@ -212,7 +230,7 @@ class Operator(TransportOperator):
|
|||
|
||||
return burnable_mats, volume, nuclides
|
||||
|
||||
def _extract_number(self, local_mats, volume, nuclides):
|
||||
def _extract_number(self, local_mats, volume, nuclides, prev_res=None):
|
||||
"""Construct AtomNumber using geometry
|
||||
|
||||
Parameters
|
||||
|
|
@ -223,6 +241,8 @@ class Operator(TransportOperator):
|
|||
Volumes for the above materials in [cm^3]
|
||||
nuclides : list of str
|
||||
Nuclides to be used in the simulation.
|
||||
prev_res : ResultsList, optional
|
||||
Results from a previous depletion calculation
|
||||
|
||||
"""
|
||||
self.number = AtomNumber(local_mats, nuclides, volume, len(self.chain))
|
||||
|
|
@ -231,10 +251,18 @@ class Operator(TransportOperator):
|
|||
for nuc in self._burnable_nucs:
|
||||
self.number.set_atom_density(np.s_[:], nuc, self.dilute_initial)
|
||||
|
||||
# Now extract the number densities and store
|
||||
for mat in self.geometry.get_all_materials().values():
|
||||
if str(mat.id) in local_mats:
|
||||
self._set_number_from_mat(mat)
|
||||
# Now extract and store the number densities
|
||||
# From the geometry if no previous depletion results
|
||||
if prev_res is None:
|
||||
for mat in self.geometry.get_all_materials().values():
|
||||
if str(mat.id) in local_mats:
|
||||
self._set_number_from_mat(mat)
|
||||
|
||||
# Else from previous depletion results
|
||||
else:
|
||||
for mat in self.geometry.get_all_materials().values():
|
||||
if str(mat.id) in local_mats:
|
||||
self._set_number_from_results(mat, prev_res)
|
||||
|
||||
def _set_number_from_mat(self, mat):
|
||||
"""Extracts material and number densities from openmc.Material
|
||||
|
|
@ -251,6 +279,41 @@ class Operator(TransportOperator):
|
|||
number = density * 1.0e24
|
||||
self.number.set_atom_density(mat_id, nuclide, number)
|
||||
|
||||
def _set_number_from_results(self, mat, prev_res):
|
||||
"""Extracts material nuclides and number densities.
|
||||
|
||||
If the nuclide concentration's evolution is tracked, the densities come
|
||||
from depletion results. Else, densities are extracted from the geometry
|
||||
in the summary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mat : openmc.Material
|
||||
The material to read from
|
||||
prev_res : ResultsList
|
||||
Results from a previous depletion calculation
|
||||
|
||||
"""
|
||||
mat_id = str(mat.id)
|
||||
|
||||
# Get nuclide lists from geometry and depletion results
|
||||
depl_nuc = prev_res[-1].nuc_to_ind
|
||||
geom_nuc_densities = mat.get_nuclide_atom_densities()
|
||||
|
||||
# Merge lists of nuclides, with the same order for every calculation
|
||||
geom_nuc_densities.update(depl_nuc)
|
||||
|
||||
for nuclide in geom_nuc_densities.keys():
|
||||
if nuclide in depl_nuc:
|
||||
concentration = prev_res.get_atoms(mat_id, nuclide)[1][-1]
|
||||
volume = prev_res[-1].volume[mat_id]
|
||||
number = concentration / volume
|
||||
else:
|
||||
density = geom_nuc_densities[nuclide][1]
|
||||
number = density * 1.0e24
|
||||
|
||||
self.number.set_atom_density(mat_id, nuclide, number)
|
||||
|
||||
def initial_condition(self):
|
||||
"""Performs final setup and returns initial condition.
|
||||
|
||||
|
|
@ -268,7 +331,7 @@ class Operator(TransportOperator):
|
|||
|
||||
# Initialize OpenMC library
|
||||
comm.barrier()
|
||||
openmc.capi.init(comm)
|
||||
openmc.capi.init(intracomm=comm)
|
||||
|
||||
# Generate tallies in memory
|
||||
self._generate_tallies()
|
||||
|
|
@ -306,15 +369,19 @@ class Operator(TransportOperator):
|
|||
densities.append(val)
|
||||
else:
|
||||
# Only output warnings if values are significantly
|
||||
# negative. CRAM does not guarantee positive values.
|
||||
# negative. CRAM does not guarantee positive values.
|
||||
if val < -1.0e-21:
|
||||
print("WARNING: nuclide ", nuc, " in material ", mat,
|
||||
" is negative (density = ", val, " at/barn-cm)")
|
||||
number_i[mat, nuc] = 0.0
|
||||
|
||||
# Update densities on C API side
|
||||
mat_internal = openmc.capi.materials[int(mat)]
|
||||
mat_internal.set_densities(nuclides, densities)
|
||||
|
||||
#TODO Update densities on the Python side, otherwise the
|
||||
# summary.h5 file contains densities at the first time step
|
||||
|
||||
def _generate_materials_xml(self):
|
||||
"""Creates materials.xml from self.number.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class ReactionRates(np.ndarray):
|
|||
Depletable nuclides
|
||||
reactions : list of str
|
||||
Transmutation reactions being tracked
|
||||
from_results : boolean
|
||||
If the reaction rates are loaded from results, indexing dictionnaries
|
||||
need to be kept the same.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
|
@ -47,16 +50,24 @@ class ReactionRates(np.ndarray):
|
|||
# the __array_finalize__ method (discussed here:
|
||||
# https://docs.scipy.org/doc/numpy/user/basics.subclassing.html)
|
||||
|
||||
def __new__(cls, local_mats, nuclides, reactions):
|
||||
def __new__(cls, local_mats, nuclides, reactions, from_results=False):
|
||||
# Create appropriately-sized zeroed-out ndarray
|
||||
shape = (len(local_mats), len(nuclides), len(reactions))
|
||||
obj = super().__new__(cls, shape)
|
||||
obj[:] = 0.0
|
||||
|
||||
# Add mapping attributes
|
||||
obj.index_mat = {mat: i for i, mat in enumerate(local_mats)}
|
||||
obj.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)}
|
||||
obj.index_rx = {rx: i for i, rx in enumerate(reactions)}
|
||||
# Add mapping attributes, keep same indexing if from depletion_results
|
||||
if from_results:
|
||||
obj.index_mat = local_mats
|
||||
obj.index_nuc = nuclides
|
||||
obj.index_rx = reactions
|
||||
# Else, assumes that reaction rates are ordered the same way as
|
||||
# the lists of local_mats, nuclides and reactions (or keys if these
|
||||
# are dictionnaries)
|
||||
else:
|
||||
obj.index_mat = {mat: i for i, mat in enumerate(local_mats)}
|
||||
obj.index_nuc = {nuc: i for i, nuc in enumerate(nuclides)}
|
||||
obj.index_rx = {rx: i for i, rx in enumerate(reactions)}
|
||||
|
||||
return obj
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ Contains results generation and saving capabilities.
|
|||
|
||||
from collections import OrderedDict
|
||||
import copy
|
||||
from warnings import warn
|
||||
|
||||
import numpy as np
|
||||
import h5py
|
||||
|
|
@ -24,6 +25,8 @@ class Results(object):
|
|||
Eigenvalue for each substep.
|
||||
time : list of float
|
||||
Time at beginning, end of step, in seconds.
|
||||
power : float
|
||||
Power during time step, in Watts
|
||||
n_mat : int
|
||||
Number of mats.
|
||||
n_nuc : int
|
||||
|
|
@ -49,6 +52,7 @@ class Results(object):
|
|||
def __init__(self):
|
||||
self.k = None
|
||||
self.time = None
|
||||
self.power = None
|
||||
self.rates = None
|
||||
self.volume = None
|
||||
|
||||
|
|
@ -161,6 +165,7 @@ class Results(object):
|
|||
else:
|
||||
kwargs = {}
|
||||
|
||||
# Write new file if first time step, else add to existing file
|
||||
kwargs['mode'] = "w" if step == 0 else "a"
|
||||
|
||||
with h5py.File(filename, **kwargs) as handle:
|
||||
|
|
@ -237,6 +242,9 @@ class Results(object):
|
|||
|
||||
handle.create_dataset("time", (1, 2), maxshape=(None, 2), dtype='float64')
|
||||
|
||||
handle.create_dataset("power", (1, n_stages), maxshape=(None, n_stages),
|
||||
dtype='float64')
|
||||
|
||||
def _to_hdf5(self, handle, index):
|
||||
"""Converts results object into an hdf5 object.
|
||||
|
||||
|
|
@ -259,6 +267,7 @@ class Results(object):
|
|||
rxn_dset = handle["/reaction rates"]
|
||||
eigenvalues_dset = handle["/eigenvalues"]
|
||||
time_dset = handle["/time"]
|
||||
power_dset = handle["/power"]
|
||||
|
||||
# Get number of results stored
|
||||
number_shape = list(number_dset.shape)
|
||||
|
|
@ -283,6 +292,10 @@ class Results(object):
|
|||
time_shape[0] = new_shape
|
||||
time_dset.resize(time_shape)
|
||||
|
||||
power_shape = list(power_dset.shape)
|
||||
power_shape[0] = new_shape
|
||||
power_dset.resize(power_shape)
|
||||
|
||||
# If nothing to write, just return
|
||||
if len(self.mat_to_ind) == 0:
|
||||
return
|
||||
|
|
@ -300,6 +313,7 @@ class Results(object):
|
|||
eigenvalues_dset[index, i] = self.k[i]
|
||||
if comm.rank == 0:
|
||||
time_dset[index, :] = self.time
|
||||
power_dset[index, :] = self.power
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, handle, step):
|
||||
|
|
@ -319,10 +333,12 @@ class Results(object):
|
|||
number_dset = handle["/number"]
|
||||
eigenvalues_dset = handle["/eigenvalues"]
|
||||
time_dset = handle["/time"]
|
||||
power_dset = handle["/power"]
|
||||
|
||||
results.data = number_dset[step, :, :, :]
|
||||
results.k = eigenvalues_dset[step, :]
|
||||
results.time = time_dset[step, :]
|
||||
results.power = power_dset[step, :]
|
||||
|
||||
# Reconstruct dictionaries
|
||||
results.volume = OrderedDict()
|
||||
|
|
@ -351,7 +367,7 @@ class Results(object):
|
|||
results.rates = []
|
||||
# Reconstruct reactions
|
||||
for i in range(results.n_stages):
|
||||
rate = ReactionRates(results.mat_to_ind, rxn_nuc_to_ind, rxn_to_ind)
|
||||
rate = ReactionRates(results.mat_to_ind, rxn_nuc_to_ind, rxn_to_ind, True)
|
||||
|
||||
rate[:] = handle["/reaction rates"][step, i, :, :, :]
|
||||
results.rates.append(rate)
|
||||
|
|
@ -359,7 +375,7 @@ class Results(object):
|
|||
return results
|
||||
|
||||
@staticmethod
|
||||
def save(op, x, op_results, t, step_ind):
|
||||
def save(op, x, op_results, t, power, step_ind):
|
||||
"""Creates and writes depletion results to disk
|
||||
|
||||
Parameters
|
||||
|
|
@ -372,6 +388,8 @@ class Results(object):
|
|||
Results of applying transport operator
|
||||
t : list of float
|
||||
Time indices.
|
||||
power : float
|
||||
Power during time step
|
||||
step_ind : int
|
||||
Step index.
|
||||
|
||||
|
|
@ -379,8 +397,18 @@ class Results(object):
|
|||
# Get indexing terms
|
||||
vol_dict, nuc_list, burn_list, full_burn_list = op.get_results_info()
|
||||
|
||||
# Create results
|
||||
# For a restart calculation, limit number of stages saved to meet the
|
||||
# format of the hdf5 file
|
||||
stages = len(x)
|
||||
offset = 0
|
||||
if op.prev_res is not None and op.prev_res[0].n_stages < stages:
|
||||
offset = stages - op.prev_res[0].n_stages
|
||||
stages = min(stages, op.prev_res[0].n_stages)
|
||||
warn("Number of restart integrator stages saved limited by initial"
|
||||
" depletion integrator choice to {}"
|
||||
.format(op.prev_res[0].n_stages))
|
||||
|
||||
# Create results
|
||||
results = Results()
|
||||
results.allocate(vol_dict, nuc_list, burn_list, full_burn_list, stages)
|
||||
|
||||
|
|
@ -388,10 +416,25 @@ class Results(object):
|
|||
|
||||
for i in range(stages):
|
||||
for mat_i in range(n_mat):
|
||||
results[i, mat_i, :] = x[i][mat_i][:]
|
||||
results[i, mat_i, :] = x[offset + i][mat_i][:]
|
||||
|
||||
results.k = [r.k for r in op_results]
|
||||
results.rates = [r.rates for r in op_results]
|
||||
results.time = t
|
||||
results.power = power
|
||||
|
||||
results.export_to_hdf5("depletion_results.h5", step_ind)
|
||||
|
||||
def transfer_volumes(self, geometry):
|
||||
"""Transfers volumes from depletion results to geometry
|
||||
|
||||
Parameters
|
||||
----------
|
||||
geometry : OpenMC geometry to be used in a depletion restart
|
||||
calculation
|
||||
|
||||
"""
|
||||
for cell in geometry.get_all_material_cells().values():
|
||||
for material in cell.get_all_materials().values():
|
||||
if material.depletable:
|
||||
material.volume = self.volume[str(material.id)]
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ class ResultsList(list):
|
|||
Total number of atoms for specified nuclide
|
||||
|
||||
"""
|
||||
time = np.empty_like(self)
|
||||
concentration = np.empty_like(self)
|
||||
time = np.empty_like(self, dtype=float)
|
||||
concentration = np.empty_like(self, dtype=float)
|
||||
|
||||
# Evaluate value in each region
|
||||
for i, result in enumerate(self):
|
||||
|
|
@ -73,8 +73,8 @@ class ResultsList(list):
|
|||
Array of reaction rates
|
||||
|
||||
"""
|
||||
time = np.empty_like(self)
|
||||
rate = np.empty_like(self)
|
||||
time = np.empty_like(self, dtype=float)
|
||||
rate = np.empty_like(self, dtype=float)
|
||||
|
||||
# Evaluate value in each region
|
||||
for i, result in enumerate(self):
|
||||
|
|
@ -94,8 +94,8 @@ class ResultsList(list):
|
|||
k-eigenvalue at each time
|
||||
|
||||
"""
|
||||
time = np.empty_like(self)
|
||||
eigenvalue = np.empty_like(self)
|
||||
time = np.empty_like(self, dtype=float)
|
||||
eigenvalue = np.empty_like(self, dtype=float)
|
||||
|
||||
# Get time/eigenvalue at each point
|
||||
for i, result in enumerate(self):
|
||||
|
|
|
|||
38
openmc/exceptions.py
Normal file
38
openmc/exceptions.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
class OpenMCError(Exception):
|
||||
"""Root exception class for OpenMC."""
|
||||
|
||||
|
||||
class GeometryError(OpenMCError):
|
||||
"""Geometry-related error"""
|
||||
|
||||
|
||||
class InvalidIDError(OpenMCError):
|
||||
"""Use of an ID that is invalid."""
|
||||
|
||||
|
||||
class AllocationError(OpenMCError):
|
||||
"""Error related to memory allocation."""
|
||||
|
||||
|
||||
class OutOfBoundsError(OpenMCError):
|
||||
"""Index in array out of bounds."""
|
||||
|
||||
|
||||
class DataError(OpenMCError):
|
||||
"""Error relating to nuclear data."""
|
||||
|
||||
|
||||
class PhysicsError(OpenMCError):
|
||||
"""Error relating to performing physics."""
|
||||
|
||||
|
||||
class InvalidArgumentError(OpenMCError):
|
||||
"""Argument passed was invalid."""
|
||||
|
||||
|
||||
class InvalidTypeError(OpenMCError):
|
||||
"""Tried to perform an operation on the wrong type."""
|
||||
|
||||
|
||||
class SetupError(OpenMCError):
|
||||
"""Error while setting up a problem."""
|
||||
1051
openmc/filter.py
1051
openmc/filter.py
File diff suppressed because it is too large
Load diff
465
openmc/filter_expansion.py
Normal file
465
openmc/filter_expansion.py
Normal file
|
|
@ -0,0 +1,465 @@
|
|||
from numbers import Integral, Real
|
||||
from xml.etree import ElementTree as ET
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
from . import Filter
|
||||
|
||||
|
||||
class ExpansionFilter(Filter):
|
||||
"""Abstract filter class for functional expansions."""
|
||||
|
||||
def __init__(self, order, filter_id=None):
|
||||
self.order = order
|
||||
self.id = filter_id
|
||||
|
||||
def __eq__(self, other):
|
||||
if type(self) is not type(other):
|
||||
return False
|
||||
else:
|
||||
return self.bins == other.bins
|
||||
|
||||
@property
|
||||
def order(self):
|
||||
return self._order
|
||||
|
||||
@order.setter
|
||||
def order(self, order):
|
||||
cv.check_type('expansion order', order, Integral)
|
||||
cv.check_greater_than('expansion order', order, 0, equality=True)
|
||||
self._order = order
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing Legendre filter data
|
||||
|
||||
"""
|
||||
element = ET.Element('filter')
|
||||
element.set('id', str(self.id))
|
||||
element.set('type', self.short_name.lower())
|
||||
|
||||
subelement = ET.SubElement(element, 'order')
|
||||
subelement.text = str(self.order)
|
||||
|
||||
return element
|
||||
|
||||
|
||||
class LegendreFilter(ExpansionFilter):
|
||||
r"""Score Legendre expansion moments up to specified order.
|
||||
|
||||
This filter allows scores to be multiplied by Legendre polynomials of the
|
||||
change in particle angle ($\mu$) up to a user-specified order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
Maximum Legendre polynomial order
|
||||
filter_id : int or None
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
order : int
|
||||
Maximum Legendre polynomial order
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
def __hash__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
return hash(string)
|
||||
|
||||
def __repr__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tID', self.id)
|
||||
return string
|
||||
|
||||
@ExpansionFilter.order.setter
|
||||
def order(self, order):
|
||||
ExpansionFilter.order.__set__(self, order)
|
||||
self.bins = ['P{}'.format(i) for i in range(order + 1)]
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
if group['type'].value.decode() != cls.short_name.lower():
|
||||
raise ValueError("Expected HDF5 data for filter type '"
|
||||
+ cls.short_name.lower() + "' but got '"
|
||||
+ group['type'].value.decode() + " instead")
|
||||
|
||||
filter_id = int(group.name.split('/')[-1].lstrip('filter '))
|
||||
|
||||
out = cls(group['order'].value, filter_id)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class SpatialLegendreFilter(ExpansionFilter):
|
||||
r"""Score Legendre expansion moments in space up to specified order.
|
||||
|
||||
This filter allows scores to be multiplied by Legendre polynomials of the
|
||||
the particle's position along a particular axis, normalized to a given
|
||||
range, up to a user-specified order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
Maximum Legendre polynomial order
|
||||
axis : {'x', 'y', 'z'}
|
||||
Axis along which to take the expansion
|
||||
minimum : float
|
||||
Minimum value along selected axis
|
||||
maximum : float
|
||||
Maximum value along selected axis
|
||||
filter_id : int or None
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
order : int
|
||||
Maximum Legendre polynomial order
|
||||
axis : {'x', 'y', 'z'}
|
||||
Axis along which to take the expansion
|
||||
minimum : float
|
||||
Minimum value along selected axis
|
||||
maximum : float
|
||||
Maximum value along selected axis
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, order, axis, minimum, maximum, filter_id=None):
|
||||
super().__init__(order, filter_id)
|
||||
self.axis = axis
|
||||
self.minimum = minimum
|
||||
self.maximum = maximum
|
||||
|
||||
def __hash__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tAxis', self.axis)
|
||||
string += '{: <16}=\t{}\n'.format('\tMin', self.minimum)
|
||||
string += '{: <16}=\t{}\n'.format('\tMax', self.maximum)
|
||||
return hash(string)
|
||||
|
||||
def __repr__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tAxis', self.axis)
|
||||
string += '{: <16}=\t{}\n'.format('\tMin', self.minimum)
|
||||
string += '{: <16}=\t{}\n'.format('\tMax', self.maximum)
|
||||
string += '{: <16}=\t{}\n'.format('\tID', self.id)
|
||||
return string
|
||||
|
||||
@ExpansionFilter.order.setter
|
||||
def order(self, order):
|
||||
ExpansionFilter.order.__set__(self, order)
|
||||
self.bins = ['P{}'.format(i) for i in range(order + 1)]
|
||||
|
||||
@property
|
||||
def axis(self):
|
||||
return self._axis
|
||||
|
||||
@axis.setter
|
||||
def axis(self, axis):
|
||||
cv.check_value('axis', axis, ('x', 'y', 'z'))
|
||||
self._axis = axis
|
||||
|
||||
@property
|
||||
def minimum(self):
|
||||
return self._minimum
|
||||
|
||||
@minimum.setter
|
||||
def minimum(self, minimum):
|
||||
cv.check_type('minimum', minimum, Real)
|
||||
self._minimum = minimum
|
||||
|
||||
@property
|
||||
def maximum(self):
|
||||
return self._maximum
|
||||
|
||||
@maximum.setter
|
||||
def maximum(self, maximum):
|
||||
cv.check_type('maximum', maximum, Real)
|
||||
self._maximum = maximum
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
if group['type'].value.decode() != cls.short_name.lower():
|
||||
raise ValueError("Expected HDF5 data for filter type '"
|
||||
+ cls.short_name.lower() + "' but got '"
|
||||
+ group['type'].value.decode() + " instead")
|
||||
|
||||
filter_id = int(group.name.split('/')[-1].lstrip('filter '))
|
||||
order = group['order'].value
|
||||
axis = group['axis'].value.decode()
|
||||
min_, max_ = group['min'].value, group['max'].value
|
||||
|
||||
return cls(order, axis, min_, max_, filter_id)
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing Legendre filter data
|
||||
|
||||
"""
|
||||
element = super().to_xml_element()
|
||||
subelement = ET.SubElement(element, 'axis')
|
||||
subelement.text = self.axis
|
||||
subelement = ET.SubElement(element, 'min')
|
||||
subelement.text = str(self.minimum)
|
||||
subelement = ET.SubElement(element, 'max')
|
||||
subelement.text = str(self.maximum)
|
||||
|
||||
return element
|
||||
|
||||
|
||||
class SphericalHarmonicsFilter(ExpansionFilter):
|
||||
r"""Score spherical harmonic expansion moments up to specified order.
|
||||
|
||||
This filter allows you to obtain real spherical harmonic moments of either
|
||||
the particle's direction or the cosine of the scattering angle. Specifying a
|
||||
filter with order :math:`\ell` tallies moments for all orders from 0 to
|
||||
:math:`\ell`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
Maximum spherical harmonics order, :math:`\ell`
|
||||
filter_id : int or None
|
||||
Unique identifier for the filter
|
||||
|
||||
Attributes
|
||||
----------
|
||||
order : int
|
||||
Maximum spherical harmonics order, :math:`\ell`
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
cosine : {'scatter', 'particle'}
|
||||
How to handle the cosine term.
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, order, filter_id=None):
|
||||
super().__init__(order, filter_id)
|
||||
self._cosine = 'particle'
|
||||
|
||||
def __hash__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine)
|
||||
return hash(string)
|
||||
|
||||
def __repr__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tCosine', self.cosine)
|
||||
string += '{: <16}=\t{}\n'.format('\tID', self.id)
|
||||
return string
|
||||
|
||||
@ExpansionFilter.order.setter
|
||||
def order(self, order):
|
||||
ExpansionFilter.order.__set__(self, order)
|
||||
self.bins = ['Y{},{}'.format(n, m)
|
||||
for n in range(order + 1)
|
||||
for m in range(-n, n + 1)]
|
||||
|
||||
@property
|
||||
def cosine(self):
|
||||
return self._cosine
|
||||
|
||||
@cosine.setter
|
||||
def cosine(self, cosine):
|
||||
cv.check_value('Spherical harmonics cosine treatment', cosine,
|
||||
('scatter', 'particle'))
|
||||
self._cosine = cosine
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
if group['type'].value.decode() != cls.short_name.lower():
|
||||
raise ValueError("Expected HDF5 data for filter type '"
|
||||
+ cls.short_name.lower() + "' but got '"
|
||||
+ group['type'].value.decode() + " instead")
|
||||
|
||||
filter_id = int(group.name.split('/')[-1].lstrip('filter '))
|
||||
|
||||
out = cls(group['order'].value, filter_id)
|
||||
out.cosine = group['cosine'].value.decode()
|
||||
|
||||
return out
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing spherical harmonics filter data
|
||||
|
||||
"""
|
||||
element = super().to_xml_element()
|
||||
element.set('cosine', self.cosine)
|
||||
return element
|
||||
|
||||
|
||||
class ZernikeFilter(ExpansionFilter):
|
||||
r"""Score Zernike expansion moments in space up to specified order.
|
||||
|
||||
This filter allows scores to be multiplied by Zernike polynomials of the
|
||||
particle's position normalized to a given unit circle, up to a
|
||||
user-specified order. The standard Zernike polynomials follow the definition by
|
||||
Born and Wolf, *Principles of Optics* and are defined as
|
||||
|
||||
.. math::
|
||||
Z_n^m(\rho, \theta) = R_n^m(\rho) \cos (m\theta), \quad m > 0
|
||||
|
||||
Z_n^{m}(\rho, \theta) = R_n^{m}(\rho) \sin (m\theta), \quad m < 0
|
||||
|
||||
Z_n^{m}(\rho, \theta) = R_n^{m}(\rho), \quad m = 0
|
||||
|
||||
where the radial polynomials are
|
||||
|
||||
.. math::
|
||||
R_n^m(\rho) = \sum\limits_{k=0}^{(n-m)/2} \frac{(-1)^k (n-k)!}{k! (
|
||||
\frac{n+m}{2} - k)! (\frac{n-m}{2} - k)!} \rho^{n-2k}.
|
||||
|
||||
With this definition, the integral of :math:`(Z_n^m)^2` over the unit disk
|
||||
is :math:`\frac{\epsilon_m\pi}{2n+2}` for each polynomial where :math:`\epsilon_m` is
|
||||
2 if :math:`m` equals 0 and 1 otherwise.
|
||||
|
||||
Specifying a filter with order N tallies moments for all :math:`n` from 0 to
|
||||
N and each value of :math:`m`. The ordering of the Zernike polynomial
|
||||
moments follows the ANSI Z80.28 standard, where the one-dimensional index
|
||||
:math:`j` corresponds to the :math:`n` and :math:`m` by
|
||||
|
||||
.. math::
|
||||
j = \frac{n(n + 2) + m}{2}.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order : int
|
||||
Maximum Zernike polynomial order
|
||||
x : float
|
||||
x-coordinate of center of circle for normalization
|
||||
y : float
|
||||
y-coordinate of center of circle for normalization
|
||||
r : int or None
|
||||
Radius of circle for normalization
|
||||
|
||||
Attributes
|
||||
----------
|
||||
order : int
|
||||
Maximum Zernike polynomial order
|
||||
x : float
|
||||
x-coordinate of center of circle for normalization
|
||||
y : float
|
||||
y-coordinate of center of circle for normalization
|
||||
r : int or None
|
||||
Radius of circle for normalization
|
||||
id : int
|
||||
Unique identifier for the filter
|
||||
num_bins : int
|
||||
The number of filter bins
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, order, x=0.0, y=0.0, r=1.0, filter_id=None):
|
||||
super().__init__(order, filter_id)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.r = r
|
||||
|
||||
def __hash__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
return hash(string)
|
||||
|
||||
def __repr__(self):
|
||||
string = type(self).__name__ + '\n'
|
||||
string += '{: <16}=\t{}\n'.format('\tOrder', self.order)
|
||||
string += '{: <16}=\t{}\n'.format('\tID', self.id)
|
||||
return string
|
||||
|
||||
@ExpansionFilter.order.setter
|
||||
def order(self, order):
|
||||
ExpansionFilter.order.__set__(self, order)
|
||||
self.bins = ['Z{},{}'.format(n, m)
|
||||
for n in range(order + 1)
|
||||
for m in range(-n, n + 1, 2)]
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._x
|
||||
|
||||
@x.setter
|
||||
def x(self, x):
|
||||
cv.check_type('x', x, Real)
|
||||
self._x = x
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self._y
|
||||
|
||||
@y.setter
|
||||
def y(self, y):
|
||||
cv.check_type('y', y, Real)
|
||||
self._y = y
|
||||
|
||||
@property
|
||||
def r(self):
|
||||
return self._r
|
||||
|
||||
@r.setter
|
||||
def r(self, r):
|
||||
cv.check_type('r', r, Real)
|
||||
self._r = r
|
||||
|
||||
@classmethod
|
||||
def from_hdf5(cls, group, **kwargs):
|
||||
if group['type'].value.decode() != cls.short_name.lower():
|
||||
raise ValueError("Expected HDF5 data for filter type '"
|
||||
+ cls.short_name.lower() + "' but got '"
|
||||
+ group['type'].value.decode() + " instead")
|
||||
|
||||
filter_id = int(group.name.split('/')[-1].lstrip('filter '))
|
||||
order = group['order'].value
|
||||
x, y, r = group['x'].value, group['y'].value, group['r'].value
|
||||
|
||||
return cls(order, x, y, r, filter_id)
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML Element representing the filter.
|
||||
|
||||
Returns
|
||||
-------
|
||||
element : xml.etree.ElementTree.Element
|
||||
XML element containing Zernike filter data
|
||||
|
||||
"""
|
||||
element = super().to_xml_element()
|
||||
subelement = ET.SubElement(element, 'x')
|
||||
subelement.text = str(self.x)
|
||||
subelement = ET.SubElement(element, 'y')
|
||||
subelement.text = str(self.y)
|
||||
subelement = ET.SubElement(element, 'r')
|
||||
subelement.text = str(self.r)
|
||||
|
||||
return element
|
||||
|
|
@ -546,10 +546,15 @@ class RectLattice(Lattice):
|
|||
"""
|
||||
if self.ndim == 2:
|
||||
nx, ny = self.shape
|
||||
return np.broadcast(*np.ogrid[:nx, :ny])
|
||||
for iy in range(ny):
|
||||
for ix in range(nx):
|
||||
yield (ix, iy)
|
||||
else:
|
||||
nx, ny, nz = self.shape
|
||||
return np.broadcast(*np.ogrid[:nx, :ny, :nz])
|
||||
for iz in range(nz):
|
||||
for iy in range(ny):
|
||||
for ix in range(nx):
|
||||
yield (ix, iy, iz)
|
||||
|
||||
@property
|
||||
def lower_left(self):
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class Material(IDManagerMixin):
|
|||
self.name = name
|
||||
self.temperature = temperature
|
||||
self._density = None
|
||||
self._density_units = ''
|
||||
self._density_units = 'sum'
|
||||
self._depletable = False
|
||||
self._paths = None
|
||||
self._num_instances = None
|
||||
|
|
@ -278,8 +278,8 @@ class Material(IDManagerMixin):
|
|||
|
||||
name = group['name'].value.decode() if 'name' in group else ''
|
||||
density = group['atom_density'].value
|
||||
nuc_densities = group['nuclide_densities'][...]
|
||||
nuclides = group['nuclides'].value
|
||||
if 'nuclide_densities' in group:
|
||||
nuc_densities = group['nuclide_densities'][...]
|
||||
|
||||
# Create the Material
|
||||
material = cls(mat_id, name)
|
||||
|
|
@ -295,10 +295,18 @@ class Material(IDManagerMixin):
|
|||
# Set the Material's density to atom/b-cm as used by OpenMC
|
||||
material.set_density(density=density, units='atom/b-cm')
|
||||
|
||||
# Add all nuclides to the Material
|
||||
for fullname, density in zip(nuclides, nuc_densities):
|
||||
name = fullname.decode().strip()
|
||||
material.add_nuclide(name, percent=density, percent_type='ao')
|
||||
if 'nuclides' in group:
|
||||
nuclides = group['nuclides'].value
|
||||
# Add all nuclides to the Material
|
||||
for fullname, density in zip(nuclides, nuc_densities):
|
||||
name = fullname.decode().strip()
|
||||
material.add_nuclide(name, percent=density, percent_type='ao')
|
||||
if 'macroscopics' in group:
|
||||
macroscopics = group['macroscopics'].value
|
||||
# Add all macroscopics to the Material
|
||||
for fullname in macroscopics:
|
||||
name = fullname.decode().strip()
|
||||
material.add_macroscopic(name)
|
||||
|
||||
return material
|
||||
|
||||
|
|
@ -313,7 +321,7 @@ class Material(IDManagerMixin):
|
|||
"""
|
||||
if volume_calc.domain_type == 'material':
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id][0]
|
||||
self._volume = volume_calc.volumes[self.id].n
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this material.')
|
||||
|
|
@ -379,33 +387,31 @@ class Material(IDManagerMixin):
|
|||
Parameters
|
||||
----------
|
||||
nuclide : str
|
||||
Nuclide to add
|
||||
Nuclide to add, e.g., 'Mo95'
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
percent_type : {'ao', 'wo'}
|
||||
'ao' for atom percent and 'wo' for weight percent
|
||||
|
||||
"""
|
||||
cv.check_type('nuclide', nuclide, str)
|
||||
cv.check_type('percent', percent, Real)
|
||||
cv.check_value('percent type', percent_type, {'ao', 'wo'})
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add a Nuclide to Material ID="{}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(nuclide, str):
|
||||
msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \
|
||||
'non-string value "{}"'.format(self._id, nuclide)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif not isinstance(percent, Real):
|
||||
msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \
|
||||
'non-floating point value "{}"'.format(self._id, percent)
|
||||
raise ValueError(msg)
|
||||
|
||||
elif percent_type not in ('ao', 'wo'):
|
||||
msg = 'Unable to add a Nuclide to Material ID="{}" with a ' \
|
||||
'percent type "{}"'.format(self._id, percent_type)
|
||||
raise ValueError(msg)
|
||||
# If nuclide name doesn't look valid, give a warning
|
||||
try:
|
||||
Z, _, _ = openmc.data.zam(nuclide)
|
||||
except ValueError as e:
|
||||
warnings.warn(str(e))
|
||||
else:
|
||||
# For actinides, have the material be depletable by default
|
||||
if Z >= 89:
|
||||
self.depletable = True
|
||||
|
||||
self._nuclides.append((nuclide, percent, percent_type))
|
||||
|
||||
|
|
@ -461,7 +467,7 @@ class Material(IDManagerMixin):
|
|||
raise ValueError(msg)
|
||||
|
||||
# Generally speaking, the density for a macroscopic object will
|
||||
# be 1.0. Therefore, lets set density to 1.0 so that the user
|
||||
# be 1.0. Therefore, lets set density to 1.0 so that the user
|
||||
# doesnt need to set it unless its needed.
|
||||
# Of course, if the user has already set a value of density,
|
||||
# then we will not override it.
|
||||
|
|
@ -493,7 +499,7 @@ class Material(IDManagerMixin):
|
|||
Parameters
|
||||
----------
|
||||
element : str
|
||||
Element to add
|
||||
Element to add, e.g., 'Zr'
|
||||
percent : float
|
||||
Atom or weight percent
|
||||
percent_type : {'ao', 'wo'}, optional
|
||||
|
|
@ -505,27 +511,15 @@ class Material(IDManagerMixin):
|
|||
(natural composition).
|
||||
|
||||
"""
|
||||
cv.check_type('nuclide', element, str)
|
||||
cv.check_type('percent', percent, Real)
|
||||
cv.check_value('percent type', percent_type, {'ao', 'wo'})
|
||||
|
||||
if self._macroscopic is not None:
|
||||
msg = 'Unable to add an Element to Material ID="{}" as a ' \
|
||||
'macroscopic data-set has already been added'.format(self._id)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(element, str):
|
||||
msg = 'Unable to add an Element to Material ID="{}" with a ' \
|
||||
'non-string value "{}"'.format(self._id, element)
|
||||
raise ValueError(msg)
|
||||
|
||||
if not isinstance(percent, Real):
|
||||
msg = 'Unable to add an Element to Material ID="{}" with a ' \
|
||||
'non-floating point value "{}"'.format(self._id, percent)
|
||||
raise ValueError(msg)
|
||||
|
||||
if percent_type not in ['ao', 'wo']:
|
||||
msg = 'Unable to add an Element to Material ID="{}" with a ' \
|
||||
'percent type "{}"'.format(self._id, percent_type)
|
||||
raise ValueError(msg)
|
||||
|
||||
if enrichment is not None:
|
||||
if not isinstance(enrichment, Real):
|
||||
msg = 'Unable to add an Element to Material ID="{}" with a ' \
|
||||
|
|
@ -551,10 +545,15 @@ class Material(IDManagerMixin):
|
|||
format(enrichment, self._id)
|
||||
warnings.warn(msg)
|
||||
|
||||
# Make sure element name is just that
|
||||
if not element.isalpha():
|
||||
raise ValueError("Element name should be given by the "
|
||||
"element's symbol, e.g., 'Zr'")
|
||||
|
||||
# Add naturally-occuring isotopes
|
||||
element = openmc.Element(element)
|
||||
for nuclide in element.expand(percent, percent_type, enrichment):
|
||||
self._nuclides.append(nuclide)
|
||||
self.add_nuclide(*nuclide)
|
||||
|
||||
def add_s_alpha_beta(self, name, fraction=1.0):
|
||||
r"""Add an :math:`S(\alpha,\beta)` table to the material
|
||||
|
|
|
|||
111
openmc/mesh.py
111
openmc/mesh.py
|
|
@ -38,6 +38,9 @@ class Mesh(IDManagerMixin):
|
|||
are given, it is assumed that the mesh is an x-y mesh.
|
||||
width : Iterable of float
|
||||
The width of mesh cells in each direction.
|
||||
indices : list of tuple
|
||||
A list of mesh indices for each mesh element, e.g. [(1, 1, 1), (2, 1,
|
||||
1), ...]
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -82,6 +85,24 @@ class Mesh(IDManagerMixin):
|
|||
def num_mesh_cells(self):
|
||||
return np.prod(self._dimension)
|
||||
|
||||
@property
|
||||
def indices(self):
|
||||
ndim = len(self._dimension)
|
||||
if ndim == 3:
|
||||
nx, ny, nz = self.dimension
|
||||
return ((x, y, z)
|
||||
for z in range(1, nz + 1)
|
||||
for y in range(1, ny + 1)
|
||||
for x in range(1, nx + 1))
|
||||
elif ndim == 2:
|
||||
nx, ny = self.dimension
|
||||
return ((x, y)
|
||||
for y in range(1, ny + 1)
|
||||
for x in range(1, nx + 1))
|
||||
else:
|
||||
nx, = self.dimension
|
||||
return ((x,) for x in range(1, nx + 1))
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
if name is not None:
|
||||
|
|
@ -161,40 +182,39 @@ class Mesh(IDManagerMixin):
|
|||
|
||||
return mesh
|
||||
|
||||
def cell_generator(self):
|
||||
"""Generator function to traverse through every [i,j,k] index of the
|
||||
mesh
|
||||
@classmethod
|
||||
def from_rect_lattice(cls, lattice, division=1, mesh_id=None, name=''):
|
||||
"""Create mesh from an existing rectangular lattice
|
||||
|
||||
For example the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
for mesh_index in mymesh.cell_generator():
|
||||
print(mesh_index)
|
||||
|
||||
will produce the following output for a 3-D 2x2x2 mesh in mymesh::
|
||||
|
||||
[1, 1, 1]
|
||||
[2, 1, 1]
|
||||
[1, 2, 1]
|
||||
[2, 2, 1]
|
||||
...
|
||||
Parameters
|
||||
----------
|
||||
lattice : openmc.RectLattice
|
||||
Rectangular lattice used as a template for this mesh
|
||||
division : int
|
||||
Number of mesh cells per lattice cell.
|
||||
If not specified, there will be 1 mesh cell per lattice cell.
|
||||
mesh_id : int
|
||||
Unique identifier for the mesh
|
||||
name : str
|
||||
Name of the mesh
|
||||
|
||||
Returns
|
||||
-------
|
||||
openmc.Mesh
|
||||
Mesh instance
|
||||
|
||||
"""
|
||||
cv.check_type('rectangular lattice', lattice, openmc.RectLattice)
|
||||
|
||||
if len(self.dimension) == 1:
|
||||
for x in range(self.dimension[0]):
|
||||
yield [x + 1, 1, 1]
|
||||
elif len(self.dimension) == 2:
|
||||
for y in range(self.dimension[1]):
|
||||
for x in range(self.dimension[0]):
|
||||
yield [x + 1, y + 1, 1]
|
||||
else:
|
||||
for z in range(self.dimension[2]):
|
||||
for y in range(self.dimension[1]):
|
||||
for x in range(self.dimension[0]):
|
||||
yield [x + 1, y + 1, z + 1]
|
||||
shape = np.array(lattice.shape)
|
||||
width = lattice.pitch*shape
|
||||
|
||||
mesh = cls(mesh_id, name)
|
||||
mesh.lower_left = lattice.lower_left
|
||||
mesh.upper_right = lattice.lower_left + width
|
||||
mesh.dimension = shape*division
|
||||
|
||||
return mesh
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the mesh
|
||||
|
|
@ -259,12 +279,14 @@ class Mesh(IDManagerMixin):
|
|||
cv.check_value('bc', entry, ['transmission', 'vacuum',
|
||||
'reflective', 'periodic'])
|
||||
|
||||
n_dim = len(self.dimension)
|
||||
|
||||
# Build the cell which will contain the lattice
|
||||
xplanes = [openmc.XPlane(x0=self.lower_left[0],
|
||||
boundary_type=bc[0]),
|
||||
openmc.XPlane(x0=self.upper_right[0],
|
||||
boundary_type=bc[1])]
|
||||
if len(self.dimension) == 1:
|
||||
if n_dim == 1:
|
||||
yplanes = [openmc.YPlane(y0=-1e10, boundary_type='reflective'),
|
||||
openmc.YPlane(y0=1e10, boundary_type='reflective')]
|
||||
else:
|
||||
|
|
@ -273,7 +295,7 @@ class Mesh(IDManagerMixin):
|
|||
openmc.YPlane(y0=self.upper_right[1],
|
||||
boundary_type=bc[3])]
|
||||
|
||||
if len(self.dimension) <= 2:
|
||||
if n_dim <= 2:
|
||||
# Would prefer to have the z ranges be the max supported float, but
|
||||
# these values are apparently different between python and Fortran.
|
||||
# Choosing a safe and sane default.
|
||||
|
|
@ -293,12 +315,12 @@ class Mesh(IDManagerMixin):
|
|||
(+yplanes[0] & -yplanes[1]) &
|
||||
(+zplanes[0] & -zplanes[1]))
|
||||
|
||||
# Build the universes which will be used for each of the [i,j,k]
|
||||
# Build the universes which will be used for each of the (i,j,k)
|
||||
# locations within the mesh.
|
||||
# We will concurrently build cells to assign to these universes
|
||||
cells = []
|
||||
universes = []
|
||||
for [i, j, k] in self.cell_generator():
|
||||
for index in self.indices:
|
||||
cells.append(openmc.Cell())
|
||||
universes.append(openmc.Universe())
|
||||
universes[-1].add_cell(cells[-1])
|
||||
|
|
@ -308,7 +330,24 @@ class Mesh(IDManagerMixin):
|
|||
|
||||
# Assign the universe and rotate to match the indexing expected for
|
||||
# the lattice
|
||||
lattice.universes = np.rot90(np.reshape(universes, self.dimension))
|
||||
if n_dim == 1:
|
||||
universe_array = np.array([universes])
|
||||
elif n_dim == 2:
|
||||
universe_array = np.empty(self.dimension, dtype=openmc.Universe)
|
||||
i = 0
|
||||
for y in range(self.dimension[1] - 1, -1, -1):
|
||||
for x in range(self.dimension[0]):
|
||||
universe_array[y][x] = universes[i]
|
||||
i += 1
|
||||
else:
|
||||
universe_array = np.empty(self.dimension, dtype=openmc.Universe)
|
||||
i = 0
|
||||
for z in range(self.dimension[2]):
|
||||
for y in range(self.dimension[1] - 1, -1, -1):
|
||||
for x in range(self.dimension[0]):
|
||||
universe_array[z][y][x] = universes[i]
|
||||
i += 1
|
||||
lattice.universes = universe_array
|
||||
|
||||
if self.width is not None:
|
||||
lattice.pitch = self.width
|
||||
|
|
@ -316,9 +355,9 @@ class Mesh(IDManagerMixin):
|
|||
dx = ((self.upper_right[0] - self.lower_left[0]) /
|
||||
self.dimension[0])
|
||||
|
||||
if len(self.dimension) == 1:
|
||||
if n_dim == 1:
|
||||
lattice.pitch = [dx]
|
||||
elif len(self.dimension) == 2:
|
||||
elif n_dim == 2:
|
||||
dy = ((self.upper_right[1] - self.lower_left[1]) /
|
||||
self.dimension[1])
|
||||
lattice.pitch = [dx, dy]
|
||||
|
|
|
|||
|
|
@ -587,7 +587,7 @@ class Library(object):
|
|||
self._nuclides = statepoint.summary.nuclides
|
||||
|
||||
if statepoint.run_mode == 'eigenvalue':
|
||||
self._keff = statepoint.k_combined[0]
|
||||
self._keff = statepoint.k_combined.n
|
||||
|
||||
# Load tallies for each MGXS for each domain and mgxs type
|
||||
for domain in self.domains:
|
||||
|
|
@ -1220,9 +1220,8 @@ class Library(object):
|
|||
xs_type = 'macro'
|
||||
|
||||
# Initialize file
|
||||
mgxs_file = openmc.MGXSLibrary(self.energy_groups,
|
||||
num_delayed_groups=\
|
||||
self.num_delayed_groups)
|
||||
mgxs_file = openmc.MGXSLibrary(
|
||||
self.energy_groups, num_delayed_groups=self.num_delayed_groups)
|
||||
|
||||
if self.domain_type == 'mesh':
|
||||
# Create the xsdata objects and add to the mgxs_file
|
||||
|
|
@ -1231,7 +1230,7 @@ class Library(object):
|
|||
if self.by_nuclide:
|
||||
raise NotImplementedError("Mesh domains do not currently "
|
||||
"support nuclidic tallies")
|
||||
for subdomain in domain.cell_generator():
|
||||
for subdomain in domain.indices:
|
||||
# Build & add metadata to XSdata object
|
||||
if xsdata_names is None:
|
||||
xsdata_name = 'set' + str(i + 1)
|
||||
|
|
@ -1346,7 +1345,7 @@ class Library(object):
|
|||
geometry.root_universe = root
|
||||
materials = openmc.Materials()
|
||||
|
||||
for i, subdomain in enumerate(self.domains[0].cell_generator()):
|
||||
for i, subdomain in enumerate(self.domains[0].indices):
|
||||
xsdata = mgxs_file.xsdatas[i]
|
||||
|
||||
# Build the macroscopic and assign it to the cell of
|
||||
|
|
@ -1401,24 +1400,16 @@ class Library(object):
|
|||
|
||||
The rules to check include:
|
||||
|
||||
- Either total or transport should be present.
|
||||
- Either total or transport must be present.
|
||||
|
||||
- Both can be available if one wants, but we should
|
||||
use whatever corresponds to Library.correction (if P0: transport)
|
||||
|
||||
- Absorption and total (or transport) are required.
|
||||
- Absorption is required.
|
||||
- A nu-fission cross section and chi values are not required as a
|
||||
fixed source problem could be the target.
|
||||
- Fission and kappa-fission are not required as they are only
|
||||
needed to support tallies the user may wish to request.
|
||||
- A nu-scatter matrix is required.
|
||||
|
||||
- Having a multiplicity matrix is preferred.
|
||||
- Having both nu-scatter (of any order) and scatter
|
||||
(at least isotropic) matrices is the second choice.
|
||||
- If only nu-scatter, need total (not transport), to
|
||||
be used in adjusting absorption
|
||||
(i.e., reduced_abs = tot - nuscatt)
|
||||
|
||||
See also
|
||||
--------
|
||||
|
|
@ -1428,36 +1419,51 @@ class Library(object):
|
|||
"""
|
||||
|
||||
error_flag = False
|
||||
|
||||
# if correction is 'P0', then transport must be provided
|
||||
# otherwise total must be provided
|
||||
if self.correction == 'P0':
|
||||
if ('transport' not in self.mgxs_types and
|
||||
'nu-transport' not in self.mgxs_types):
|
||||
error_flag = True
|
||||
warn('If the "correction" parameter is "P0", then a '
|
||||
'"transport" or "nu-transport" MGXS type is required.')
|
||||
else:
|
||||
if 'total' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('If the "correction" parameter is None, then a '
|
||||
'"total" MGXS type is required.')
|
||||
|
||||
# Check consistency of "nu-transport" and "nu-scatter"
|
||||
if 'nu-transport' in self.mgxs_types:
|
||||
if not ('nu-scatter matrix' in self.mgxs_types or
|
||||
'consistent nu-scatter matrix' in self.mgxs_types):
|
||||
error_flag = True
|
||||
warn('If a "nu-transport" MGXS type is used then a '
|
||||
'"nu-scatter matrix" or "consistent nu-scatter matrix" '
|
||||
'must also be used.')
|
||||
elif 'transport' in self.mgxs_types:
|
||||
if not ('scatter matrix' in self.mgxs_types or
|
||||
'consistent scatter matrix' in self.mgxs_types):
|
||||
error_flag = True
|
||||
warn('If a "transport" MGXS type is used then a '
|
||||
'"scatter matrix" or "consistent scatter matrix" '
|
||||
'must also be used.')
|
||||
|
||||
# Make sure there is some kind of a scattering matrix data
|
||||
if 'nu-scatter matrix' not in self.mgxs_types and \
|
||||
'consistent nu-scatter matrix' not in self.mgxs_types and \
|
||||
'scatter matrix' not in self.mgxs_types and \
|
||||
'consistent scatter matrix' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('A "nu-scatter matrix", "consistent nu-scatter matrix", '
|
||||
'"scatter matrix", or "consistent scatter matrix" MGXS '
|
||||
'type is required.')
|
||||
|
||||
# Ensure absorption is present
|
||||
if 'absorption' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('An "absorption" MGXS type is required but not provided.')
|
||||
# Ensure nu-scattering matrix is required
|
||||
if 'nu-scatter matrix' not in self.mgxs_types and \
|
||||
'consistent nu-scatter matrix' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('A "nu-scatter matrix" MGXS type is required but not provided.')
|
||||
else:
|
||||
# Ok, now see the status of scatter and/or multiplicity
|
||||
if 'scatter matrix' not in self.mgxs_types or \
|
||||
'consistent scatter matrix' not in self.mgxs_types and \
|
||||
'multiplicity matrix' not in self.mgxs_types:
|
||||
# We dont have data needed for multiplicity matrix, therefore
|
||||
# we need total, and not transport.
|
||||
if 'total' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('A "total" MGXS type is required if a '
|
||||
'scattering matrix is not provided.')
|
||||
# Total or transport can be present, but if using
|
||||
# self.correction=="P0", then we should use transport.
|
||||
if self.correction == "P0" and 'nu-transport' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('A "nu-transport" MGXS type is required since a "P0" '
|
||||
'correction is applied, but a "nu-transport" MGXS is '
|
||||
'not provided.')
|
||||
elif self.correction is None and 'total' not in self.mgxs_types:
|
||||
error_flag = True
|
||||
warn('A "total" MGXS type is required, but not provided.')
|
||||
|
||||
if error_flag:
|
||||
raise ValueError('Invalid MGXS configuration encountered.')
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import warnings
|
|||
import os
|
||||
import copy
|
||||
from abc import ABCMeta
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
import h5py
|
||||
|
|
@ -937,8 +936,7 @@ class MGXS(metaclass=ABCMeta):
|
|||
# NOTE: This is important if tally merging was used
|
||||
if self.domain_type == 'mesh':
|
||||
filters = [_DOMAIN_TO_FILTER[self.domain_type]]
|
||||
xyz = [range(1, x + 1) for x in self.domain.dimension]
|
||||
filter_bins = [tuple(itertools.product(*xyz))]
|
||||
filter_bins = [tuple(self.domain.indices)]
|
||||
elif self.domain_type != 'distribcell':
|
||||
filters = [_DOMAIN_TO_FILTER[self.domain_type]]
|
||||
filter_bins = [(self.domain.id,)]
|
||||
|
|
@ -1166,12 +1164,14 @@ class MGXS(metaclass=ABCMeta):
|
|||
if not isinstance(tally_filter, (openmc.EnergyFilter,
|
||||
openmc.EnergyoutFilter)):
|
||||
continue
|
||||
elif len(tally_filter.bins) != len(fine_edges):
|
||||
elif len(tally_filter.bins) != len(fine_edges) - 1:
|
||||
continue
|
||||
elif not np.allclose(tally_filter.bins, fine_edges):
|
||||
elif not np.allclose(tally_filter.bins[:, 0], fine_edges[:-1]):
|
||||
continue
|
||||
else:
|
||||
tally_filter.bins = coarse_groups.group_edges
|
||||
cedge = coarse_groups.group_edges
|
||||
tally_filter.values = cedge
|
||||
tally_filter.bins = np.vstack((cedge[:-1], cedge[1:])).T
|
||||
mean = np.add.reduceat(mean, energy_indices, axis=i)
|
||||
std_dev = np.add.reduceat(std_dev**2, energy_indices,
|
||||
axis=i)
|
||||
|
|
@ -1531,8 +1531,7 @@ class MGXS(metaclass=ABCMeta):
|
|||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
elif self.domain_type == 'mesh':
|
||||
xyz = [range(1, x + 1) for x in self.domain.dimension]
|
||||
subdomains = list(itertools.product(*xyz))
|
||||
subdomains = list(self.domain.indices)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -1702,8 +1701,7 @@ class MGXS(metaclass=ABCMeta):
|
|||
domain_filter = self.xs_tally.find_filter('sum(distribcell)')
|
||||
subdomains = domain_filter.bins
|
||||
elif self.domain_type == 'mesh':
|
||||
xyz = [range(1, x+1) for x in self.domain.dimension]
|
||||
subdomains = list(itertools.product(*xyz))
|
||||
subdomains = list(self.domain.indices)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -2342,8 +2340,7 @@ class MatrixMGXS(MGXS):
|
|||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
elif self.domain_type == 'mesh':
|
||||
xyz = [range(1, x + 1) for x in self.domain.dimension]
|
||||
subdomains = list(itertools.product(*xyz))
|
||||
subdomains = list(self.domain.indices)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -2720,9 +2717,9 @@ class TransportXS(MGXS):
|
|||
@property
|
||||
def scores(self):
|
||||
if not self.nu:
|
||||
return ['flux', 'total', 'flux', 'scatter-1']
|
||||
return ['flux', 'total', 'flux', 'scatter']
|
||||
else:
|
||||
return ['flux', 'total', 'flux', 'nu-scatter-1']
|
||||
return ['flux', 'total', 'flux', 'nu-scatter']
|
||||
|
||||
@property
|
||||
def tally_keys(self):
|
||||
|
|
@ -2733,8 +2730,9 @@ class TransportXS(MGXS):
|
|||
group_edges = self.energy_groups.group_edges
|
||||
energy_filter = openmc.EnergyFilter(group_edges)
|
||||
energyout_filter = openmc.EnergyoutFilter(group_edges)
|
||||
p1_filter = openmc.LegendreFilter(1)
|
||||
filters = [[energy_filter], [energy_filter],
|
||||
[energy_filter], [energyout_filter]]
|
||||
[energy_filter], [energyout_filter, p1_filter]]
|
||||
|
||||
return self._add_angle_filters(filters)
|
||||
|
||||
|
|
@ -2742,12 +2740,18 @@ class TransportXS(MGXS):
|
|||
def rxn_rate_tally(self):
|
||||
if self._rxn_rate_tally is None:
|
||||
# Switch EnergyoutFilter to EnergyFilter.
|
||||
old_filt = self.tallies['scatter-1'].filters[-1]
|
||||
new_filt = openmc.EnergyFilter(old_filt.bins)
|
||||
self.tallies['scatter-1'].filters[-1] = new_filt
|
||||
p1_tally = self.tallies['scatter-1']
|
||||
old_filt = p1_tally.filters[-2]
|
||||
new_filt = openmc.EnergyFilter(old_filt.values)
|
||||
p1_tally.filters[-2] = new_filt
|
||||
|
||||
self._rxn_rate_tally = \
|
||||
self.tallies['total'] - self.tallies['scatter-1']
|
||||
# Slice Legendre expansion filter and change name of score
|
||||
p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P1',)],
|
||||
squeeze=True)
|
||||
p1_tally.scores = ['scatter-1']
|
||||
|
||||
self._rxn_rate_tally = self.tallies['total'] - p1_tally
|
||||
self._rxn_rate_tally.sparse = self.sparse
|
||||
|
||||
return self._rxn_rate_tally
|
||||
|
|
@ -2761,15 +2765,22 @@ class TransportXS(MGXS):
|
|||
raise ValueError(msg)
|
||||
|
||||
# Switch EnergyoutFilter to EnergyFilter.
|
||||
old_filt = self.tallies['scatter-1'].filters[-1]
|
||||
new_filt = openmc.EnergyFilter(old_filt.bins)
|
||||
self.tallies['scatter-1'].filters[-1] = new_filt
|
||||
p1_tally = self.tallies['scatter-1']
|
||||
old_filt = p1_tally.filters[-2]
|
||||
new_filt = openmc.EnergyFilter(old_filt.values)
|
||||
p1_tally.filters[-2] = new_filt
|
||||
|
||||
# Slice Legendre expansion filter and change name of score
|
||||
p1_tally = p1_tally.get_slice(filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P1',)],
|
||||
squeeze=True)
|
||||
p1_tally.scores = ['scatter-1']
|
||||
|
||||
# Compute total cross section
|
||||
total_xs = self.tallies['total'] / self.tallies['flux (tracklength)']
|
||||
|
||||
# Compute transport correction term
|
||||
trans_corr = self.tallies['scatter-1'] / self.tallies['flux (analog)']
|
||||
trans_corr = p1_tally / self.tallies['flux (analog)']
|
||||
|
||||
# Compute the transport-corrected total cross section
|
||||
self._xs_tally = total_xs - trans_corr
|
||||
|
|
@ -3513,6 +3524,7 @@ class ScatterXS(MGXS):
|
|||
self._estimator = 'analog'
|
||||
self._valid_estimators = ['analog']
|
||||
|
||||
|
||||
class ScatterMatrixXS(MatrixMGXS):
|
||||
r"""A scattering matrix multi-group cross section with the cosine of the
|
||||
change-in-angle represented as one or more Legendre moments or a histogram.
|
||||
|
|
@ -3601,10 +3613,10 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
name : str, optional
|
||||
Name of the multi-group cross section. Used as a label to identify
|
||||
tallies in OpenMC 'tallies.xml' file.
|
||||
num_polar : Integral, optional
|
||||
num_polar : int, optional
|
||||
Number of equi-width polar angle bins for angle discretization;
|
||||
defaults to one bin
|
||||
num_azimuthal : Integral, optional
|
||||
num_azimuthal : int, optional
|
||||
Number of equi-width azimuthal angle bins for angle discretization;
|
||||
defaults to one bin
|
||||
nu : bool
|
||||
|
|
@ -3650,9 +3662,9 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
Domain type for spatial homogenization
|
||||
energy_groups : openmc.mgxs.EnergyGroups
|
||||
Energy group structure for energy condensation
|
||||
num_polar : Integral
|
||||
num_polar : int
|
||||
Number of equi-width polar angle bins for angle discretization
|
||||
num_azimuthal : Integral
|
||||
num_azimuthal : int
|
||||
Number of equi-width azimuthal angle bins for angle discretization
|
||||
tally_trigger : openmc.Trigger
|
||||
An (optional) tally precision trigger given to each tally used to
|
||||
|
|
@ -3770,38 +3782,25 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
def scores(self):
|
||||
|
||||
if self.formulation == 'simple':
|
||||
scores = ['flux']
|
||||
|
||||
if self.scatter_format == 'legendre':
|
||||
if self.legendre_order == 0:
|
||||
scores.append('{}-0'.format(self.rxn_type))
|
||||
if self.correction:
|
||||
scores.append('{}-1'.format(self.rxn_type))
|
||||
else:
|
||||
scores.append('{}-P{}'.format(self.rxn_type, self.legendre_order))
|
||||
elif self.scatter_format == 'histogram':
|
||||
scores += [self.rxn_type]
|
||||
scores = ['flux', self.rxn_type]
|
||||
|
||||
else:
|
||||
# Add scores for groupwise scattering cross section
|
||||
scores = ['flux', 'scatter']
|
||||
|
||||
# Add scores for group-to-group scattering probability matrix
|
||||
if self.scatter_format == 'legendre':
|
||||
if self.legendre_order == 0:
|
||||
scores.append('scatter-0')
|
||||
else:
|
||||
scores.append('scatter-P{}'.format(self.legendre_order))
|
||||
elif self.scatter_format == 'histogram':
|
||||
scores.append('scatter-0')
|
||||
# these scores also contain the angular information, whether it be
|
||||
# Legendre expansion or histogram bins
|
||||
scores.append('scatter')
|
||||
|
||||
# Add scores for multiplicity matrix
|
||||
# Add scores for multiplicity matrix; scatter info for the
|
||||
# denominator will come from the previous score
|
||||
if self.nu:
|
||||
scores.extend(['nu-scatter-0', 'scatter-0'])
|
||||
scores.append('nu-scatter')
|
||||
|
||||
# Add scores for transport correction
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
scores.extend(['{}-1'.format(self.rxn_type), 'flux'])
|
||||
scores.extend([self.rxn_type, 'flux'])
|
||||
|
||||
return scores
|
||||
|
||||
|
|
@ -3814,15 +3813,15 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
tally_keys = ['flux (tracklength)', 'scatter']
|
||||
|
||||
# Add keys for group-to-group scattering probability matrix
|
||||
tally_keys.append('scatter-P{}'.format(self.legendre_order))
|
||||
tally_keys.append('scatter matrix')
|
||||
|
||||
# Add keys for multiplicity matrix
|
||||
if self.nu:
|
||||
tally_keys.extend(['nu-scatter-0', 'scatter-0'])
|
||||
tally_keys.extend(['nu-scatter'])
|
||||
|
||||
# Add keys for transport correction
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
tally_keys.extend(['{}-1'.format(self.rxn_type), 'flux (analog)'])
|
||||
tally_keys.extend(['correction', 'flux (analog)'])
|
||||
|
||||
return tally_keys
|
||||
|
||||
|
|
@ -3839,7 +3838,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
# Add estimators for multiplicity matrix
|
||||
if self.nu:
|
||||
estimators.extend(['analog', 'analog'])
|
||||
estimators.extend(['analog'])
|
||||
|
||||
# Add estimators for transport correction
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
|
|
@ -3856,13 +3855,15 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
if self.scatter_format == 'legendre':
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
filters = [[energy], [energy, energyout], [energyout]]
|
||||
angle_filter = openmc.LegendreFilter(order=1)
|
||||
else:
|
||||
filters = [[energy], [energy, energyout]]
|
||||
angle_filter = \
|
||||
openmc.LegendreFilter(order=self.legendre_order)
|
||||
elif self.scatter_format == 'histogram':
|
||||
bins = np.linspace(-1., 1., num=self.histogram_bins + 1,
|
||||
endpoint=True)
|
||||
filters = [[energy], [energy, energyout, openmc.MuFilter(bins)]]
|
||||
angle_filter = openmc.MuFilter(bins)
|
||||
filters = [[energy], [energy, energyout, angle_filter]]
|
||||
|
||||
else:
|
||||
group_edges = self.energy_groups.group_edges
|
||||
|
|
@ -3874,19 +3875,21 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
# Group-to-group scattering probability matrix
|
||||
if self.scatter_format == 'legendre':
|
||||
filters.append([energy, energyout])
|
||||
angle_filter = openmc.LegendreFilter(order=self.legendre_order)
|
||||
elif self.scatter_format == 'histogram':
|
||||
bins = np.linspace(-1., 1., num=self.histogram_bins + 1,
|
||||
endpoint=True)
|
||||
filters.append([energy, energyout, openmc.MuFilter(bins)])
|
||||
angle_filter = openmc.MuFilter(bins)
|
||||
filters.append([energy, energyout, angle_filter])
|
||||
|
||||
# Multiplicity matrix
|
||||
if self.nu:
|
||||
filters.extend([[energy, energyout], [energy, energyout]])
|
||||
filters.extend([[energy, energyout]])
|
||||
|
||||
# Add filters for transport correction
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
filters.extend([[energyout], [energy]])
|
||||
filters.extend([[energyout, openmc.LegendreFilter(1)],
|
||||
[energy]])
|
||||
|
||||
return self._add_angle_filters(filters)
|
||||
|
||||
|
|
@ -3897,27 +3900,39 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
if self.formulation == 'simple':
|
||||
if self.scatter_format == 'legendre':
|
||||
# If using P0 correction subtract scatter-1 from the diagonal
|
||||
# If using P0 correction subtract P2 scatter from the diag.
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
scatter_p0 = self.tallies['{}-0'.format(self.rxn_type)]
|
||||
scatter_p1 = self.tallies['{}-1'.format(self.rxn_type)]
|
||||
energy_filter = scatter_p0.find_filter(openmc.EnergyFilter)
|
||||
scatter_p0 = self.tallies[self.rxn_type].get_slice(
|
||||
filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P0',)])
|
||||
scatter_p1 = self.tallies[self.rxn_type].get_slice(
|
||||
filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P1',)])
|
||||
|
||||
# Transform scatter-p1 tally into an energyin/out matrix
|
||||
# Set the Legendre order of these tallies to be 0
|
||||
# so they can be subtracted
|
||||
legendre = openmc.LegendreFilter(order=0)
|
||||
scatter_p0.filters[-1] = legendre
|
||||
scatter_p1.filters[-1] = legendre
|
||||
|
||||
scatter_p1 = scatter_p1.summation(
|
||||
filter_type=openmc.EnergyFilter,
|
||||
remove_filter=True)
|
||||
|
||||
energy_filter = \
|
||||
scatter_p0.find_filter(openmc.EnergyFilter)
|
||||
|
||||
# Transform scatter-p1 into an energyin/out matrix
|
||||
# to match scattering matrix shape for tally arithmetic
|
||||
energy_filter = copy.deepcopy(energy_filter)
|
||||
scatter_p1 = scatter_p1.diagonalize_filter(energy_filter)
|
||||
scatter_p1 = \
|
||||
scatter_p1.diagonalize_filter(energy_filter)
|
||||
|
||||
self._rxn_rate_tally = scatter_p0 - scatter_p1
|
||||
|
||||
# Extract scattering moment reaction rate Tally
|
||||
elif self.legendre_order == 0:
|
||||
tally_key = '{}-{}'.format(self.rxn_type,
|
||||
self.legendre_order)
|
||||
self._rxn_rate_tally = self.tallies[tally_key]
|
||||
# Otherwise, extract scattering moment reaction rate Tally
|
||||
else:
|
||||
tally_key = '{}-P{}'.format(self.rxn_type,
|
||||
self.legendre_order)
|
||||
self._rxn_rate_tally = self.tallies[tally_key]
|
||||
self._rxn_rate_tally = self.tallies[self.rxn_type]
|
||||
elif self.scatter_format == 'histogram':
|
||||
# Extract scattering rate distribution tally
|
||||
self._rxn_rate_tally = self.tallies[self.rxn_type]
|
||||
|
|
@ -3944,35 +3959,24 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
self._xs_tally = MGXS.xs_tally.fget(self)
|
||||
|
||||
else:
|
||||
# Compute scattering probability matrix
|
||||
energyout_bins = [self.energy_groups.get_group_bounds(i)
|
||||
for i in range(self.num_groups, 0, -1)]
|
||||
tally_key = 'scatter-P{}'.format(self.legendre_order)
|
||||
# Compute scattering probability matrixS
|
||||
tally_key = 'scatter matrix'
|
||||
|
||||
# Compute normalization factor summed across outgoing energies
|
||||
norm = self.tallies[tally_key].get_slice(scores=['scatter-0'])
|
||||
norm = norm.summation(
|
||||
filter_type=openmc.EnergyoutFilter, filter_bins=energyout_bins)
|
||||
|
||||
# Remove the AggregateFilter summed across energyout bins
|
||||
norm._filters = norm._filters[:2]
|
||||
if self.scatter_format == 'legendre':
|
||||
norm = self.tallies[tally_key].get_slice(
|
||||
scores=['scatter'],
|
||||
filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P0',)], squeeze=True)
|
||||
|
||||
# Compute normalization factor summed across outgoing mu bins
|
||||
if self.scatter_format == 'histogram':
|
||||
|
||||
# (Re-)append the MuFilter which was removed above
|
||||
mu_bins = np.linspace(
|
||||
-1., 1., num=self.histogram_bins + 1, endpoint=True)
|
||||
norm._filters.append(openmc.MuFilter(mu_bins))
|
||||
|
||||
# Sum across all mu bins
|
||||
mu_bins = [(mu_bins[i], mu_bins[i+1]) for
|
||||
i in range(self.histogram_bins)]
|
||||
elif self.scatter_format == 'histogram':
|
||||
norm = self.tallies[tally_key].get_slice(
|
||||
scores=['scatter'])
|
||||
norm = norm.summation(
|
||||
filter_type=openmc.MuFilter, filter_bins=mu_bins)
|
||||
|
||||
# Remove the AggregateFilter summed across mu bins
|
||||
norm._filters = norm._filters[:2]
|
||||
filter_type=openmc.MuFilter, remove_filter=True)
|
||||
norm = norm.summation(filter_type=openmc.EnergyoutFilter,
|
||||
remove_filter=True)
|
||||
|
||||
# Compute groupwise scattering cross section
|
||||
self._xs_tally = self.tallies['scatter'] * \
|
||||
|
|
@ -3984,15 +3988,36 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
# Multiply by the multiplicity matrix
|
||||
if self.nu:
|
||||
numer = self.tallies['nu-scatter-0']
|
||||
denom = self.tallies['scatter-0']
|
||||
numer = self.tallies['nu-scatter']
|
||||
# Get the denominator
|
||||
if self.scatter_format == 'legendre':
|
||||
denom = self.tallies[tally_key].get_slice(
|
||||
scores=['scatter'],
|
||||
filters=[openmc.LegendreFilter],
|
||||
filter_bins=[('P0',)], squeeze=True)
|
||||
|
||||
# Compute normalization factor summed across mu bins
|
||||
elif self.scatter_format == 'histogram':
|
||||
denom = self.tallies[tally_key].get_slice(
|
||||
scores=['scatter'])
|
||||
|
||||
# Sum across all mu bins
|
||||
denom = denom.summation(
|
||||
filter_type=openmc.MuFilter, remove_filter=True)
|
||||
|
||||
self._xs_tally *= (numer / denom)
|
||||
|
||||
# If using P0 correction subtract scatter-1 from the diagonal
|
||||
if self.correction == 'P0' and self.legendre_order == 0:
|
||||
scatter_p1 = self.tallies['{}-1'.format(self.rxn_type)]
|
||||
scatter_p1 = self.tallies['correction'].get_slice(
|
||||
filters=[openmc.LegendreFilter], filter_bins=[('P1',)])
|
||||
flux = self.tallies['flux (analog)']
|
||||
|
||||
# Set the Legendre order of the P1 tally to be P0
|
||||
# so it can be subtracted
|
||||
legendre = openmc.LegendreFilter(order=0)
|
||||
scatter_p1.filters[-1] = legendre
|
||||
|
||||
# Transform scatter-p1 tally into an energyin/out matrix
|
||||
# to match scattering matrix shape for tally arithmetic
|
||||
energy_filter = flux.find_filter(openmc.EnergyFilter)
|
||||
|
|
@ -4004,10 +4029,34 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
# Override the nuclides for tally arithmetic
|
||||
correction.nuclides = scatter_p1.nuclides
|
||||
|
||||
# Set xs_tally to be itself with only P0 data
|
||||
self._xs_tally = self._xs_tally.get_slice(
|
||||
filters=[openmc.LegendreFilter], filter_bins=[('P0',)])
|
||||
# Tell xs_tally that it is P0
|
||||
legendre_xs_tally = \
|
||||
self._xs_tally.find_filter(openmc.LegendreFilter)
|
||||
legendre_xs_tally.order = 0
|
||||
|
||||
# And subtract the P1 correction from the P0 matrix
|
||||
self._xs_tally -= correction
|
||||
|
||||
self._compute_xs()
|
||||
|
||||
# Force the angle filter to be the last filter
|
||||
if self.scatter_format == 'histogram':
|
||||
angle_filter = self._xs_tally.find_filter(openmc.MuFilter)
|
||||
else:
|
||||
angle_filter = \
|
||||
self._xs_tally.find_filter(openmc.LegendreFilter)
|
||||
angle_filter_index = self._xs_tally.filters.index(angle_filter)
|
||||
# If the angle filter index is not last, then make it last
|
||||
if angle_filter_index != len(self._xs_tally.filters) - 1:
|
||||
energyout_filter = \
|
||||
self._xs_tally.find_filter(openmc.EnergyoutFilter)
|
||||
self._xs_tally._swap_filters(energyout_filter,
|
||||
angle_filter)
|
||||
|
||||
return self._xs_tally
|
||||
|
||||
@nu.setter
|
||||
|
|
@ -4128,16 +4177,6 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
self._rxn_rate_tally = None
|
||||
self._loaded_sp = False
|
||||
|
||||
if self.scatter_format == 'legendre':
|
||||
# Expand scores to match the format in the statepoint
|
||||
# e.g., "scatter-P2" -> "scatter-0", "scatter-1", "scatter-2"
|
||||
for tally_key, tally in self.tallies.items():
|
||||
if 'scatter-P' in tally.scores[0]:
|
||||
score_prefix = tally.scores[0].split('P')[0]
|
||||
self.tallies[tally_key].scores = \
|
||||
[score_prefix + '{}'.format(i)
|
||||
for i in range(self.legendre_order + 1)]
|
||||
|
||||
super().load_from_statepoint(statepoint)
|
||||
|
||||
def get_slice(self, nuclides=[], in_groups=[], out_groups=[],
|
||||
|
|
@ -4190,12 +4229,11 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
slice_xs.legendre_order = legendre_order
|
||||
|
||||
# Slice the scattering tally
|
||||
tally_key = '{}-P{}'.format(self.rxn_type, self.legendre_order)
|
||||
expand_scores = \
|
||||
[self.rxn_type + '-{}'.format(i)
|
||||
for i in range(self.legendre_order + 1)]
|
||||
slice_xs.tallies[tally_key] = \
|
||||
slice_xs.tallies[tally_key].get_slice(scores=expand_scores)
|
||||
filter_bins = [tuple(['P{}'.format(i)
|
||||
for i in range(self.legendre_order + 1)])]
|
||||
slice_xs.tallies[self.rxn_type] = \
|
||||
slice_xs.tallies[self.rxn_type].get_slice(
|
||||
filters=[openmc.LegendreFilter], filter_bins=filter_bins)
|
||||
|
||||
# Slice outgoing energy groups if needed
|
||||
if len(out_groups) != 0:
|
||||
|
|
@ -4209,7 +4247,8 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
for tally_type, tally in slice_xs.tallies.items():
|
||||
if tally.contains_filter(openmc.EnergyoutFilter):
|
||||
tally_slice = tally.get_slice(
|
||||
filters=[openmc.EnergyoutFilter], filter_bins=filter_bins)
|
||||
filters=[openmc.EnergyoutFilter],
|
||||
filter_bins=filter_bins)
|
||||
slice_xs.tallies[tally_type] = tally_slice
|
||||
|
||||
slice_xs.sparse = self.sparse
|
||||
|
|
@ -4320,14 +4359,19 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
filter_bins.append((self.energy_groups.get_group_bounds(group),))
|
||||
|
||||
# Construct CrossScore for requested scattering moment
|
||||
if moment != 'all' and self.scatter_format == 'legendre':
|
||||
cv.check_type('moment', moment, Integral)
|
||||
cv.check_greater_than('moment', moment, 0, equality=True)
|
||||
cv.check_less_than(
|
||||
'moment', moment, self.legendre_order, equality=True)
|
||||
scores = [self.xs_tally.scores[moment]]
|
||||
if self.scatter_format == 'legendre':
|
||||
if moment != 'all':
|
||||
cv.check_type('moment', moment, Integral)
|
||||
cv.check_greater_than('moment', moment, 0, equality=True)
|
||||
cv.check_less_than(
|
||||
'moment', moment, self.legendre_order, equality=True)
|
||||
filters.append(openmc.LegendreFilter)
|
||||
filter_bins.append(('P{}'.format(moment),))
|
||||
num_angle_bins = 1
|
||||
else:
|
||||
num_angle_bins = self.legendre_order + 1
|
||||
else:
|
||||
scores = []
|
||||
num_angle_bins = self.histogram_bins
|
||||
|
||||
# Construct a collection of the nuclides to retrieve from the xs tally
|
||||
if self.by_nuclide:
|
||||
|
|
@ -4339,6 +4383,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
query_nuclides = ['total']
|
||||
|
||||
# Use tally summation if user requested the sum for all nuclides
|
||||
scores = self.xs_tally.scores
|
||||
if nuclides == 'sum' or nuclides == ['sum']:
|
||||
xs_tally = self.xs_tally.summation(nuclides=query_nuclides)
|
||||
xs = xs_tally.get_values(scores=scores, filters=filters,
|
||||
|
|
@ -4370,24 +4415,15 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
else:
|
||||
num_out_groups = len(out_groups)
|
||||
|
||||
if self.scatter_format == 'histogram':
|
||||
num_mu_bins = self.histogram_bins
|
||||
else:
|
||||
num_mu_bins = 1
|
||||
|
||||
# Reshape tally data array with separate axes for domain and energy
|
||||
# Accomodate the polar and azimuthal bins if needed
|
||||
num_subdomains = int(xs.shape[0] / (num_mu_bins * num_in_groups *
|
||||
num_subdomains = int(xs.shape[0] / (num_angle_bins * num_in_groups *
|
||||
num_out_groups * self.num_polar *
|
||||
self.num_azimuthal))
|
||||
if self.num_polar > 1 or self.num_azimuthal > 1:
|
||||
if self.scatter_format == 'histogram':
|
||||
new_shape = (self.num_polar, self.num_azimuthal,
|
||||
num_subdomains, num_in_groups, num_out_groups,
|
||||
num_mu_bins)
|
||||
else:
|
||||
new_shape = (self.num_polar, self.num_azimuthal,
|
||||
num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape = (self.num_polar, self.num_azimuthal,
|
||||
num_subdomains, num_in_groups, num_out_groups,
|
||||
num_angle_bins)
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
|
|
@ -4400,11 +4436,9 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
if order_groups == 'increasing':
|
||||
xs = xs[:, :, :, ::-1, ::-1, ...]
|
||||
else:
|
||||
if self.scatter_format == 'histogram':
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups,
|
||||
num_mu_bins)
|
||||
else:
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups)
|
||||
new_shape = (num_subdomains, num_in_groups, num_out_groups,
|
||||
num_angle_bins)
|
||||
|
||||
new_shape += xs.shape[1:]
|
||||
xs = np.reshape(xs, new_shape)
|
||||
|
||||
|
|
@ -4419,14 +4453,14 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
if squeeze:
|
||||
# We want to squeeze out everything but the angles, in_groups,
|
||||
# out_groups, and, if needed, num_mu_bins dimension. These must
|
||||
# out_groups, and, if needed, num_angle_bins dimension. These must
|
||||
# not be squeezed so 1-group, 1-angle problems have the correct
|
||||
# shape.
|
||||
xs = self._squeeze_xs(xs)
|
||||
return xs
|
||||
|
||||
def get_pandas_dataframe(self, groups='all', nuclides='all', moment='all',
|
||||
xs_type='macro', paths=True):
|
||||
def get_pandas_dataframe(self, groups='all', nuclides='all',
|
||||
xs_type='macro', paths=False):
|
||||
"""Build a Pandas DataFrame for the MGXS data.
|
||||
|
||||
This method leverages :meth:`openmc.Tally.get_pandas_dataframe`, but
|
||||
|
|
@ -4441,19 +4475,15 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
may be a list of nuclide name strings (e.g., ['U235', 'U238']).
|
||||
The special string 'all' will include the cross sections for all
|
||||
nuclides in the spatial domain. The special string 'sum' will
|
||||
include the cross sections summed over all nuclides. Defaults
|
||||
to 'all'.
|
||||
moment : int or 'all'
|
||||
The scattering matrix moment to return. All moments will be
|
||||
returned if the moment is 'all' (default); otherwise, a specific
|
||||
moment will be returned.
|
||||
include the cross sections summed over all nuclides. Defaults to
|
||||
'all'.
|
||||
xs_type: {'macro', 'micro'}
|
||||
Return macro or micro cross section in units of cm^-1 or barns.
|
||||
Defaults to 'macro'.
|
||||
paths : bool, optional
|
||||
Construct columns for distribcell tally filters (default is True).
|
||||
The geometric information in the Summary object is embedded into a
|
||||
Multi-index column with a geometric "path" to each distribcell
|
||||
The geometric information in the Summary object is embedded into
|
||||
a Multi-index column with a geometric "path" to each distribcell
|
||||
instance.
|
||||
|
||||
Returns
|
||||
|
|
@ -4469,35 +4499,13 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
|
||||
"""
|
||||
|
||||
df = super().get_pandas_dataframe(groups, nuclides, xs_type, paths)
|
||||
# Build the dataframe using the parent class method
|
||||
df = super().get_pandas_dataframe(groups, nuclides, xs_type,
|
||||
paths=paths)
|
||||
|
||||
if self.scatter_format == 'legendre':
|
||||
# Add a moment column to dataframe
|
||||
if self.legendre_order > 0:
|
||||
# Insert a column corresponding to the Legendre moments
|
||||
moments = ['P{}'.format(i)
|
||||
for i in range(self.legendre_order + 1)]
|
||||
moments = np.tile(moments, int(df.shape[0] / len(moments)))
|
||||
df['moment'] = moments
|
||||
|
||||
# Place the moment column before the mean column
|
||||
columns = df.columns.tolist()
|
||||
mean_index \
|
||||
= [i for i, s in enumerate(columns) if 'mean' in s][0]
|
||||
if self.domain_type == 'mesh':
|
||||
df = df[columns[:mean_index] + [('moment', '')] +
|
||||
columns[mean_index:-1]]
|
||||
else:
|
||||
df = df[columns[:mean_index] + ['moment'] +
|
||||
columns[mean_index:-1]]
|
||||
|
||||
# Select rows corresponding to requested scattering moment
|
||||
if moment != 'all':
|
||||
cv.check_type('moment', moment, Integral)
|
||||
cv.check_greater_than('moment', moment, 0, equality=True)
|
||||
cv.check_less_than(
|
||||
'moment', moment, self.legendre_order, equality=True)
|
||||
df = df[df['moment'] == 'P{}'.format(moment)]
|
||||
# If the matrix is P0, remove the legendre column
|
||||
if self.scatter_format == 'legendre' and self.legendre_order == 0:
|
||||
df = df.drop(axis=1, labels=['legendre'])
|
||||
|
||||
return df
|
||||
|
||||
|
|
@ -4514,8 +4522,9 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
The nuclides of the cross-sections to include in the report. This
|
||||
may be a list of nuclide name strings (e.g., ['U235', 'U238']).
|
||||
The special string 'all' will report the cross sections for all
|
||||
nuclides in the spatial domain. The special string 'sum' will report
|
||||
the cross sections summed over all nuclides. Defaults to 'all'.
|
||||
nuclides in the spatial domain. The special string 'sum' will
|
||||
report the cross sections summed over all nuclides. Defaults to
|
||||
'all'.
|
||||
xs_type: {'macro', 'micro'}
|
||||
Return the macro or micro cross section in units of cm^-1 or barns.
|
||||
Defaults to 'macro'.
|
||||
|
|
@ -4530,8 +4539,7 @@ class ScatterMatrixXS(MatrixMGXS):
|
|||
elif self.domain_type == 'distribcell':
|
||||
subdomains = np.arange(self.num_subdomains, dtype=np.int)
|
||||
elif self.domain_type == 'mesh':
|
||||
xyz = [range(1, x + 1) for x in self.domain.dimension]
|
||||
subdomains = list(itertools.product(*xyz))
|
||||
subdomains = list(self.domain.indices)
|
||||
else:
|
||||
subdomains = [self.domain.id]
|
||||
|
||||
|
|
@ -4990,14 +4998,9 @@ class ScatterProbabilityMatrix(MatrixMGXS):
|
|||
def xs_tally(self):
|
||||
|
||||
if self._xs_tally is None:
|
||||
energyout_bins = [self.energy_groups.get_group_bounds(i)
|
||||
for i in range(self.num_groups, 0, -1)]
|
||||
norm = self.rxn_rate_tally.get_slice(scores=[self.rxn_type])
|
||||
norm = norm.summation(
|
||||
filter_type=openmc.EnergyoutFilter, filter_bins=energyout_bins)
|
||||
|
||||
# Remove the AggregateFilter summed across energyout bins
|
||||
norm._filters = norm._filters[:2]
|
||||
filter_type=openmc.EnergyoutFilter, remove_filter=True)
|
||||
|
||||
# Compute the group-to-group probabilities
|
||||
self._xs_tally = self.tallies[self.rxn_type] / norm
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
from collections.abc import Iterable
|
||||
|
||||
import openmc
|
||||
from openmc.checkvalue import check_type
|
||||
import openmc.deplete as dep
|
||||
|
||||
_DEPLETE_METHODS = {'predictor': dep.integrator.predictor,
|
||||
'cecm': dep.integrator.cecm}
|
||||
from openmc.checkvalue import check_type, check_value
|
||||
|
||||
|
||||
class Model(object):
|
||||
|
|
@ -140,7 +136,8 @@ class Model(object):
|
|||
for plot in plots:
|
||||
self._plots.append(plot)
|
||||
|
||||
def deplete(self, timesteps, power, chain_file=None, method='cecm', **kwargs):
|
||||
def deplete(self, timesteps, power, chain_file=None, method='cecm',
|
||||
**kwargs):
|
||||
"""Deplete model using specified timesteps/power
|
||||
|
||||
Parameters
|
||||
|
|
@ -164,11 +161,21 @@ class Model(object):
|
|||
:func:`openmc.deplete.integrator.cecm`)
|
||||
|
||||
"""
|
||||
# Import the depletion module. This is done here rather than the module
|
||||
# header to delay importing openmc.capi (through openmc.deplete) which
|
||||
# can be tough to install properly.
|
||||
import openmc.deplete as dep
|
||||
|
||||
# Create OpenMC transport operator
|
||||
op = dep.Operator(self.geometry, self.settings, chain_file)
|
||||
|
||||
# Perform depletion
|
||||
_DEPLETE_METHODS[method](op, timesteps, power, **kwargs)
|
||||
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'))
|
||||
|
||||
def export_to_xml(self):
|
||||
"""Export model to XML files."""
|
||||
|
|
@ -203,7 +210,7 @@ class Model(object):
|
|||
|
||||
Returns
|
||||
-------
|
||||
2-tuple of float
|
||||
uncertainties.UFloat
|
||||
Combined estimator of k-effective from the statepoint
|
||||
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -113,8 +113,7 @@ class _Domain(metaclass=ABCMeta):
|
|||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
Constraint on where particle center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
|
|
@ -158,8 +157,6 @@ class _Domain(metaclass=ABCMeta):
|
|||
raise ValueError('Unable to set domain center to {} since it must '
|
||||
'be of length 3'.format(center))
|
||||
self._center = [float(x) for x in center]
|
||||
self._limits = None
|
||||
self._cell_length = None
|
||||
|
||||
def mesh_cell(self, p):
|
||||
"""Calculate the index of the cell in a mesh overlaid on the domain in
|
||||
|
|
@ -211,6 +208,26 @@ class _Domain(metaclass=ABCMeta):
|
|||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def repel_particles(self, p, q, d, d_new):
|
||||
"""Move particles p and q apart according to the following
|
||||
transformation (accounting for boundary conditions on domain):
|
||||
|
||||
r_i^(n+1) = r_i^(n) + 1/2(d_out^(n+1) - d^(n))
|
||||
r_j^(n+1) = r_j^(n) - 1/2(d_out^(n+1) - d^(n))
|
||||
|
||||
Parameters
|
||||
----------
|
||||
p, q : numpy.ndarray
|
||||
Cartesian coordinates of particle center.
|
||||
d : float
|
||||
distance between centers of particles i and j.
|
||||
d_new : float
|
||||
final distance between centers of particles i and j.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class _CubicDomain(_Domain):
|
||||
"""Cubic container in which to pack particles.
|
||||
|
|
@ -238,7 +255,7 @@ class _CubicDomain(_Domain):
|
|||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
Maximum distance from center in x-, y-, or z-direction where particle
|
||||
center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
|
@ -256,9 +273,7 @@ class _CubicDomain(_Domain):
|
|||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
xlim = self.length/2 - self.particle_radius
|
||||
self._limits = [[x - xlim for x in self.center],
|
||||
[x + xlim for x in self.center]]
|
||||
self._limits = [self.length/2 - self.particle_radius]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
|
|
@ -284,9 +299,27 @@ class _CubicDomain(_Domain):
|
|||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
return [uniform(self.limits[0][0], self.limits[1][0]),
|
||||
uniform(self.limits[0][1], self.limits[1][1]),
|
||||
uniform(self.limits[0][2], self.limits[1][2])]
|
||||
x_max = self.limits[0]
|
||||
return [uniform(-x_max, x_max),
|
||||
uniform(-x_max, x_max),
|
||||
uniform(-x_max, x_max)]
|
||||
|
||||
def repel_particles(self, p, q, d, d_new):
|
||||
# Moving each particle distance 's' away from the other along the line
|
||||
# joining the particle centers will ensure their final distance is
|
||||
# equal to the outer diameter
|
||||
s = (d_new - d)/2
|
||||
|
||||
v = (p - q)/d
|
||||
p += s*v
|
||||
q -= s*v
|
||||
|
||||
# Enforce the rigid boundary by moving each particle back along the
|
||||
# surface normal until it is completely within the container if it
|
||||
# overlaps the surface
|
||||
x_max = self.limits[0]
|
||||
p[:] = np.clip(p, -x_max, x_max)
|
||||
q[:] = np.clip(q, -x_max, x_max)
|
||||
|
||||
|
||||
class _CylindricalDomain(_Domain):
|
||||
|
|
@ -317,8 +350,8 @@ class _CylindricalDomain(_Domain):
|
|||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
Maximum radial distance and maximum distance from center in z-direction
|
||||
where particle center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
|
|
@ -340,12 +373,8 @@ class _CylindricalDomain(_Domain):
|
|||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
xlim = self.length/2 - self.particle_radius
|
||||
rlim = self.radius - self.particle_radius
|
||||
self._limits = [[self.center[0] - rlim, self.center[1] - rlim,
|
||||
self.center[2] - xlim],
|
||||
[self.center[0] + rlim, self.center[1] + rlim,
|
||||
self.center[2] + xlim]]
|
||||
self._limits = [self.radius - self.particle_radius,
|
||||
self.length/2 - self.particle_radius]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
|
|
@ -377,10 +406,37 @@ class _CylindricalDomain(_Domain):
|
|||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
r = sqrt(uniform(0, (self.radius - self.particle_radius)**2))
|
||||
r_max = self.limits[0]
|
||||
z_max = self.limits[1]
|
||||
r = sqrt(uniform(0, r_max**2))
|
||||
t = uniform(0, 2*pi)
|
||||
return [r*cos(t) + self.center[0], r*sin(t) + self.center[1],
|
||||
uniform(self.limits[0][2], self.limits[1][2])]
|
||||
return [r*cos(t), r*sin(t), uniform(-z_max, z_max)]
|
||||
|
||||
def repel_particles(self, p, q, d, d_new):
|
||||
# Moving each particle distance 's' away from the other along the line
|
||||
# joining the particle centers will ensure their final distance is
|
||||
# equal to the outer diameter
|
||||
s = (d_new - d)/2
|
||||
|
||||
v = (p - q)/d
|
||||
p += s*v
|
||||
q -= s*v
|
||||
|
||||
# Enforce the rigid boundary by moving each particle back along the
|
||||
# surface normal until it is completely within the container if it
|
||||
# overlaps the surface
|
||||
r_max = self.limits[0]
|
||||
z_max = self.limits[1]
|
||||
|
||||
r = sqrt(p[0]**2 + p[1]**2)
|
||||
if r > r_max:
|
||||
p[0:2] *= r_max/r
|
||||
p[2] = np.clip(p[2], -z_max, z_max)
|
||||
|
||||
r = sqrt(q[0]**2 + q[1]**2)
|
||||
if r > r_max:
|
||||
q[0:2] *= r_max/r
|
||||
q[2] = np.clip(q[2], -z_max, z_max)
|
||||
|
||||
|
||||
class _SphericalDomain(_Domain):
|
||||
|
|
@ -407,8 +463,7 @@ class _SphericalDomain(_Domain):
|
|||
Length in x-, y-, and z- directions of each cell in mesh overlaid on
|
||||
domain.
|
||||
limits : list of float
|
||||
Minimum and maximum position in x-, y-, and z-directions where particle
|
||||
center can be placed.
|
||||
Maximum radial distance where particle center can be placed.
|
||||
volume : float
|
||||
Volume of the container.
|
||||
|
||||
|
|
@ -425,9 +480,7 @@ class _SphericalDomain(_Domain):
|
|||
@property
|
||||
def limits(self):
|
||||
if self._limits is None:
|
||||
rlim = self.radius - self.particle_radius
|
||||
self._limits = [[x - rlim for x in self.center],
|
||||
[x + rlim for x in self.center]]
|
||||
self._limits = [self.radius - self.particle_radius]
|
||||
return self._limits
|
||||
|
||||
@property
|
||||
|
|
@ -453,10 +506,33 @@ class _SphericalDomain(_Domain):
|
|||
self._limits = limits
|
||||
|
||||
def random_point(self):
|
||||
r_max = self.limits[0]
|
||||
x = (gauss(0, 1), gauss(0, 1), gauss(0, 1))
|
||||
r = (uniform(0, (self.radius - self.particle_radius)**3)**(1/3) /
|
||||
sqrt(x[0]**2 + x[1]**2 + x[2]**2))
|
||||
return [r*x[i] + self.center[i] for i in range(3)]
|
||||
r = (uniform(0, r_max**3)**(1/3) / sqrt(x[0]**2 + x[1]**2 + x[2]**2))
|
||||
return [r*s for s in x]
|
||||
|
||||
def repel_particles(self, p, q, d, d_new):
|
||||
# Moving each particle distance 's' away from the other along the line
|
||||
# joining the particle centers will ensure their final distance is
|
||||
# equal to the outer diameter
|
||||
s = (d_new - d)/2
|
||||
|
||||
v = (p - q)/d
|
||||
p += s*v
|
||||
q -= s*v
|
||||
|
||||
# Enforce the rigid boundary by moving each particle back along the
|
||||
# surface normal until it is completely within the container if it
|
||||
# overlaps the surface
|
||||
r_max = self.limits[0]
|
||||
|
||||
r = sqrt(p[0]**2 + p[1]**2 + p[2]**2)
|
||||
if r > r_max:
|
||||
p *= r_max/r
|
||||
|
||||
r = sqrt(q[0]**2 + q[1]**2 + q[2]**2)
|
||||
if r > r_max:
|
||||
q *= r_max/r
|
||||
|
||||
|
||||
def create_triso_lattice(trisos, lower_left, pitch, shape, background):
|
||||
|
|
@ -636,6 +712,7 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
del rods_map[i]
|
||||
del rods_map[j]
|
||||
return d, i, j
|
||||
return None, None, None
|
||||
|
||||
def create_rod_list():
|
||||
"""Generate sorted list of rods (distances between particle centers).
|
||||
|
|
@ -660,8 +737,8 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
# Find distance to nearest neighbor and index of nearest neighbor for
|
||||
# all particles
|
||||
d, n = tree.query(particles, k=2)
|
||||
d = d[:,1]
|
||||
n = n[:,1]
|
||||
d = d[:, 1]
|
||||
n = n[:, 1]
|
||||
|
||||
# Array of particle indices, indices of nearest neighbors, and
|
||||
# distances to nearest neighbors
|
||||
|
|
@ -670,8 +747,8 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
# Sort along second column and swap first and second columns to create
|
||||
# array of nearest neighbor indices, indices of particles they are
|
||||
# nearest neighbors of, and distances between them
|
||||
b = a[a[:,1].argsort()]
|
||||
b[:,[0, 1]] = b[:,[1, 0]]
|
||||
b = a[a[:, 1].argsort()]
|
||||
b[:, [0, 1]] = b[:, [1, 0]]
|
||||
|
||||
# Find the intersection between 'a' and 'b': a list of particles who
|
||||
# are each other's nearest neighbors and the distance between them
|
||||
|
|
@ -685,12 +762,8 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
del rods[:]
|
||||
rods_map.clear()
|
||||
for d, i, j in r:
|
||||
add_rod(d, i, j)
|
||||
|
||||
# Inner diameter is set initially to the shortest center-to-center
|
||||
# distance between any two particles
|
||||
if rods:
|
||||
inner_diameter[0] = rods[0][0]
|
||||
if d < outer_diameter and not np.isclose(d, outer_diameter, atol=1.0e-14):
|
||||
add_rod(d, i, j)
|
||||
|
||||
def update_mesh(i):
|
||||
"""Update which mesh cells the particle is in based on new particle
|
||||
|
|
@ -729,50 +802,19 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
|
||||
j = floor(-log10(pf_out - pf_in)).
|
||||
|
||||
Returns
|
||||
-------
|
||||
float
|
||||
New outer diameter
|
||||
|
||||
"""
|
||||
|
||||
inner_pf = (4/3 * pi * (inner_diameter[0]/2)**3 * n_particles /
|
||||
domain.volume)
|
||||
outer_pf = (4/3 * pi * (outer_diameter[0]/2)**3 * n_particles /
|
||||
domain.volume)
|
||||
inner_pf = 4/3*pi*(inner_diameter/2)**3*n_particles/domain.volume
|
||||
outer_pf = 4/3*pi*(outer_diameter/2)**3*n_particles/domain.volume
|
||||
|
||||
j = floor(-log10(outer_pf - inner_pf))
|
||||
outer_diameter[0] = (outer_diameter[0] - 0.5**j * contraction_rate *
|
||||
initial_outer_diameter / n_particles)
|
||||
|
||||
|
||||
def repel_particles(i, j, d):
|
||||
"""Move particles p and q apart according to the following
|
||||
transformation (accounting for reflective boundary conditions on
|
||||
domain):
|
||||
|
||||
r_i^(n+1) = r_i^(n) + 1/2(d_out^(n+1) - d^(n))
|
||||
r_j^(n+1) = r_j^(n) - 1/2(d_out^(n+1) - d^(n))
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
d : float
|
||||
distance between centers of particles i and j.
|
||||
|
||||
"""
|
||||
|
||||
# Moving each particle distance 'r' away from the other along the line
|
||||
# joining the particle centers will ensure their final distance is equal
|
||||
# to the outer diameter
|
||||
r = (outer_diameter[0] - d)/2
|
||||
|
||||
v = (particles[i] - particles[j])/d
|
||||
particles[i] += r*v
|
||||
particles[j] -= r*v
|
||||
|
||||
# Apply reflective boundary conditions
|
||||
particles[i] = particles[i].clip(domain.limits[0], domain.limits[1])
|
||||
particles[j] = particles[j].clip(domain.limits[0], domain.limits[1])
|
||||
|
||||
update_mesh(i)
|
||||
update_mesh(j)
|
||||
return (outer_diameter - 0.5**j * contraction_rate *
|
||||
initial_outer_diameter / n_particles)
|
||||
|
||||
def nearest(i):
|
||||
"""Find index of nearest neighbor of particle i.
|
||||
|
|
@ -803,14 +845,14 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
else:
|
||||
return None, None
|
||||
|
||||
def update_rod_list(i, j):
|
||||
"""Update the rod list with the new nearest neighbors of particles i
|
||||
and j since their overlap was eliminated.
|
||||
def update_rod_list(i):
|
||||
"""Update the rod list with the new nearest neighbors of particle since
|
||||
its overlap was eliminated.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
i, j : int
|
||||
Index of particles in particles array.
|
||||
i : int
|
||||
Index of particle in particles array.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -818,18 +860,10 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
# remove the rod currently containing k from the rod list and add rod
|
||||
# k-i, keeping the rod list sorted
|
||||
k, d_ik = nearest(i)
|
||||
if k and nearest(k)[0] == i:
|
||||
if (k and nearest(k)[0] == i and d_ik < outer_diameter
|
||||
and not np.isclose(d, outer_diameter, atol=1.0e-14)):
|
||||
remove_rod(k)
|
||||
add_rod(d_ik, i, k)
|
||||
l, d_jl = nearest(j)
|
||||
if l and nearest(l)[0] == j:
|
||||
remove_rod(l)
|
||||
add_rod(d_jl, j, l)
|
||||
|
||||
# Set inner diameter to the shortest distance between two particle
|
||||
# centers
|
||||
if rods:
|
||||
inner_diameter[0] = rods[0][0]
|
||||
|
||||
n_particles = len(particles)
|
||||
diameter = 2*domain.particle_radius
|
||||
|
|
@ -841,36 +875,71 @@ def _close_random_pack(domain, particles, contraction_rate):
|
|||
initial_outer_diameter = 2*(domain.volume/(n_particles*4/3*pi))**(1/3)
|
||||
|
||||
# Inner and outer diameter of particles will change during packing
|
||||
outer_diameter = [initial_outer_diameter]
|
||||
inner_diameter = [0]
|
||||
outer_diameter = initial_outer_diameter
|
||||
inner_diameter = 0.
|
||||
|
||||
# List of rods arranged in a heap and mapping of particle ids to rods
|
||||
rods = []
|
||||
rods_map = {}
|
||||
|
||||
# Initialize two-way dictionary that identifies which particles are near a
|
||||
# given mesh cell and which mesh cells a particle is near
|
||||
mesh = defaultdict(set)
|
||||
mesh_map = defaultdict(set)
|
||||
|
||||
for i in range(n_particles):
|
||||
for idx in domain.nearby_mesh_cells(particles[i]):
|
||||
mesh[idx].add(i)
|
||||
mesh_map[i].add(idx)
|
||||
|
||||
while True:
|
||||
# Rebuild the sorted list of rods according to the current particle
|
||||
# configuration
|
||||
create_rod_list()
|
||||
if inner_diameter[0] >= diameter:
|
||||
|
||||
# Set the inner diameter to the shortest center-to-center distance
|
||||
# between any two particles
|
||||
if rods:
|
||||
inner_diameter = rods[0][0]
|
||||
|
||||
# Reached the desired particle radius
|
||||
if inner_diameter >= diameter:
|
||||
break
|
||||
|
||||
# The algorithm converged before reaching the desired particle radius.
|
||||
# This can happen when the desired packing fraction is close to the
|
||||
# packing fraction limit. The packing fraction is a random variable
|
||||
# that is determined by the particle locations and the contraction
|
||||
# rate. A higher packing fraction can be achieved with a smaller
|
||||
# contraction rate, though at the cost of a longer simulation time --
|
||||
# the number of iterations needed to remove all overlaps is inversely
|
||||
# proportional to the contraction rate.
|
||||
if inner_diameter >= outer_diameter or not rods:
|
||||
warnings.warn('Close random pack converged before reaching true '
|
||||
'particle radius; some particles may overlap. Try '
|
||||
'reducing contraction rate or packing fraction.')
|
||||
break
|
||||
|
||||
while True:
|
||||
d, i, j = pop_rod()
|
||||
reduce_outer_diameter()
|
||||
repel_particles(i, j, d)
|
||||
update_rod_list(i, j)
|
||||
if inner_diameter[0] >= diameter or not rods:
|
||||
if not d:
|
||||
break
|
||||
outer_diameter = reduce_outer_diameter()
|
||||
domain.repel_particles(particles[i], particles[j], d, outer_diameter)
|
||||
update_mesh(i)
|
||||
update_mesh(j)
|
||||
update_rod_list(i)
|
||||
update_rod_list(j)
|
||||
if not rods:
|
||||
break
|
||||
inner_diameter = rods[0][0]
|
||||
if inner_diameter >= diameter or inner_diameter >= outer_diameter:
|
||||
break
|
||||
|
||||
|
||||
def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None,
|
||||
domain_radius=None, domain_center=[0., 0., 0.],
|
||||
n_particles=None, packing_fraction=None,
|
||||
initial_packing_fraction=0.3, contraction_rate=1/400, seed=1):
|
||||
initial_packing_fraction=0.3, contraction_rate=1.e-3, seed=1):
|
||||
"""Generate a random, non-overlapping configuration of TRISO particles
|
||||
within a container.
|
||||
|
||||
|
|
@ -933,7 +1002,7 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None,
|
|||
to speed up the nearest neighbor search by only searching for a particle's
|
||||
neighbors within that mesh cell.
|
||||
|
||||
In CRP, each particle is assigned two diameters, and inner and an outer,
|
||||
In CRP, each particle is assigned two diameters, an inner and an outer,
|
||||
which approach each other during the simulation. The inner diameter,
|
||||
defined as the minimum center-to-center distance, is the true diameter of
|
||||
the particles and defines the pf. At each iteration the worst overlap
|
||||
|
|
@ -1008,8 +1077,7 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None,
|
|||
# Recalculate the limits for the initial random sequential packing using
|
||||
# the desired final particle radius to ensure particles are fully contained
|
||||
# within the domain during the close random pack
|
||||
domain.limits = [[x - initial_radius + radius for x in domain.limits[0]],
|
||||
[x + initial_radius - radius for x in domain.limits[1]]]
|
||||
domain.limits = [x + initial_radius - radius for x in domain.limits]
|
||||
|
||||
# Generate non-overlapping particles for an initial inner radius using
|
||||
# random sequential packing algorithm
|
||||
|
|
@ -1024,5 +1092,5 @@ def pack_trisos(radius, fill, domain_shape='cylinder', domain_length=None,
|
|||
|
||||
trisos = []
|
||||
for p in particles:
|
||||
trisos.append(TRISO(radius, fill, p))
|
||||
trisos.append(TRISO(radius, fill, [x + c for x, c in zip(p, domain.center)]))
|
||||
return trisos
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from collections.abc import Iterable, Mapping
|
||||
from numbers import Real, Integral
|
||||
from xml.etree import ElementTree as ET
|
||||
import subprocess
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
|
|
@ -650,6 +651,49 @@ class Plot(IDManagerMixin):
|
|||
|
||||
return element
|
||||
|
||||
def to_ipython_image(self, openmc_exec='openmc', cwd='.',
|
||||
convert_exec='convert'):
|
||||
"""Render plot as an image
|
||||
|
||||
This method runs OpenMC in plotting mode to produce a bitmap image which
|
||||
is then converted to a .png file and loaded in as an
|
||||
:class:`IPython.display.Image` object. As such, it requires that your
|
||||
model geometry, materials, and settings have already been exported to
|
||||
XML.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
openmc_exec : str
|
||||
Path to OpenMC executable
|
||||
cwd : str, optional
|
||||
Path to working directory to run in
|
||||
convert_exec : str, optional
|
||||
Command that can convert PPM files into PNG files
|
||||
|
||||
Returns
|
||||
-------
|
||||
IPython.display.Image
|
||||
Image generated
|
||||
|
||||
"""
|
||||
from IPython.display import Image
|
||||
|
||||
# Create plots.xml
|
||||
Plots([self]).export_to_xml()
|
||||
|
||||
# Run OpenMC in geometry plotting mode
|
||||
openmc.plot_geometry(False, openmc_exec, cwd)
|
||||
|
||||
# Convert to .png
|
||||
if self.filename is not None:
|
||||
ppm_file = '{}.ppm'.format(self.filename)
|
||||
else:
|
||||
ppm_file = 'plot_{}.ppm'.format(self.id)
|
||||
png_file = ppm_file.replace('.ppm', '.png')
|
||||
subprocess.check_call([convert_exec, ppm_file, png_file])
|
||||
|
||||
return Image(png_file)
|
||||
|
||||
|
||||
class Plots(cv.CheckedList):
|
||||
"""Collection of Plots used for an OpenMC simulation.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from numbers import Integral, Real
|
|||
from itertools import chain
|
||||
import string
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
import openmc.checkvalue as cv
|
||||
|
|
@ -125,6 +124,8 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None,
|
|||
generated.
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
cv.check_type("plot_CE", plot_CE, bool)
|
||||
|
||||
if data_type is None:
|
||||
|
|
@ -169,13 +170,13 @@ def plot_xs(this, types, divisor_types=None, temperature=294., data_type=None,
|
|||
data = data_new
|
||||
else:
|
||||
# Calculate for MG cross sections
|
||||
E, data = calculate_mgxs(this, types, orders, temperature,
|
||||
E, data = calculate_mgxs(this, data_type, types, orders, temperature,
|
||||
mg_cross_sections, ce_cross_sections,
|
||||
enrichment)
|
||||
|
||||
if divisor_types:
|
||||
cv.check_length('divisor types', divisor_types, len(types))
|
||||
Ediv, data_div = calculate_mgxs(this, divisor_types,
|
||||
Ediv, data_div = calculate_mgxs(this, data_type, divisor_types,
|
||||
divisor_orders, temperature,
|
||||
mg_cross_sections,
|
||||
ce_cross_sections, enrichment)
|
||||
|
|
@ -242,7 +243,7 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None,
|
|||
|
||||
Parameters
|
||||
----------
|
||||
this : str or openmc.Material
|
||||
this : {str, openmc.Nuclide, openmc.Element, openmc.Material}
|
||||
Object to source data from
|
||||
data_type : {'nuclide', 'element', material'}
|
||||
Type of object to plot
|
||||
|
|
@ -279,7 +280,11 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None,
|
|||
cv.check_type('enrichment', enrichment, Real)
|
||||
|
||||
if data_type == 'nuclide':
|
||||
energy_grid, xs = _calculate_cexs_nuclide(this, types, temperature,
|
||||
if isinstance(this, str):
|
||||
nuc = openmc.Nuclide(this)
|
||||
else:
|
||||
nuc = this
|
||||
energy_grid, xs = _calculate_cexs_nuclide(nuc, types, temperature,
|
||||
sab_name, cross_sections)
|
||||
# Convert xs (Iterable of Callable) to a grid of cross section values
|
||||
# calculated on @ the points in energy_grid for consistency with the
|
||||
|
|
@ -288,10 +293,15 @@ def calculate_cexs(this, data_type, types, temperature=294., sab_name=None,
|
|||
for line in range(len(types)):
|
||||
data[line, :] = xs[line](energy_grid)
|
||||
elif data_type == 'element':
|
||||
energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature,
|
||||
if isinstance(this, str):
|
||||
elem = openmc.Element(this)
|
||||
else:
|
||||
elem = this
|
||||
energy_grid, data = _calculate_cexs_elem_mat(elem, types, temperature,
|
||||
cross_sections, sab_name,
|
||||
enrichment)
|
||||
elif data_type == 'material':
|
||||
cv.check_type('this', this, openmc.Material)
|
||||
energy_grid, data = _calculate_cexs_elem_mat(this, types, temperature,
|
||||
cross_sections)
|
||||
else:
|
||||
|
|
@ -517,10 +527,8 @@ def _calculate_cexs_elem_mat(this, types, temperature=294.,
|
|||
T = this.temperature
|
||||
else:
|
||||
T = temperature
|
||||
data_type = 'material'
|
||||
else:
|
||||
T = temperature
|
||||
data_type = 'element'
|
||||
|
||||
# Load the library
|
||||
library = openmc.data.DataLibrary.from_xml(cross_sections)
|
||||
|
|
@ -570,7 +578,7 @@ def _calculate_cexs_elem_mat(this, types, temperature=294.,
|
|||
name = nuclide[0]
|
||||
nuc = nuclide[1]
|
||||
sab_tab = sabs[name]
|
||||
temp_E, temp_xs = calculate_cexs(nuc, data_type, types, T, sab_tab,
|
||||
temp_E, temp_xs = calculate_cexs(nuc, 'nuclide', types, T, sab_tab,
|
||||
cross_sections)
|
||||
E.append(temp_E)
|
||||
# Since the energy grids are different, store the cross sections as
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@ def _search_keff(guess, target, model_builder, model_args, print_iterations,
|
|||
if print_iterations:
|
||||
text = 'Iteration: {}; Guess of {:.2e} produced a keff of ' + \
|
||||
'{:1.5f} +/- {:1.5f}'
|
||||
print(text.format(len(guesses), guess, keff[0], keff[1]))
|
||||
print(text.format(len(guesses), guess, keff.n, keff.s))
|
||||
|
||||
return (keff[0] - target)
|
||||
return keff.n - target
|
||||
|
||||
|
||||
def search_for_keff(model_builder, initial_guess=None, target=1.0,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from datetime import datetime
|
||||
import re
|
||||
import os
|
||||
import warnings
|
||||
|
|
@ -5,6 +6,7 @@ import glob
|
|||
|
||||
import numpy as np
|
||||
import h5py
|
||||
from uncertainties import ufloat
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
|
|
@ -47,8 +49,8 @@ class StatePoint(object):
|
|||
CMFD fission source distribution over all mesh cells and energy groups.
|
||||
current_batch : int
|
||||
Number of batches simulated
|
||||
date_and_time : str
|
||||
Date and time when simulation began
|
||||
date_and_time : datetime.datetime
|
||||
Date and time at which statepoint was written
|
||||
entropy : numpy.ndarray
|
||||
Shannon entropy of fission source at each batch
|
||||
filters : dict
|
||||
|
|
@ -59,8 +61,8 @@ class StatePoint(object):
|
|||
global_tallies : numpy.ndarray of compound datatype
|
||||
Global tallies for k-effective estimates and leakage. The compound
|
||||
datatype has fields 'name', 'sum', 'sum_sq', 'mean', and 'std_dev'.
|
||||
k_combined : list
|
||||
Combined estimator for k-effective and its uncertainty
|
||||
k_combined : uncertainties.UFloat
|
||||
Combined estimator for k-effective
|
||||
k_col_abs : float
|
||||
Cross-product of collision and absorption estimates of k-effective
|
||||
k_col_tra : float
|
||||
|
|
@ -148,6 +150,8 @@ class StatePoint(object):
|
|||
|
||||
def __exit__(self, *exc):
|
||||
self._f.close()
|
||||
if self._summary is not None:
|
||||
self._summary._f.close()
|
||||
|
||||
@property
|
||||
def cmfd_on(self):
|
||||
|
|
@ -187,7 +191,8 @@ class StatePoint(object):
|
|||
|
||||
@property
|
||||
def date_and_time(self):
|
||||
return self._f.attrs['date_and_time'].decode()
|
||||
s = self._f.attrs['date_and_time'].decode()
|
||||
return datetime.strptime(s, '%Y-%m-%d %H:%M:%S')
|
||||
|
||||
@property
|
||||
def entropy(self):
|
||||
|
|
@ -255,7 +260,7 @@ class StatePoint(object):
|
|||
@property
|
||||
def k_combined(self):
|
||||
if self.run_mode == 'eigenvalue':
|
||||
return self._f['k_combined'].value
|
||||
return ufloat(*self._f['k_combined'].value)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
|
@ -402,17 +407,10 @@ class StatePoint(object):
|
|||
scores = group['score_bins'].value
|
||||
n_score_bins = group['n_score_bins'].value
|
||||
|
||||
# Read scattering moment order strings (e.g., P3, Y1,2, etc.)
|
||||
moments = group['moment_orders'].value
|
||||
|
||||
# Add the scores to the Tally
|
||||
for j, score in enumerate(scores):
|
||||
score = score.decode()
|
||||
|
||||
# If this is a moment, use generic moment order
|
||||
pattern = r'-n$|-pn$|-yn$'
|
||||
score = re.sub(pattern, '-' + moments[j].decode(), score)
|
||||
|
||||
tally.scores.append(score)
|
||||
|
||||
# Add Tally to the global dictionary of all Tallies
|
||||
|
|
@ -457,7 +455,7 @@ class StatePoint(object):
|
|||
|
||||
@property
|
||||
def version(self):
|
||||
return tuple(self._f.attrs['version'])
|
||||
return tuple(self._f.attrs['openmc_version'])
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import openmc
|
|||
import openmc.checkvalue as cv
|
||||
from openmc.region import Region
|
||||
|
||||
_VERSION_SUMMARY = 5
|
||||
_VERSION_SUMMARY = 6
|
||||
|
||||
|
||||
class Summary(object):
|
||||
|
|
@ -26,6 +26,8 @@ class Summary(object):
|
|||
nuclides : dict
|
||||
Dictionary whose keys are nuclide names and values are atomic weight
|
||||
ratios.
|
||||
macroscopics : list
|
||||
Names of macroscopic data sets
|
||||
version: tuple of int
|
||||
Version of OpenMC
|
||||
|
||||
|
|
@ -44,13 +46,15 @@ class Summary(object):
|
|||
self._fast_materials = {}
|
||||
self._fast_surfaces = {}
|
||||
self._fast_cells = {}
|
||||
self._fast_universes = {}
|
||||
self._fast_universes = {}
|
||||
self._fast_lattices = {}
|
||||
|
||||
self._materials = openmc.Materials()
|
||||
self._nuclides = {}
|
||||
self._macroscopics = []
|
||||
|
||||
self._read_nuclides()
|
||||
self._read_macroscopics()
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", openmc.IDWarning)
|
||||
self._read_geometry()
|
||||
|
|
@ -71,15 +75,26 @@ class Summary(object):
|
|||
def nuclides(self):
|
||||
return self._nuclides
|
||||
|
||||
@property
|
||||
def macroscopics(self):
|
||||
return self._macroscopics
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return tuple(self._f.attrs['openmc_version'])
|
||||
|
||||
def _read_nuclides(self):
|
||||
names = self._f['nuclides/names'].value
|
||||
awrs = self._f['nuclides/awrs'].value
|
||||
for name, awr in zip(names, awrs):
|
||||
self._nuclides[name.decode()] = awr
|
||||
if 'nuclides/names' in self._f:
|
||||
names = self._f['nuclides/names'].value
|
||||
awrs = self._f['nuclides/awrs'].value
|
||||
for name, awr in zip(names, awrs):
|
||||
self._nuclides[name.decode()] = awr
|
||||
|
||||
def _read_macroscopics(self):
|
||||
if 'macroscopics/names' in self._f:
|
||||
names = self._f['macroscopics/names'].value
|
||||
for name in names:
|
||||
self._macroscopics = name.decode()
|
||||
|
||||
def _read_geometry(self):
|
||||
# Read in and initialize the Materials and Geometry
|
||||
|
|
|
|||
|
|
@ -65,9 +65,7 @@ class Tally(IDManagerMixin):
|
|||
triggers : list of openmc.Trigger
|
||||
List of tally triggers
|
||||
num_scores : int
|
||||
Total number of scores, accounting for the fact that a single
|
||||
user-specified score, e.g. scatter-P3 or flux-Y2,2, might have multiple
|
||||
bins
|
||||
Total number of scores
|
||||
num_filter_bins : int
|
||||
Total number of filter bins accounting for all filters
|
||||
num_bins : int
|
||||
|
|
@ -218,10 +216,9 @@ class Tally(IDManagerMixin):
|
|||
f = h5py.File(self._sp_filename, 'r')
|
||||
|
||||
# Extract Tally data from the file
|
||||
data = f['tallies/tally {0}/results'.format(
|
||||
self.id)].value
|
||||
sum = data[:,:,0]
|
||||
sum_sq = data[:,:,1]
|
||||
data = f['tallies/tally {0}/results'.format(self.id)].value
|
||||
sum = data[:, :, 0]
|
||||
sum_sq = data[:, :, 1]
|
||||
|
||||
# Reshape the results arrays
|
||||
sum = np.reshape(sum, self.shape)
|
||||
|
|
@ -251,7 +248,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
@property
|
||||
def sum_sq(self):
|
||||
if not self._sp_filename:
|
||||
if not self._sp_filename or self.derived:
|
||||
return None
|
||||
|
||||
if not self._results_read:
|
||||
|
|
@ -273,8 +270,8 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Convert NumPy array to SciPy sparse LIL matrix
|
||||
if self.sparse:
|
||||
self._mean = \
|
||||
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
|
||||
self._mean = sps.lil_matrix(self._mean.flatten(),
|
||||
self._mean.shape)
|
||||
|
||||
if self.sparse:
|
||||
return np.reshape(self._mean.toarray(), self.shape)
|
||||
|
|
@ -295,8 +292,8 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Convert NumPy array to SciPy sparse LIL matrix
|
||||
if self.sparse:
|
||||
self._std_dev = \
|
||||
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
|
||||
self._std_dev = sps.lil_matrix(self._std_dev.flatten(),
|
||||
self._std_dev.shape)
|
||||
|
||||
self.with_batch_statistics = True
|
||||
|
||||
|
|
@ -389,6 +386,13 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# If score is a string, strip whitespace
|
||||
if isinstance(score, str):
|
||||
# Check to see if scores are deprecated before storing
|
||||
for deprecated in ['scatter-', 'nu-scatter-', 'scatter-p',
|
||||
'nu-scatter-p', 'scatter-y', 'nu-scatter-y',
|
||||
'flux-y', 'total-y']:
|
||||
if score.startswith(deprecated):
|
||||
msg = score.strip() + ' is no longer supported.'
|
||||
raise ValueError(msg)
|
||||
scores[i] = score.strip()
|
||||
|
||||
self._scores = cv.CheckedList(_SCORE_CLASSES, 'tally scores', scores)
|
||||
|
|
@ -436,17 +440,16 @@ class Tally(IDManagerMixin):
|
|||
# Convert NumPy arrays to SciPy sparse LIL matrices
|
||||
if sparse and not self.sparse:
|
||||
if self._sum is not None:
|
||||
self._sum = \
|
||||
sps.lil_matrix(self._sum.flatten(), self._sum.shape)
|
||||
self._sum = sps.lil_matrix(self._sum.flatten(), self._sum.shape)
|
||||
if self._sum_sq is not None:
|
||||
self._sum_sq = \
|
||||
sps.lil_matrix(self._sum_sq.flatten(), self._sum_sq.shape)
|
||||
self._sum_sq = sps.lil_matrix(self._sum_sq.flatten(),
|
||||
self._sum_sq.shape)
|
||||
if self._mean is not None:
|
||||
self._mean = \
|
||||
sps.lil_matrix(self._mean.flatten(), self._mean.shape)
|
||||
self._mean = sps.lil_matrix(self._mean.flatten(),
|
||||
self._mean.shape)
|
||||
if self._std_dev is not None:
|
||||
self._std_dev = \
|
||||
sps.lil_matrix(self._std_dev.flatten(), self._std_dev.shape)
|
||||
self._std_dev = sps.lil_matrix(self._std_dev.flatten(),
|
||||
self._std_dev.shape)
|
||||
|
||||
self._sparse = True
|
||||
|
||||
|
|
@ -776,11 +779,11 @@ class Tally(IDManagerMixin):
|
|||
other_sum = other_copy.get_reshaped_data(value='sum')
|
||||
|
||||
if join_right:
|
||||
merged_sum = \
|
||||
np.concatenate((self_sum, other_sum), axis=merge_axis)
|
||||
merged_sum = np.concatenate((self_sum, other_sum),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_sum = \
|
||||
np.concatenate((other_sum, self_sum), axis=merge_axis)
|
||||
merged_sum = np.concatenate((other_sum, self_sum),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._sum = np.reshape(merged_sum, merged_tally.shape)
|
||||
|
||||
|
|
@ -790,11 +793,11 @@ class Tally(IDManagerMixin):
|
|||
other_sum_sq = other_copy.get_reshaped_data(value='sum_sq')
|
||||
|
||||
if join_right:
|
||||
merged_sum_sq = \
|
||||
np.concatenate((self_sum_sq, other_sum_sq), axis=merge_axis)
|
||||
merged_sum_sq = np.concatenate((self_sum_sq, other_sum_sq),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_sum_sq = \
|
||||
np.concatenate((other_sum_sq, self_sum_sq), axis=merge_axis)
|
||||
merged_sum_sq = np.concatenate((other_sum_sq, self_sum_sq),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._sum_sq = np.reshape(merged_sum_sq, merged_tally.shape)
|
||||
|
||||
|
|
@ -804,11 +807,11 @@ class Tally(IDManagerMixin):
|
|||
other_mean = other_copy.get_reshaped_data(value='mean')
|
||||
|
||||
if join_right:
|
||||
merged_mean = \
|
||||
np.concatenate((self_mean, other_mean), axis=merge_axis)
|
||||
merged_mean = np.concatenate((self_mean, other_mean),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_mean = \
|
||||
np.concatenate((other_mean, self_mean), axis=merge_axis)
|
||||
merged_mean = np.concatenate((other_mean, self_mean),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._mean = np.reshape(merged_mean, merged_tally.shape)
|
||||
|
||||
|
|
@ -818,62 +821,19 @@ class Tally(IDManagerMixin):
|
|||
other_std_dev = other_copy.get_reshaped_data(value='std_dev')
|
||||
|
||||
if join_right:
|
||||
merged_std_dev = \
|
||||
np.concatenate((self_std_dev, other_std_dev), axis=merge_axis)
|
||||
merged_std_dev = np.concatenate((self_std_dev, other_std_dev),
|
||||
axis=merge_axis)
|
||||
else:
|
||||
merged_std_dev = \
|
||||
np.concatenate((other_std_dev, self_std_dev), axis=merge_axis)
|
||||
merged_std_dev = np.concatenate((other_std_dev, self_std_dev),
|
||||
axis=merge_axis)
|
||||
|
||||
merged_tally._std_dev = np.reshape(merged_std_dev, merged_tally.shape)
|
||||
|
||||
# Sparsify merged tally if both tallies are sparse
|
||||
merged_tally.sparse = self.sparse and other.sparse
|
||||
|
||||
# Consolidate scatter and flux Legendre moment scores
|
||||
merged_tally._consolidate_moment_scores()
|
||||
|
||||
return merged_tally
|
||||
|
||||
def _consolidate_moment_scores(self):
|
||||
"""Remove redundant scattering and flux moment scores from a Tally."""
|
||||
|
||||
# Define regex for scatter, nu-scatter and flux moment scores
|
||||
regex = [(r'^((?!nu-)scatter-\d)', r'^((?!nu-)scatter-(P|p)\d)'),
|
||||
(r'nu-scatter-\d', r'nu-scatter-(P|p)\d'),
|
||||
(r'flux-\d', r'flux-(P|p)\d')]
|
||||
|
||||
# Find all non-scattering and non-flux moment scores
|
||||
scores = [x for x in self.scores if
|
||||
re.search(r'^((?!scatter-).)*$', x)]
|
||||
scores = [x for x in scores if
|
||||
re.search(r'^((?!flux-).)*$', x)]
|
||||
|
||||
for regex_n, regex_pn in regex:
|
||||
|
||||
# Use regex to find score-(P)n scores
|
||||
score_n = [x for x in self.scores if re.search(regex_n, x)]
|
||||
score_pn = [x for x in self.scores if re.search(regex_pn, x)]
|
||||
|
||||
# Consolidate moment scores
|
||||
if len(score_pn) > 0:
|
||||
|
||||
# Only keep the highest score-PN score
|
||||
high_pn = sorted([x.lower() for x in score_pn])[-1]
|
||||
pn = int(high_pn.split('-')[-1].replace('p', ''))
|
||||
|
||||
# Only keep the score-N scores with N > PN
|
||||
score_n = sorted([x.lower() for x in score_n])
|
||||
score_n = [x for x in score_n if (int(x.split('-')[1]) > pn)]
|
||||
|
||||
# Append highest score-PN and any higher score-N scores
|
||||
scores.extend([high_pn] + score_n)
|
||||
else:
|
||||
scores.extend(score_n)
|
||||
|
||||
# Override Tally's scores with consolidated list of scores
|
||||
self.scores = scores
|
||||
|
||||
|
||||
def to_xml_element(self):
|
||||
"""Return XML representation of the tally
|
||||
|
||||
|
|
@ -1003,35 +963,6 @@ class Tally(IDManagerMixin):
|
|||
|
||||
return filter_found
|
||||
|
||||
def get_filter_index(self, filter_type, filter_bin):
|
||||
"""Returns the index in the Tally's results array for a Filter bin
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filter_type : openmc.FilterMeta
|
||||
Type of the filter, e.g. MeshFilter
|
||||
filter_bin : int or tuple
|
||||
The bin is an integer ID for 'material', 'surface', 'cell',
|
||||
'cellborn', and 'universe' Filters. The bin is an integer for the
|
||||
cell instance ID for 'distribcell' Filters. The bin is a 2-tuple of
|
||||
floats for 'energy' and 'energyout' filters corresponding to the
|
||||
energy boundaries of the bin of interest. The bin is a (x,y,z)
|
||||
3-tuple for 'mesh' filters corresponding to the mesh cell of
|
||||
interest.
|
||||
|
||||
Returns
|
||||
-------
|
||||
The index in the Tally data array for this filter bin
|
||||
|
||||
"""
|
||||
|
||||
# Find the equivalent Filter in this Tally's list of Filters
|
||||
filter_found = self.find_filter(filter_type)
|
||||
|
||||
# Get the index for the requested bin from the Filter and return it
|
||||
filter_index = filter_found.get_bin_index(filter_bin)
|
||||
return filter_index
|
||||
|
||||
def get_nuclide_index(self, nuclide):
|
||||
"""Returns the index in the Tally's results array for a Nuclide bin
|
||||
|
||||
|
|
@ -1151,50 +1082,28 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Loop over all of the Tally's Filters
|
||||
for i, self_filter in enumerate(self.filters):
|
||||
user_filter = False
|
||||
|
||||
# If a user-requested Filter, get the user-requested bins
|
||||
for j, test_filter in enumerate(filters):
|
||||
if type(self_filter) is test_filter:
|
||||
bins = filter_bins[j]
|
||||
user_filter = True
|
||||
break
|
||||
else:
|
||||
# If not a user-requested Filter, get all bins
|
||||
if isinstance(self_filter, openmc.DistribcellFilter):
|
||||
# Create list of cell instance IDs for distribcell Filters
|
||||
bins = list(range(self_filter.num_bins))
|
||||
|
||||
# If not a user-requested Filter, get all bins
|
||||
if not user_filter:
|
||||
# Create list of 2- or 3-tuples tuples for mesh cell bins
|
||||
if isinstance(self_filter, openmc.MeshFilter):
|
||||
dimension = self_filter.mesh.dimension
|
||||
xyz = [range(1, x+1) for x in dimension]
|
||||
bins = list(product(*xyz))
|
||||
|
||||
# Create list of 2-tuples for energy boundary bins
|
||||
elif isinstance(self_filter, (openmc.EnergyFilter,
|
||||
openmc.EnergyoutFilter, openmc.MuFilter,
|
||||
openmc.PolarFilter, openmc.AzimuthalFilter)):
|
||||
bins = []
|
||||
for k in range(self_filter.num_bins):
|
||||
bins.append((self_filter.bins[k], self_filter.bins[k+1]))
|
||||
|
||||
# Create list of cell instance IDs for distribcell Filters
|
||||
elif isinstance(self_filter, openmc.DistribcellFilter):
|
||||
bins = [b for b in range(self_filter.num_bins)]
|
||||
|
||||
# EnergyFunctionFilters don't have bins so just add a None
|
||||
elif isinstance(self_filter, openmc.EnergyFunctionFilter):
|
||||
# EnergyFunctionFilters don't have bins so just add a None
|
||||
bins = [None]
|
||||
|
||||
# Create list of IDs for bins for all other filter types
|
||||
else:
|
||||
# Create list of IDs for bins for all other filter types
|
||||
bins = self_filter.bins
|
||||
|
||||
# Initialize a NumPy array for the Filter bin indices
|
||||
filter_indices.append(np.zeros(len(bins), dtype=np.int))
|
||||
|
||||
# Add indices for each bin in this Filter to the list
|
||||
for j, bin in enumerate(bins):
|
||||
filter_index = self.get_filter_index(type(self_filter), bin)
|
||||
filter_indices[i][j] = filter_index
|
||||
indices = np.array([self_filter.get_bin_index(b) for b in bins])
|
||||
filter_indices.append(indices)
|
||||
|
||||
# Account for stride in each of the previous filters
|
||||
for indices in filter_indices[:i]:
|
||||
|
|
@ -1233,7 +1142,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Determine the score indices from any of the requested scores
|
||||
if nuclides:
|
||||
nuclide_indices = np.zeros(len(nuclides), dtype=np.int)
|
||||
nuclide_indices = np.zeros(len(nuclides), dtype=int)
|
||||
for i, nuclide in enumerate(nuclides):
|
||||
nuclide_indices[i] = self.get_nuclide_index(nuclide)
|
||||
|
||||
|
|
@ -1272,7 +1181,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Determine the score indices from any of the requested scores
|
||||
if scores:
|
||||
score_indices = np.zeros(len(scores), dtype=np.int)
|
||||
score_indices = np.zeros(len(scores), dtype=int)
|
||||
for i, score in enumerate(scores):
|
||||
score_indices[i] = self.get_score_index(score)
|
||||
|
||||
|
|
@ -1544,11 +1453,8 @@ class Tally(IDManagerMixin):
|
|||
data = self.get_values(value=value)
|
||||
|
||||
# Build a new array shape with one dimension per filter
|
||||
new_shape = ()
|
||||
for self_filter in self.filters:
|
||||
new_shape += (self_filter.num_bins, )
|
||||
new_shape += (self.num_nuclides,)
|
||||
new_shape += (self.num_scores,)
|
||||
new_shape = tuple(f.num_bins for f in self.filters)
|
||||
new_shape += (self.num_nuclides, self.num_scores)
|
||||
|
||||
# Reshape the data with one dimension for each filter
|
||||
data = np.reshape(data, new_shape)
|
||||
|
|
@ -1676,19 +1582,22 @@ class Tally(IDManagerMixin):
|
|||
new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 +
|
||||
data['other']['std. dev.']**2)
|
||||
elif binary_op == '*':
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] * data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
elif binary_op == '/':
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] / data['other']['mean']
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
self_rel_err = data['self']['std. dev.'] / data['self']['mean']
|
||||
other_rel_err = data['other']['std. dev.'] / data['other']['mean']
|
||||
new_tally._mean = data['self']['mean'] / data['other']['mean']
|
||||
new_tally._std_dev = np.abs(new_tally.mean) * \
|
||||
np.sqrt(self_rel_err**2 + other_rel_err**2)
|
||||
elif binary_op == '^':
|
||||
mean_ratio = data['other']['mean'] / data['self']['mean']
|
||||
with np.errstate(divide='ignore', invalid='ignore'):
|
||||
mean_ratio = data['other']['mean'] / data['self']['mean']
|
||||
first_term = mean_ratio * data['self']['std. dev.']
|
||||
second_term = \
|
||||
np.log(data['self']['mean']) * data['other']['std. dev.']
|
||||
|
|
@ -1955,14 +1864,14 @@ class Tally(IDManagerMixin):
|
|||
elif isinstance(filter1, openmc.EnergyFunctionFilter):
|
||||
filter1_bins = [None]
|
||||
else:
|
||||
filter1_bins = [filter1.get_bin(i) for i in range(filter1.num_bins)]
|
||||
filter1_bins = filter1.bins
|
||||
|
||||
if isinstance(filter2, openmc.DistribcellFilter):
|
||||
filter2_bins = [b for b in range(filter2.num_bins)]
|
||||
elif isinstance(filter2, openmc.EnergyFunctionFilter):
|
||||
filter2_bins = [None]
|
||||
else:
|
||||
filter2_bins = [filter2.get_bin(i) for i in range(filter2.num_bins)]
|
||||
filter2_bins = filter2.bins
|
||||
|
||||
# Create variables to store views of data in the misaligned structure
|
||||
mean = {}
|
||||
|
|
@ -2603,7 +2512,8 @@ class Tally(IDManagerMixin):
|
|||
new_tally = self * -1
|
||||
return new_tally
|
||||
|
||||
def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[]):
|
||||
def get_slice(self, scores=[], filters=[], filter_bins=[], nuclides=[],
|
||||
squeeze=False):
|
||||
"""Build a sliced tally for the specified filters, scores and nuclides.
|
||||
|
||||
This method constructs a new tally to encapsulate a subset of the data
|
||||
|
|
@ -2614,26 +2524,26 @@ class Tally(IDManagerMixin):
|
|||
Parameters
|
||||
----------
|
||||
scores : list of str
|
||||
A list of one or more score strings
|
||||
(e.g., ['absorption', 'nu-fission']; default is [])
|
||||
A list of one or more score strings (e.g., ['absorption',
|
||||
'nu-fission']
|
||||
filters : Iterable of openmc.FilterMeta
|
||||
An iterable of filter types
|
||||
(e.g., [MeshFilter, EnergyFilter]; default is [])
|
||||
An iterable of filter types (e.g., [MeshFilter, EnergyFilter])
|
||||
filter_bins : list of Iterables
|
||||
A list of tuples of filter bins corresponding to the filter_types
|
||||
parameter (e.g., [(1,), ((0., 0.625e-6),)]; default is []). Each
|
||||
tuple contains bins to slice for the corresponding filter type in
|
||||
the filters parameter. Each bins is the integer ID for 'material',
|
||||
A list of iterables of filter bins corresponding to the specified
|
||||
filter types (e.g., [(1,), ((0., 0.625e-6),)]). Each iterable
|
||||
contains bins to slice for the corresponding filter type in the
|
||||
filters parameter. Each bin is the integer ID for 'material',
|
||||
'surface', 'cell', 'cellborn', and 'universe' Filters. Each bin is
|
||||
an integer for the cell instance ID for 'distribcell' Filters. Each
|
||||
bin is a 2-tuple of floats for 'energy' and 'energyout' filters
|
||||
corresponding to the energy boundaries of the bin of interest. The
|
||||
bin is an (x,y,z) 3-tuple for 'mesh' filters corresponding to the
|
||||
mesh cell of interest. The order of the bins in the list must
|
||||
correspond to the filter_types parameter.
|
||||
correspond to the `filters` argument.
|
||||
nuclides : list of str
|
||||
A list of nuclide name strings
|
||||
(e.g., ['U235', 'U238']; default is [])
|
||||
A list of nuclide name strings (e.g., ['U235', 'U238'])
|
||||
squeeze : bool
|
||||
Whether to remove filters with only a single bin in the sliced tally
|
||||
|
||||
Returns
|
||||
-------
|
||||
|
|
@ -2713,32 +2623,29 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Determine the filter indices from any of the requested filters
|
||||
for i, filter_type in enumerate(filters):
|
||||
find_filter = new_tally.find_filter(filter_type)
|
||||
f = new_tally.find_filter(filter_type)
|
||||
|
||||
# Remove filters with only a single bin if requested
|
||||
if squeeze:
|
||||
if len(filter_bins[i]) == 1:
|
||||
new_tally.filters.remove(f)
|
||||
continue
|
||||
else:
|
||||
raise RuntimeError('Cannot remove sliced filter with '
|
||||
'more than one bin.')
|
||||
|
||||
# Remove and/or reorder filter bins to user specifications
|
||||
bin_indices = []
|
||||
bin_indices = [f.get_bin_index(b)
|
||||
for b in filter_bins[i]]
|
||||
bin_indices = np.unique(bin_indices)
|
||||
|
||||
for filter_bin in filter_bins[i]:
|
||||
bin_index = find_filter.get_bin_index(filter_bin)
|
||||
if issubclass(filter_type, openmc.RealFilter):
|
||||
bin_indices.extend([bin_index, bin_index+1])
|
||||
else:
|
||||
bin_indices.append(bin_index)
|
||||
|
||||
# Set bins for mesh/distribcell filters apart from others
|
||||
if filter_type is openmc.MeshFilter:
|
||||
bins = find_filter.mesh
|
||||
elif filter_type is openmc.DistribcellFilter:
|
||||
bins = find_filter.bins
|
||||
else:
|
||||
bins = np.unique(find_filter.bins[bin_indices])
|
||||
|
||||
# Create new filter
|
||||
new_filter = filter_type(bins)
|
||||
# Set bins for sliced filter
|
||||
new_filter = copy.copy(f)
|
||||
new_filter.bins = [f.bins[i] for i in bin_indices]
|
||||
|
||||
# Set number of bins manually for mesh/distribcell filters
|
||||
if filter_type in (openmc.DistribcellFilter, openmc.MeshFilter):
|
||||
new_filter._num_bins = find_filter._num_bins
|
||||
if filter_type is openmc.DistribcellFilter:
|
||||
new_filter._num_bins = f._num_bins
|
||||
|
||||
# Replace existing filter with new one
|
||||
for j, test_filter in enumerate(new_tally.filters):
|
||||
|
|
@ -2815,9 +2722,7 @@ class Tally(IDManagerMixin):
|
|||
elif isinstance(find_filter, openmc.EnergyFunctionFilter):
|
||||
filter_bins = [None]
|
||||
else:
|
||||
num_bins = find_filter.num_bins
|
||||
filter_bins = \
|
||||
[(find_filter.get_bin(i)) for i in range(num_bins)]
|
||||
filter_bins = find_filter.bins
|
||||
|
||||
# Only sum across bins specified by the user
|
||||
else:
|
||||
|
|
@ -2826,7 +2731,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
# Sum across the bins in the user-specified filter
|
||||
for i, self_filter in enumerate(self.filters):
|
||||
if isinstance(self_filter, filter_type):
|
||||
if type(self_filter) == filter_type:
|
||||
shape = mean.shape
|
||||
mean = np.take(mean, indices=bin_indices, axis=i)
|
||||
std_dev = np.take(std_dev, indices=bin_indices, axis=i)
|
||||
|
|
@ -2969,9 +2874,7 @@ class Tally(IDManagerMixin):
|
|||
elif isinstance(find_filter, openmc.EnergyFunctionFilter):
|
||||
filter_bins = [None]
|
||||
else:
|
||||
num_bins = find_filter.num_bins
|
||||
filter_bins = \
|
||||
[(find_filter.get_bin(i)) for i in range(num_bins)]
|
||||
filter_bins = find_filter.bins
|
||||
|
||||
# Only average across bins specified by the user
|
||||
else:
|
||||
|
|
@ -3068,7 +2971,7 @@ class Tally(IDManagerMixin):
|
|||
The data in the derived tally arrays is "diagonalized" along the bins in
|
||||
the new filter. This functionality is used by the openmc.mgxs module; to
|
||||
transport-correct scattering matrices by subtracting a 'scatter-P1'
|
||||
reaction rate tally with an energy filter from an 'scatter' reaction
|
||||
reaction rate tally with an energy filter from a 'scatter' reaction
|
||||
rate tally with both energy and energyout filters.
|
||||
|
||||
Parameters
|
||||
|
|
@ -3087,7 +2990,7 @@ class Tally(IDManagerMixin):
|
|||
|
||||
if new_filter in self.filters:
|
||||
msg = 'Unable to diagonalize Tally ID="{0}" which already ' \
|
||||
'contains a "{1}" filter'.format(self.id, new_filter.type)
|
||||
'contains a "{1}" filter'.format(self.id, type(new_filter))
|
||||
raise ValueError(msg)
|
||||
|
||||
# Add the new filter to a copy of this Tally
|
||||
|
|
@ -3098,8 +3001,8 @@ class Tally(IDManagerMixin):
|
|||
# by which the "base" indices should be repeated to account for all
|
||||
# other filter bins in the diagonalized tally
|
||||
indices = np.arange(0, new_filter.num_bins**2, new_filter.num_bins+1)
|
||||
diag_factor = int(self.num_filter_bins / new_filter.num_bins)
|
||||
diag_indices = np.zeros(self.num_filter_bins, dtype=np.int)
|
||||
diag_factor = self.num_filter_bins // new_filter.num_bins
|
||||
diag_indices = np.zeros(self.num_filter_bins, dtype=int)
|
||||
|
||||
# Determine the filter indices along the new "diagonal"
|
||||
for i in range(diag_factor):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ from numbers import Integral, Real
|
|||
import random
|
||||
import sys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
import openmc
|
||||
|
|
@ -146,7 +145,7 @@ class Universe(IDManagerMixin):
|
|||
"""
|
||||
if volume_calc.domain_type == 'universe':
|
||||
if self.id in volume_calc.volumes:
|
||||
self._volume = volume_calc.volumes[self.id][0]
|
||||
self._volume = volume_calc.volumes[self.id].n
|
||||
self._atoms = volume_calc.atoms[self.id]
|
||||
else:
|
||||
raise ValueError('No volume information found for this universe.')
|
||||
|
|
@ -184,10 +183,14 @@ class Universe(IDManagerMixin):
|
|||
return []
|
||||
|
||||
def plot(self, origin=(0., 0., 0.), width=(1., 1.), pixels=(200, 200),
|
||||
basis='xy', color_by='cell', colors=None, filename=None, seed=None,
|
||||
basis='xy', color_by='cell', colors=None, seed=None,
|
||||
**kwargs):
|
||||
"""Display a slice plot of the universe.
|
||||
|
||||
To display or save the plot, call :func:`matplotlib.pyplot.show` or
|
||||
:func:`matplotlib.pyplot.savefig`. In a Jupyter notebook, enabling the
|
||||
matplotlib inline backend will show the plot inline.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
origin : Iterable of float
|
||||
|
|
@ -212,9 +215,6 @@ class Universe(IDManagerMixin):
|
|||
water = openmc.Cell(fill=h2o)
|
||||
universe.plot(..., colors={water: (0., 0., 1.))
|
||||
|
||||
filename : str or None
|
||||
Filename to save plot to. If no filename is given, the plot will be
|
||||
displayed using the currently enabled matplotlib backend.
|
||||
seed : hashable object or None
|
||||
Hashable object which is used to seed the random number generator
|
||||
used to select colors. If None, the generator is seeded from the
|
||||
|
|
@ -223,7 +223,14 @@ class Universe(IDManagerMixin):
|
|||
All keyword arguments are passed to
|
||||
:func:`matplotlib.pyplot.imshow`.
|
||||
|
||||
Returns
|
||||
-------
|
||||
matplotlib.image.AxesImage
|
||||
Resulting image
|
||||
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Seed the random number generator
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
|
@ -298,14 +305,8 @@ class Universe(IDManagerMixin):
|
|||
img[j, i, :] = colors[obj]
|
||||
|
||||
# Display image
|
||||
plt.imshow(img, extent=(x_min, x_max, y_min, y_max),
|
||||
interpolation='nearest', **kwargs)
|
||||
|
||||
# Show or save the plot
|
||||
if filename is None:
|
||||
plt.show()
|
||||
else:
|
||||
plt.savefig(filename)
|
||||
return plt.imshow(img, extent=(x_min, x_max, y_min, y_max),
|
||||
interpolation='nearest', **kwargs)
|
||||
|
||||
def add_cell(self, cell):
|
||||
"""Add a cell to the universe.
|
||||
|
|
@ -405,7 +406,7 @@ class Universe(IDManagerMixin):
|
|||
volume = self.volume
|
||||
for name, atoms in self._atoms.items():
|
||||
nuclide = openmc.Nuclide(name)
|
||||
density = 1.0e-24 * atoms[0]/volume # density in atoms/b-cm
|
||||
density = 1.0e-24 * atoms.n/volume # density in atoms/b-cm
|
||||
nuclides[name] = (nuclide, density)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import warnings
|
|||
import numpy as np
|
||||
import pandas as pd
|
||||
import h5py
|
||||
from uncertainties import ufloat
|
||||
|
||||
import openmc
|
||||
import openmc.checkvalue as cv
|
||||
|
|
@ -137,11 +138,10 @@ class VolumeCalculation(object):
|
|||
@property
|
||||
def atoms_dataframe(self):
|
||||
items = []
|
||||
columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms',
|
||||
'Uncertainty']
|
||||
columns = [self.domain_type.capitalize(), 'Nuclide', 'Atoms']
|
||||
for uid, atoms_dict in self.atoms.items():
|
||||
for name, atoms in atoms_dict.items():
|
||||
items.append((uid, name, atoms[0], atoms[1]))
|
||||
items.append((uid, name, atoms))
|
||||
|
||||
return pd.DataFrame.from_records(items, columns=columns)
|
||||
|
||||
|
|
@ -211,13 +211,13 @@ class VolumeCalculation(object):
|
|||
domain_id = int(obj_name[7:])
|
||||
ids.append(domain_id)
|
||||
group = f[obj_name]
|
||||
volume = tuple(group['volume'].value)
|
||||
volume = ufloat(*group['volume'].value)
|
||||
nucnames = group['nuclides'].value
|
||||
atoms_ = group['atoms'].value
|
||||
|
||||
atom_dict = OrderedDict()
|
||||
for name_i, atoms_i in zip(nucnames, atoms_):
|
||||
atom_dict[name_i.decode()] = tuple(atoms_i)
|
||||
atom_dict[name_i.decode()] = ufloat(*atoms_i)
|
||||
volumes[domain_id] = volume
|
||||
atoms[domain_id] = atom_dict
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ parser.add_argument('-b', '--batch', action='store_true',
|
|||
parser.add_argument('-d', '--destination', default='jeff-3.2-hdf5',
|
||||
help='Directory to create new library in')
|
||||
parser.add_argument('--libver', choices=['earliest', 'latest'],
|
||||
default='earliest', help="Output HDF5 versioning. Use "
|
||||
default='latest', help="Output HDF5 versioning. Use "
|
||||
"'earliest' for backwards compatibility or 'latest' for "
|
||||
"performance")
|
||||
args = parser.parse_args()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
module angle_distribution
|
||||
|
||||
use hdf5, only: HID_T, HSIZE_T
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants, only: ZERO, ONE, HISTOGRAM, LINEAR_LINEAR
|
||||
use distribution_univariate, only: DistributionContainer, Tabular
|
||||
use hdf5_interface, only: read_attribute, get_shape, read_dataset, &
|
||||
open_dataset, close_dataset
|
||||
open_dataset, close_dataset, HID_T, HSIZE_T
|
||||
use random_lcg, only: prn
|
||||
|
||||
implicit none
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
module angleenergy_header
|
||||
|
||||
use hdf5, only: HID_T
|
||||
use hdf5_interface, only: HID_T
|
||||
|
||||
!===============================================================================
|
||||
! ANGLEENERGY (abstract) defines a correlated or uncorrelated angle-energy
|
||||
|
|
|
|||
50
src/api.F90
50
src/api.F90
|
|
@ -2,8 +2,6 @@ module openmc_api
|
|||
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use hdf5, only: HID_T, h5tclose_f, h5close_f
|
||||
|
||||
use bank_header, only: openmc_source_bank
|
||||
use constants, only: K_BOLTZMANN
|
||||
use eigenvalue, only: k_sum, openmc_get_keff
|
||||
|
|
@ -12,10 +10,11 @@ module openmc_api
|
|||
use geometry_header
|
||||
use hdf5_interface
|
||||
use material_header
|
||||
use math
|
||||
use mesh_header
|
||||
use message_passing
|
||||
use nuclide_header
|
||||
use initialize, only: openmc_init
|
||||
use initialize, only: openmc_init_f
|
||||
use particle_header, only: Particle
|
||||
use plot, only: openmc_plot_geometry
|
||||
use random_lcg, only: openmc_get_seed, openmc_set_seed
|
||||
|
|
@ -36,6 +35,7 @@ module openmc_api
|
|||
|
||||
private
|
||||
public :: openmc_calculate_volumes
|
||||
public :: openmc_cell_filter_get_bins
|
||||
public :: openmc_cell_get_id
|
||||
public :: openmc_cell_get_fill
|
||||
public :: openmc_cell_set_fill
|
||||
|
|
@ -64,7 +64,7 @@ module openmc_api
|
|||
public :: openmc_get_tally_index
|
||||
public :: openmc_global_tallies
|
||||
public :: openmc_hard_reset
|
||||
public :: openmc_init
|
||||
public :: openmc_init_f
|
||||
public :: openmc_load_nuclide
|
||||
public :: openmc_material_add_nuclide
|
||||
public :: openmc_material_get_id
|
||||
|
|
@ -75,11 +75,11 @@ module openmc_api
|
|||
public :: openmc_material_filter_get_bins
|
||||
public :: openmc_material_filter_set_bins
|
||||
public :: openmc_mesh_filter_set_mesh
|
||||
public :: openmc_meshsurface_filter_set_mesh
|
||||
public :: openmc_next_batch
|
||||
public :: openmc_nuclide_name
|
||||
public :: openmc_plot_geometry
|
||||
public :: openmc_reset
|
||||
public :: openmc_run
|
||||
public :: openmc_set_seed
|
||||
public :: openmc_simulation_finalize
|
||||
public :: openmc_simulation_init
|
||||
|
|
@ -104,12 +104,16 @@ contains
|
|||
! variables
|
||||
!===============================================================================
|
||||
|
||||
subroutine openmc_finalize() bind(C)
|
||||
function openmc_finalize() result(err) bind(C)
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: err
|
||||
interface
|
||||
subroutine openmc_free_bank() bind(C)
|
||||
end subroutine openmc_free_bank
|
||||
end interface
|
||||
|
||||
! Clear results
|
||||
call openmc_reset()
|
||||
err = openmc_reset()
|
||||
|
||||
! Reset global variables
|
||||
assume_separate = .false.
|
||||
|
|
@ -171,18 +175,14 @@ contains
|
|||
! Deallocate arrays
|
||||
call free_memory()
|
||||
|
||||
! Release compound datatypes
|
||||
call h5tclose_f(hdf5_bank_t, err)
|
||||
|
||||
! Close FORTRAN interface.
|
||||
call h5close_f(err)
|
||||
|
||||
err = 0
|
||||
#ifdef OPENMC_MPI
|
||||
! Free all MPI types
|
||||
call MPI_TYPE_FREE(MPI_BANK, err)
|
||||
call openmc_free_bank()
|
||||
#endif
|
||||
|
||||
end subroutine openmc_finalize
|
||||
end function openmc_finalize
|
||||
|
||||
!===============================================================================
|
||||
! OPENMC_FIND determines the ID or a cell or material at a given point in space
|
||||
|
|
@ -209,7 +209,7 @@ contains
|
|||
|
||||
if (found) then
|
||||
if (rtype == 1) then
|
||||
id = cells(p % coord(p % n_coord) % cell) % id
|
||||
id = cells(p % coord(p % n_coord) % cell) % id()
|
||||
elseif (rtype == 2) then
|
||||
if (p % material == MATERIAL_VOID) then
|
||||
id = 0
|
||||
|
|
@ -233,9 +233,11 @@ contains
|
|||
! generator state
|
||||
!===============================================================================
|
||||
|
||||
subroutine openmc_hard_reset() bind(C)
|
||||
function openmc_hard_reset() result(err) bind(C)
|
||||
integer(C_INT) :: err
|
||||
|
||||
! Reset all tallies and timers
|
||||
call openmc_reset()
|
||||
err = openmc_reset()
|
||||
|
||||
! Reset total generations and keff guess
|
||||
keff = ONE
|
||||
|
|
@ -243,13 +245,15 @@ contains
|
|||
|
||||
! Reset the random number generator state
|
||||
call openmc_set_seed(DEFAULT_SEED)
|
||||
end subroutine openmc_hard_reset
|
||||
end function openmc_hard_reset
|
||||
|
||||
!===============================================================================
|
||||
! OPENMC_RESET resets tallies and timers
|
||||
!===============================================================================
|
||||
|
||||
subroutine openmc_reset() bind(C)
|
||||
function openmc_reset() result(err) bind(C)
|
||||
integer(C_INT) :: err
|
||||
|
||||
integer :: i
|
||||
|
||||
if (allocated(tallies)) then
|
||||
|
|
@ -277,8 +281,9 @@ contains
|
|||
! Clear active tally lists
|
||||
call active_analog_tallies % clear()
|
||||
call active_tracklength_tallies % clear()
|
||||
call active_current_tallies % clear()
|
||||
call active_meshsurf_tallies % clear()
|
||||
call active_collision_tallies % clear()
|
||||
call active_surface_tallies % clear()
|
||||
call active_tallies % clear()
|
||||
|
||||
! Reset timers
|
||||
|
|
@ -296,7 +301,8 @@ contains
|
|||
call time_transport % reset()
|
||||
call time_finalize % reset()
|
||||
|
||||
end subroutine openmc_reset
|
||||
err = 0
|
||||
end function openmc_reset
|
||||
|
||||
!===============================================================================
|
||||
! FREE_MEMORY deallocates and clears all global allocatable arrays in the
|
||||
|
|
|
|||
523
src/cell.cpp
Normal file
523
src/cell.cpp
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
#include "cell.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "lattice.h"
|
||||
#include "surface.h"
|
||||
#include "xml_interface.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
constexpr int32_t OP_LEFT_PAREN {std::numeric_limits<int32_t>::max()};
|
||||
constexpr int32_t OP_RIGHT_PAREN {std::numeric_limits<int32_t>::max() - 1};
|
||||
constexpr int32_t OP_COMPLEMENT {std::numeric_limits<int32_t>::max() - 2};
|
||||
constexpr int32_t OP_INTERSECTION {std::numeric_limits<int32_t>::max() - 3};
|
||||
constexpr int32_t OP_UNION {std::numeric_limits<int32_t>::max() - 4};
|
||||
|
||||
extern "C" double FP_PRECISION;
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
int32_t n_cells {0};
|
||||
|
||||
std::vector<Cell*> global_cells;
|
||||
std::unordered_map<int32_t, int32_t> cell_map;
|
||||
|
||||
std::vector<Universe*> global_universes;
|
||||
std::unordered_map<int32_t, int32_t> universe_map;
|
||||
|
||||
//==============================================================================
|
||||
//! Convert region specification string to integer tokens.
|
||||
//!
|
||||
//! The characters (, ), |, and ~ count as separate tokens since they represent
|
||||
//! operators.
|
||||
//==============================================================================
|
||||
|
||||
std::vector<int32_t>
|
||||
tokenize(const std::string region_spec) {
|
||||
// Check for an empty region_spec first.
|
||||
std::vector<int32_t> tokens;
|
||||
if (region_spec.empty()) {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// Parse all halfspaces and operators except for intersection (whitespace).
|
||||
for (int i = 0; i < region_spec.size(); ) {
|
||||
if (region_spec[i] == '(') {
|
||||
tokens.push_back(OP_LEFT_PAREN);
|
||||
i++;
|
||||
|
||||
} else if (region_spec[i] == ')') {
|
||||
tokens.push_back(OP_RIGHT_PAREN);
|
||||
i++;
|
||||
|
||||
} else if (region_spec[i] == '|') {
|
||||
tokens.push_back(OP_UNION);
|
||||
i++;
|
||||
|
||||
} else if (region_spec[i] == '~') {
|
||||
tokens.push_back(OP_COMPLEMENT);
|
||||
i++;
|
||||
|
||||
} else if (region_spec[i] == '-' || region_spec[i] == '+'
|
||||
|| std::isdigit(region_spec[i])) {
|
||||
// This is the start of a halfspace specification. Iterate j until we
|
||||
// find the end, then push-back everything between i and j.
|
||||
int j = i + 1;
|
||||
while (j < region_spec.size() && std::isdigit(region_spec[j])) {j++;}
|
||||
tokens.push_back(std::stoi(region_spec.substr(i, j-i)));
|
||||
i = j;
|
||||
|
||||
} else if (std::isspace(region_spec[i])) {
|
||||
i++;
|
||||
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Region specification contains invalid character, \""
|
||||
<< region_spec[i] << "\"";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Add in intersection operators where a missing operator is needed.
|
||||
int i = 0;
|
||||
while (i < tokens.size()-1) {
|
||||
bool left_compat {(tokens[i] < OP_UNION) || (tokens[i] == OP_RIGHT_PAREN)};
|
||||
bool right_compat {(tokens[i+1] < OP_UNION)
|
||||
|| (tokens[i+1] == OP_LEFT_PAREN)
|
||||
|| (tokens[i+1] == OP_COMPLEMENT)};
|
||||
if (left_compat && right_compat) {
|
||||
tokens.insert(tokens.begin()+i+1, OP_INTERSECTION);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
//! Convert infix region specification to Reverse Polish Notation (RPN)
|
||||
//!
|
||||
//! This function uses the shunting-yard algorithm.
|
||||
//==============================================================================
|
||||
|
||||
std::vector<int32_t>
|
||||
generate_rpn(int32_t cell_id, std::vector<int32_t> infix)
|
||||
{
|
||||
std::vector<int32_t> rpn;
|
||||
std::vector<int32_t> stack;
|
||||
|
||||
for (int32_t token : infix) {
|
||||
if (token < OP_UNION) {
|
||||
// If token is not an operator, add it to output
|
||||
rpn.push_back(token);
|
||||
|
||||
} else if (token < OP_RIGHT_PAREN) {
|
||||
// Regular operators union, intersection, complement
|
||||
while (stack.size() > 0) {
|
||||
int32_t op = stack.back();
|
||||
|
||||
if (op < OP_RIGHT_PAREN &&
|
||||
((token == OP_COMPLEMENT && token < op) ||
|
||||
(token != OP_COMPLEMENT && token <= op))) {
|
||||
// While there is an operator, op, on top of the stack, if the token
|
||||
// is left-associative and its precedence is less than or equal to
|
||||
// that of op or if the token is right-associative and its precedence
|
||||
// is less than that of op, move op to the output queue and push the
|
||||
// token on to the stack. Note that only complement is
|
||||
// right-associative.
|
||||
rpn.push_back(op);
|
||||
stack.pop_back();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stack.push_back(token);
|
||||
|
||||
} else if (token == OP_LEFT_PAREN) {
|
||||
// If the token is a left parenthesis, push it onto the stack
|
||||
stack.push_back(token);
|
||||
|
||||
} else {
|
||||
// If the token is a right parenthesis, move operators from the stack to
|
||||
// the output queue until reaching the left parenthesis.
|
||||
for (auto it = stack.rbegin(); *it != OP_LEFT_PAREN; it++) {
|
||||
// If we run out of operators without finding a left parenthesis, it
|
||||
// means there are mismatched parentheses.
|
||||
if (it == stack.rend()) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Mismatched parentheses in region specification for cell "
|
||||
<< cell_id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
rpn.push_back(stack.back());
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
// Pop the left parenthesis.
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
while (stack.size() > 0) {
|
||||
int32_t op = stack.back();
|
||||
|
||||
// If the operator is a parenthesis it is mismatched.
|
||||
if (op >= OP_RIGHT_PAREN) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Mismatched parentheses in region specification for cell "
|
||||
<< cell_id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
rpn.push_back(stack.back());
|
||||
stack.pop_back();
|
||||
}
|
||||
|
||||
return rpn;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Cell implementation
|
||||
//==============================================================================
|
||||
|
||||
Cell::Cell(pugi::xml_node cell_node)
|
||||
{
|
||||
if (check_for_node(cell_node, "id")) {
|
||||
id = stoi(get_node_value(cell_node, "id"));
|
||||
} else {
|
||||
fatal_error("Must specify id of cell in geometry XML file.");
|
||||
}
|
||||
|
||||
//TODO: don't automatically lowercase cell and surface names
|
||||
if (check_for_node(cell_node, "name")) {
|
||||
name = get_node_value(cell_node, "name");
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "universe")) {
|
||||
universe = stoi(get_node_value(cell_node, "universe"));
|
||||
} else {
|
||||
universe = 0;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "fill")) {
|
||||
fill = stoi(get_node_value(cell_node, "fill"));
|
||||
} else {
|
||||
fill = C_NONE;
|
||||
}
|
||||
|
||||
if (check_for_node(cell_node, "material")) {
|
||||
//TODO: read material ids.
|
||||
material.push_back(C_NONE+1);
|
||||
material.shrink_to_fit();
|
||||
} else {
|
||||
material.push_back(C_NONE);
|
||||
material.shrink_to_fit();
|
||||
}
|
||||
|
||||
// Make sure that either material or fill was specified.
|
||||
if ((material[0] == C_NONE) && (fill == C_NONE)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Neither material nor fill was specified for cell " << id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
// Make sure that material and fill haven't been specified simultaneously.
|
||||
if ((material[0] != C_NONE) && (fill != C_NONE)) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Cell " << id << " has both a material and a fill specified; "
|
||||
<< "only one can be specified per cell";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
// Read the region specification.
|
||||
std::string region_spec;
|
||||
if (check_for_node(cell_node, "region")) {
|
||||
region_spec = get_node_value(cell_node, "region");
|
||||
}
|
||||
|
||||
// Get a tokenized representation of the region specification.
|
||||
region = tokenize(region_spec);
|
||||
region.shrink_to_fit();
|
||||
|
||||
// Convert user IDs to surface indices.
|
||||
for (auto &r : region) {
|
||||
if (r < OP_UNION) {
|
||||
r = copysign(surface_map[abs(r)] + 1, r);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the infix region spec to RPN.
|
||||
rpn = generate_rpn(id, region);
|
||||
rpn.shrink_to_fit();
|
||||
|
||||
// Check if this is a simple cell.
|
||||
simple = true;
|
||||
for (int32_t token : rpn) {
|
||||
if ((token == OP_COMPLEMENT) || (token == OP_UNION)) {
|
||||
simple = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
{
|
||||
if (simple) {
|
||||
return contains_simple(xyz, uvw, on_surface);
|
||||
} else {
|
||||
return contains_complex(xyz, uvw, on_surface);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
std::pair<double, int32_t>
|
||||
Cell::distance(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
{
|
||||
double min_dist {INFTY};
|
||||
int32_t i_surf {std::numeric_limits<int32_t>::max()};
|
||||
|
||||
for (int32_t token : rpn) {
|
||||
// Ignore this token if it corresponds to an operator rather than a region.
|
||||
if (token >= OP_UNION) {continue;}
|
||||
|
||||
// Calculate the distance to this surface.
|
||||
// Note the off-by-one indexing
|
||||
bool coincident {token == on_surface};
|
||||
double d {surfaces_c[abs(token)-1]->distance(xyz, uvw, coincident)};
|
||||
|
||||
// Check if this distance is the new minimum.
|
||||
if (d < min_dist) {
|
||||
if (std::abs(d - min_dist) / min_dist >= FP_PRECISION) {
|
||||
min_dist = d;
|
||||
i_surf = -token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {min_dist, i_surf};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
Cell::to_hdf5(hid_t cell_group) const
|
||||
{
|
||||
if (!name.empty()) {
|
||||
write_string(cell_group, "name", name, false);
|
||||
}
|
||||
|
||||
//TODO: Fix the off-by-one indexing.
|
||||
write_int(cell_group, 0, nullptr, "universe",
|
||||
&global_universes[universe-1]->id, false);
|
||||
|
||||
// Write the region specification.
|
||||
if (!region.empty()) {
|
||||
std::stringstream region_spec {};
|
||||
for (int32_t token : region) {
|
||||
if (token == OP_LEFT_PAREN) {
|
||||
region_spec << " (";
|
||||
} else if (token == OP_RIGHT_PAREN) {
|
||||
region_spec << " )";
|
||||
} else if (token == OP_COMPLEMENT) {
|
||||
region_spec << " ~";
|
||||
} else if (token == OP_INTERSECTION) {
|
||||
} else if (token == OP_UNION) {
|
||||
region_spec << " |";
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
region_spec << " " << copysign(surfaces_c[abs(token)-1]->id, token);
|
||||
}
|
||||
}
|
||||
write_string(cell_group, "region", region_spec.str(), false);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains_simple(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
{
|
||||
for (int32_t token : rpn) {
|
||||
if (token < OP_UNION) {
|
||||
// If the token is not an operator, evaluate the sense of particle with
|
||||
// respect to the surface and see if the token matches the sense. If the
|
||||
// particle's surface attribute is set and matches the token, that
|
||||
// overrides the determination based on sense().
|
||||
if (token == on_surface) {
|
||||
} else if (-token == on_surface) {
|
||||
return false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);
|
||||
if (sense != (token > 0)) {return false;}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
bool
|
||||
Cell::contains_complex(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const
|
||||
{
|
||||
// Make a stack of booleans. We don't know how big it needs to be, but we do
|
||||
// know that rpn.size() is an upper-bound.
|
||||
bool stack[rpn.size()];
|
||||
int i_stack = -1;
|
||||
|
||||
for (int32_t token : rpn) {
|
||||
// If the token is a binary operator (intersection/union), apply it to
|
||||
// the last two items on the stack. If the token is a unary operator
|
||||
// (complement), apply it to the last item on the stack.
|
||||
if (token == OP_UNION) {
|
||||
stack[i_stack-1] = stack[i_stack-1] || stack[i_stack];
|
||||
i_stack --;
|
||||
} else if (token == OP_INTERSECTION) {
|
||||
stack[i_stack-1] = stack[i_stack-1] && stack[i_stack];
|
||||
i_stack --;
|
||||
} else if (token == OP_COMPLEMENT) {
|
||||
stack[i_stack] = !stack[i_stack];
|
||||
} else {
|
||||
// If the token is not an operator, evaluate the sense of particle with
|
||||
// respect to the surface and see if the token matches the sense. If the
|
||||
// particle's surface attribute is set and matches the token, that
|
||||
// overrides the determination based on sense().
|
||||
i_stack ++;
|
||||
if (token == on_surface) {
|
||||
stack[i_stack] = true;
|
||||
} else if (-token == on_surface) {
|
||||
stack[i_stack] = false;
|
||||
} else {
|
||||
// Note the off-by-one indexing
|
||||
bool sense = surfaces_c[abs(token)-1]->sense(xyz, uvw);;
|
||||
stack[i_stack] = (sense == (token > 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i_stack == 0) {
|
||||
// The one remaining bool on the stack indicates whether the particle is
|
||||
// in the cell.
|
||||
return stack[i_stack];
|
||||
} else {
|
||||
// This case occurs if there is no region specification since i_stack will
|
||||
// still be -1.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Non-method functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
read_cells(pugi::xml_node *node)
|
||||
{
|
||||
// Count the number of cells.
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {n_cells++;}
|
||||
if (n_cells == 0) {
|
||||
fatal_error("No cells found in geometry.xml!");
|
||||
}
|
||||
|
||||
// Allocate the vector of Cells.
|
||||
global_cells.reserve(n_cells);
|
||||
|
||||
// Loop over XML cell elements and populate the array.
|
||||
for (pugi::xml_node cell_node: node->children("cell")) {
|
||||
global_cells.push_back(new Cell(cell_node));
|
||||
}
|
||||
|
||||
// Populate the Universe vector and map.
|
||||
for (int i = 0; i < global_cells.size(); i++) {
|
||||
int32_t uid = global_cells[i]->universe;
|
||||
auto it = universe_map.find(uid);
|
||||
if (it == universe_map.end()) {
|
||||
global_universes.push_back(new Universe());
|
||||
global_universes.back()->id = uid;
|
||||
global_universes.back()->cells.push_back(i);
|
||||
universe_map[uid] = global_universes.size() - 1;
|
||||
} else {
|
||||
global_universes[it->second]->cells.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// Fortran compatibility functions
|
||||
//==============================================================================
|
||||
|
||||
extern "C" {
|
||||
Cell* cell_pointer(int32_t cell_ind) {return global_cells[cell_ind];}
|
||||
|
||||
int32_t cell_id(Cell *c) {return c->id;}
|
||||
|
||||
void cell_set_id(Cell *c, int32_t id) {c->id = id;}
|
||||
|
||||
int cell_type(Cell *c) {return c->type;}
|
||||
|
||||
void cell_set_type(Cell *c, int type) {c->type = type;}
|
||||
|
||||
int32_t cell_universe(Cell *c) {return c->universe;}
|
||||
|
||||
void cell_set_universe(Cell *c, int32_t universe) {c->universe = universe;}
|
||||
|
||||
int32_t cell_fill(Cell *c) {return c->fill;}
|
||||
|
||||
int32_t* cell_fill_ptr(Cell *c) {return &c->fill;}
|
||||
|
||||
int32_t cell_n_instances(Cell *c) {return c->n_instances;}
|
||||
|
||||
bool cell_simple(Cell *c) {return c->simple;}
|
||||
|
||||
bool cell_contains(Cell *c, double xyz[3], double uvw[3], int32_t on_surface)
|
||||
{return c->contains(xyz, uvw, on_surface);}
|
||||
|
||||
void cell_distance(Cell *c, double xyz[3], double uvw[3], int32_t on_surface,
|
||||
double *min_dist, int32_t *i_surf)
|
||||
{
|
||||
std::pair<double, int32_t> out = c->distance(xyz, uvw, on_surface);
|
||||
*min_dist = out.first;
|
||||
*i_surf = out.second;
|
||||
}
|
||||
|
||||
int32_t cell_offset(Cell *c, int map) {return c->offset[map];}
|
||||
|
||||
void cell_to_hdf5(Cell *c, hid_t group) {c->to_hdf5(group);}
|
||||
|
||||
void extend_cells_c(int32_t n)
|
||||
{
|
||||
global_cells.reserve(global_cells.size() + n);
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
global_cells.push_back(new Cell());
|
||||
}
|
||||
n_cells = global_cells.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace openmc
|
||||
118
src/cell.h
Normal file
118
src/cell.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef CELL_H
|
||||
#define CELL_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "pugixml/pugixml.hpp"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
// Constants
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int FILL_MATERIAL;
|
||||
extern "C" int FILL_UNIVERSE;
|
||||
extern "C" int FILL_LATTICE;
|
||||
|
||||
//==============================================================================
|
||||
// Global variables
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int32_t n_cells;
|
||||
|
||||
class Cell;
|
||||
extern std::vector<Cell*> global_cells;
|
||||
extern std::unordered_map<int32_t, int32_t> cell_map;
|
||||
|
||||
class Universe;
|
||||
extern std::vector<Universe*> global_universes;
|
||||
extern std::unordered_map<int32_t, int32_t> universe_map;
|
||||
|
||||
//==============================================================================
|
||||
//! A geometry primitive that fills all space and contains cells.
|
||||
//==============================================================================
|
||||
|
||||
class Universe
|
||||
{
|
||||
public:
|
||||
int32_t id; //!< Unique ID
|
||||
std::vector<int32_t> cells; //!< Cells within this universe
|
||||
//double x0, y0, z0; //!< Translation coordinates.
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
//! A geometry primitive that links surfaces, universes, and materials
|
||||
//==============================================================================
|
||||
|
||||
class Cell
|
||||
{
|
||||
public:
|
||||
int32_t id; //!< Unique ID
|
||||
std::string name; //!< User-defined name
|
||||
int type; //!< Material, universe, or lattice
|
||||
int32_t universe; //!< Universe # this cell is in
|
||||
int32_t fill; //!< Universe # filling this cell
|
||||
int32_t n_instances{0}; //!< Number of instances of this cell
|
||||
|
||||
//! \brief Material(s) within this cell.
|
||||
//!
|
||||
//! May be multiple materials for distribcell. C_NONE signifies a universe.
|
||||
std::vector<int32_t> material;
|
||||
|
||||
//! Definition of spatial region as Boolean expression of half-spaces
|
||||
std::vector<std::int32_t> region;
|
||||
//! Reverse Polish notation for region expression
|
||||
std::vector<std::int32_t> rpn;
|
||||
bool simple; //!< Does the region contain only intersections?
|
||||
|
||||
std::vector<int32_t> offset; //!< Distribcell offset table
|
||||
|
||||
Cell() {};
|
||||
|
||||
explicit Cell(pugi::xml_node cell_node);
|
||||
|
||||
//! \brief Determine if a cell contains the particle at a given location.
|
||||
//!
|
||||
//! The bounds of the cell are detemined by a logical expression involving
|
||||
//! surface half-spaces. At initialization, the expression was converted
|
||||
//! to RPN notation.
|
||||
//!
|
||||
//! The function is split into two cases, one for simple cells (those
|
||||
//! involving only the intersection of half-spaces) and one for complex cells.
|
||||
//! Simple cells can be evaluated with short circuit evaluation, i.e., as soon
|
||||
//! as we know that one half-space is not satisfied, we can exit. This
|
||||
//! provides a performance benefit for the common case. In
|
||||
//! contains_complex, we evaluate the RPN expression using a stack, similar to
|
||||
//! how a RPN calculator would work.
|
||||
//! @param xyz[3] The 3D Cartesian coordinate to check.
|
||||
//! @param uvw[3] A direction used to "break ties" the coordinates are very
|
||||
//! close to a surface.
|
||||
//! @param on_surface The signed index of a surface that the coordinate is
|
||||
//! known to be on. This index takes precedence over surface sense
|
||||
//! calculations.
|
||||
bool
|
||||
contains(const double xyz[3], const double uvw[3], int32_t on_surface) const;
|
||||
|
||||
//! Find the oncoming boundary of this cell.
|
||||
std::pair<double, int32_t>
|
||||
distance(const double xyz[3], const double uvw[3], int32_t on_surface) const;
|
||||
|
||||
//! \brief Write cell information to an HDF5 group.
|
||||
//! @param group_id An HDF5 group id.
|
||||
void to_hdf5(hid_t group_id) const;
|
||||
|
||||
protected:
|
||||
bool contains_simple(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const;
|
||||
bool contains_complex(const double xyz[3], const double uvw[3],
|
||||
int32_t on_surface) const;
|
||||
};
|
||||
|
||||
} // namespace openmc
|
||||
#endif // CELL_H
|
||||
|
|
@ -73,12 +73,11 @@ contains
|
|||
integer :: ital ! tally object index
|
||||
integer :: ijk(3) ! indices for mesh cell
|
||||
integer :: score_index ! index to pull from tally object
|
||||
integer :: i_filt ! index in filters array
|
||||
integer :: i_filter_mesh ! index for mesh filter
|
||||
integer :: i_filter_ein ! index for incoming energy filter
|
||||
integer :: i_filter_eout ! index for outgoing energy filter
|
||||
integer :: i_filter_surf ! index for surface filter
|
||||
integer :: stride_surf ! stride for surface filter
|
||||
integer :: i_filter_legendre ! index for Legendre filter
|
||||
integer :: i_mesh ! flattend index for mesh
|
||||
logical :: energy_filters! energy filters present
|
||||
real(8) :: flux ! temp variable for flux
|
||||
type(RegularMesh), pointer :: m ! pointer for mesh object
|
||||
|
|
@ -95,10 +94,10 @@ contains
|
|||
|
||||
! Associate tallies and mesh
|
||||
associate (t => cmfd_tallies(1) % obj)
|
||||
i_filt = t % filter(t % find_filter(FILTER_MESH))
|
||||
i_filter_mesh = t % filter(t % find_filter(FILTER_MESH))
|
||||
end associate
|
||||
|
||||
select type(filt => filters(i_filt) % obj)
|
||||
select type(filt => filters(i_filter_mesh) % obj)
|
||||
type is (MeshFilter)
|
||||
m => meshes(filt % mesh)
|
||||
end select
|
||||
|
|
@ -115,16 +114,19 @@ contains
|
|||
|
||||
! Associate tallies and mesh
|
||||
associate (t => cmfd_tallies(ital) % obj)
|
||||
i_filt = t % filter(t % find_filter(FILTER_MESH))
|
||||
select type(filt => filters(i_filt) % obj)
|
||||
type is (MeshFilter)
|
||||
m => meshes(filt % mesh)
|
||||
end select
|
||||
|
||||
if (ital < 3) then
|
||||
i_filter_mesh = t % filter(t % find_filter(FILTER_MESH))
|
||||
else if (ital == 3) then
|
||||
i_filter_mesh = t % filter(t % find_filter(FILTER_MESHSURFACE))
|
||||
else if (ital == 4) then
|
||||
i_filter_mesh = t % filter(t % find_filter(FILTER_MESH))
|
||||
i_filter_legendre = t % filter(t % find_filter(FILTER_LEGENDRE))
|
||||
end if
|
||||
|
||||
! Check for energy filters
|
||||
energy_filters = (t % find_filter(FILTER_ENERGYIN) > 0)
|
||||
|
||||
i_filter_mesh = t % filter(t % find_filter(FILTER_MESH))
|
||||
if (energy_filters) then
|
||||
i_filter_ein = t % filter(t % find_filter(FILTER_ENERGYIN))
|
||||
i_filter_eout = t % filter(t % find_filter(FILTER_ENERGYOUT))
|
||||
|
|
@ -189,13 +191,6 @@ contains
|
|||
! Get total rr and convert to total xs
|
||||
cmfd % totalxs(h,i,j,k) = t % results(RESULT_SUM,2,score_index) / flux
|
||||
|
||||
! Get p1 scatter rr and convert to p1 scatter xs
|
||||
cmfd % p1scattxs(h,i,j,k) = t % results(RESULT_SUM,3,score_index) / flux
|
||||
|
||||
! Calculate diffusion coefficient
|
||||
cmfd % diffcof(h,i,j,k) = ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - &
|
||||
cmfd % p1scattxs(h,i,j,k)))
|
||||
|
||||
else if (ital == 2) then
|
||||
|
||||
! Begin loop to get energy out tallies
|
||||
|
|
@ -247,65 +242,102 @@ contains
|
|||
|
||||
else if (ital == 3) then
|
||||
|
||||
i_filter_surf = t % filter(t % find_filter(FILTER_SURFACE))
|
||||
stride_surf = t % stride(t % find_filter(FILTER_SURFACE))
|
||||
|
||||
! Initialize and filter for energy
|
||||
do l = 1, size(t % filter)
|
||||
call filter_matches(t % filter(l)) % bins % clear()
|
||||
call filter_matches(t % filter(l)) % bins % push_back(1)
|
||||
end do
|
||||
|
||||
! Set the bin for this mesh cell
|
||||
i_mesh = m % get_bin_from_indices([ i, j, k ])
|
||||
filter_matches(i_filter_mesh) % bins % data(1) = 12*(i_mesh - 1) + 1
|
||||
|
||||
! Set the energy bin if needed
|
||||
if (energy_filters) then
|
||||
filter_matches(i_filter_ein) % bins % data(1) = ng - h + 1
|
||||
end if
|
||||
|
||||
! Get the bin for this mesh cell
|
||||
filter_matches(i_filter_mesh) % bins % data(1) = &
|
||||
m % get_bin_from_indices([ i, j, k ])
|
||||
|
||||
score_index = 1
|
||||
score_index = 0
|
||||
do l = 1, size(t % filter)
|
||||
if (t % filter(l) == i_filter_surf) cycle
|
||||
score_index = score_index + (filter_matches(t % filter(l)) &
|
||||
% bins % data(1) - 1) * t % stride(l)
|
||||
end do
|
||||
|
||||
! Left surface
|
||||
cmfd % current(1,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_LEFT - 1) * stride_surf)
|
||||
score_index + OUT_LEFT)
|
||||
cmfd % current(2,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_LEFT - 1) * stride_surf)
|
||||
score_index + IN_LEFT)
|
||||
|
||||
! Right surface
|
||||
cmfd % current(3,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_RIGHT - 1) * stride_surf)
|
||||
score_index + IN_RIGHT)
|
||||
cmfd % current(4,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_RIGHT - 1) * stride_surf)
|
||||
score_index + OUT_RIGHT)
|
||||
|
||||
! Back surface
|
||||
cmfd % current(5,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_BACK - 1) * stride_surf)
|
||||
score_index + OUT_BACK)
|
||||
cmfd % current(6,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_BACK - 1) * stride_surf)
|
||||
score_index + IN_BACK)
|
||||
|
||||
! Front surface
|
||||
cmfd % current(7,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_FRONT - 1) * stride_surf)
|
||||
score_index + IN_FRONT)
|
||||
cmfd % current(8,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_FRONT - 1) * stride_surf)
|
||||
score_index + OUT_FRONT)
|
||||
|
||||
! Bottom surface
|
||||
! Left surface
|
||||
cmfd % current(9,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_BOTTOM - 1) * stride_surf)
|
||||
score_index + OUT_BOTTOM)
|
||||
cmfd % current(10,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_BOTTOM - 1) * stride_surf)
|
||||
score_index + IN_BOTTOM)
|
||||
|
||||
! Top surface
|
||||
! Right surface
|
||||
cmfd % current(11,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (IN_TOP - 1) * stride_surf)
|
||||
score_index + IN_TOP)
|
||||
cmfd % current(12,h,i,j,k) = t % results(RESULT_SUM, 1, &
|
||||
score_index + (OUT_TOP - 1) * stride_surf)
|
||||
score_index + OUT_TOP)
|
||||
|
||||
else if (ital == 4) then
|
||||
|
||||
! Reset all bins to 1
|
||||
do l = 1, size(t % filter)
|
||||
call filter_matches(t % filter(l)) % bins % clear()
|
||||
call filter_matches(t % filter(l)) % bins % push_back(1)
|
||||
end do
|
||||
|
||||
! Set ijk as mesh indices
|
||||
ijk = (/ i, j, k /)
|
||||
|
||||
! Get bin number for mesh indices
|
||||
filter_matches(i_filter_mesh) % bins % data(1) = &
|
||||
m % get_bin_from_indices(ijk)
|
||||
|
||||
! Apply energy in filter
|
||||
if (energy_filters) then
|
||||
filter_matches(i_filter_ein) % bins % data(1) = ng - h + 1
|
||||
end if
|
||||
|
||||
! Apply Legendre filter
|
||||
filter_matches(i_filter_legendre) % bins % data(1) = 2
|
||||
|
||||
! Calculate score index from bins
|
||||
score_index = 1
|
||||
do l = 1, size(t % filter)
|
||||
score_index = score_index + (filter_matches(t % filter(l)) &
|
||||
% bins % data(1) - 1) * t % stride(l)
|
||||
end do
|
||||
|
||||
! Get p1 scatter rr and convert to p1 scatter xs
|
||||
cmfd % p1scattxs(h,i,j,k) = &
|
||||
t % results(RESULT_SUM,1,score_index) / &
|
||||
cmfd % flux(h,i,j,k)
|
||||
|
||||
! Calculate diffusion coefficient
|
||||
cmfd % diffcof(h,i,j,k) = &
|
||||
ONE/(3.0_8*(cmfd % totalxs(h,i,j,k) - &
|
||||
cmfd % p1scattxs(h,i,j,k)))
|
||||
end if TALLY
|
||||
|
||||
end do OUTGROUP
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ contains
|
|||
m % id = i_start
|
||||
|
||||
! Set mesh type to rectangular
|
||||
m % type = LATTICE_RECT
|
||||
m % type = MESH_REGULAR
|
||||
|
||||
! Get pointer to mesh XML node
|
||||
node_mesh = root % child("mesh")
|
||||
|
|
@ -409,48 +409,25 @@ contains
|
|||
! Duplicate the mesh filter for the mesh current tally since other
|
||||
! tallies use this filter and we need to change the dimension
|
||||
i_filt = i_filt + 1
|
||||
err = openmc_filter_set_type(i_filt, C_CHAR_'mesh' // C_NULL_CHAR)
|
||||
err = openmc_filter_set_type(i_filt, C_CHAR_'meshsurface' // C_NULL_CHAR)
|
||||
call openmc_get_filter_next_id(filt_id)
|
||||
err = openmc_filter_set_id(i_filt, filt_id)
|
||||
err = openmc_mesh_filter_set_mesh(i_filt, i_start)
|
||||
err = openmc_meshsurface_filter_set_mesh(i_filt, i_start)
|
||||
|
||||
! We need to increase the dimension by one since we also need
|
||||
! currents coming into and out of the boundary mesh cells.
|
||||
filters(i_filt) % obj % n_bins = product(m % dimension + 1)
|
||||
|
||||
! Set up surface filter
|
||||
! Add in legendre filter for the P1 tally
|
||||
i_filt = i_filt + 1
|
||||
allocate(SurfaceFilter :: filters(i_filt) % obj)
|
||||
select type(filt => filters(i_filt) % obj)
|
||||
type is(SurfaceFilter)
|
||||
filt % id = i_filt
|
||||
filt % n_bins = 4 * m % n_dimension
|
||||
allocate(filt % surfaces(4 * m % n_dimension))
|
||||
if (m % n_dimension == 2) then
|
||||
filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, &
|
||||
OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT /)
|
||||
elseif (m % n_dimension == 3) then
|
||||
filt % surfaces = (/ OUT_LEFT, IN_LEFT, IN_RIGHT, OUT_RIGHT, &
|
||||
OUT_BACK, IN_BACK, IN_FRONT, OUT_FRONT, &
|
||||
OUT_BOTTOM, IN_BOTTOM, IN_TOP, OUT_TOP /)
|
||||
end if
|
||||
filt % current = .true.
|
||||
! Add filter to dictionary
|
||||
call filter_dict % set(filt % id, i_filt)
|
||||
end select
|
||||
err = openmc_filter_set_type(i_filt, C_CHAR_'legendre' // C_NULL_CHAR)
|
||||
call openmc_get_filter_next_id(filt_id)
|
||||
err = openmc_filter_set_id(i_filt, filt_id)
|
||||
err = openmc_legendre_filter_set_order(i_filt, 1)
|
||||
|
||||
! Initialize filters
|
||||
do i = i_filt_start, i_filt_end
|
||||
select type (filt => filters(i) % obj)
|
||||
type is (SurfaceFilter)
|
||||
! Don't do anything
|
||||
class default
|
||||
call filt % initialize()
|
||||
end select
|
||||
call filters(i) % obj % initialize()
|
||||
end do
|
||||
|
||||
! Allocate tallies
|
||||
err = openmc_extend_tallies(3, i_start, i_end)
|
||||
err = openmc_extend_tallies(4, i_start, i_end)
|
||||
cmfd_tallies => tallies(i_start:i_end)
|
||||
|
||||
! Begin loop around tallies
|
||||
|
|
@ -484,7 +461,7 @@ contains
|
|||
if (i == 1) then
|
||||
|
||||
! Set name
|
||||
t % name = "CMFD flux, total, scatter-1"
|
||||
t % name = "CMFD flux, total"
|
||||
|
||||
! Set tally estimator to analog
|
||||
t % estimator = ESTIMATOR_ANALOG
|
||||
|
|
@ -502,19 +479,12 @@ contains
|
|||
deallocate(filter_indices)
|
||||
|
||||
! Allocate scoring bins
|
||||
allocate(t % score_bins(3))
|
||||
t % n_score_bins = 3
|
||||
t % n_user_score_bins = 3
|
||||
|
||||
! Allocate scattering order data
|
||||
allocate(t % moment_order(3))
|
||||
t % moment_order = 0
|
||||
allocate(t % score_bins(2))
|
||||
t % n_score_bins = 2
|
||||
|
||||
! Set macro_bins
|
||||
t % score_bins(1) = SCORE_FLUX
|
||||
t % score_bins(2) = SCORE_TOTAL
|
||||
t % score_bins(3) = SCORE_SCATTER_N
|
||||
t % moment_order(3) = 1
|
||||
|
||||
else if (i == 2) then
|
||||
|
||||
|
|
@ -546,11 +516,6 @@ contains
|
|||
! Allocate macro reactions
|
||||
allocate(t % score_bins(2))
|
||||
t % n_score_bins = 2
|
||||
t % n_user_score_bins = 2
|
||||
|
||||
! Allocate scattering order data
|
||||
allocate(t % moment_order(2))
|
||||
t % moment_order = 0
|
||||
|
||||
! Set macro_bins
|
||||
t % score_bins(1) = SCORE_NU_SCATTER
|
||||
|
|
@ -564,13 +529,9 @@ contains
|
|||
! Set tally estimator to analog
|
||||
t % estimator = ESTIMATOR_ANALOG
|
||||
|
||||
! Set the surface filter index in the tally find_filter array
|
||||
n_filter = n_filter + 1
|
||||
|
||||
! Allocate and set filters
|
||||
allocate(filter_indices(n_filter))
|
||||
filter_indices(1) = i_filt_end - 1
|
||||
filter_indices(n_filter) = i_filt_end
|
||||
if (energy_filters) then
|
||||
filter_indices(2) = i_filt_start + 1
|
||||
end if
|
||||
|
|
@ -580,15 +541,41 @@ contains
|
|||
! Allocate macro reactions
|
||||
allocate(t % score_bins(1))
|
||||
t % n_score_bins = 1
|
||||
t % n_user_score_bins = 1
|
||||
|
||||
! Allocate scattering order data
|
||||
allocate(t % moment_order(1))
|
||||
t % moment_order = 0
|
||||
|
||||
! Set macro bins
|
||||
t % score_bins(1) = SCORE_CURRENT
|
||||
t % type = TALLY_MESH_CURRENT
|
||||
t % type = TALLY_MESH_SURFACE
|
||||
|
||||
else if (i == 4) then
|
||||
! Set name
|
||||
t % name = "CMFD P1 scatter"
|
||||
|
||||
! Set tally estimator to analog
|
||||
t % estimator = ESTIMATOR_ANALOG
|
||||
|
||||
! Set tally type to volume
|
||||
t % type = TALLY_VOLUME
|
||||
|
||||
! Allocate and set filters
|
||||
n_filter = 2
|
||||
if (energy_filters) then
|
||||
n_filter = n_filter + 1
|
||||
end if
|
||||
allocate(filter_indices(n_filter))
|
||||
filter_indices(1) = i_filt_start
|
||||
filter_indices(2) = i_filt_end
|
||||
if (energy_filters) then
|
||||
filter_indices(3) = i_filt_start + 1
|
||||
end if
|
||||
err = openmc_tally_set_filters(i_start + i - 1, n_filter, filter_indices)
|
||||
deallocate(filter_indices)
|
||||
|
||||
! Allocate scoring bins
|
||||
allocate(t % score_bins(1))
|
||||
t % n_score_bins = 1
|
||||
|
||||
! Set macro_bins
|
||||
t % score_bins(1) = SCORE_SCATTER
|
||||
end if
|
||||
|
||||
! Make CMFD tallies active from the start
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ module constants
|
|||
integer, parameter :: VERSION_STATEPOINT(2) = [17, 0]
|
||||
integer, parameter :: VERSION_PARTICLE_RESTART(2) = [2, 0]
|
||||
integer, parameter :: VERSION_TRACK(2) = [2, 0]
|
||||
integer, parameter :: VERSION_SUMMARY(2) = [5, 0]
|
||||
integer, parameter :: VERSION_SUMMARY(2) = [6, 0]
|
||||
integer, parameter :: VERSION_VOLUME(2) = [1, 0]
|
||||
integer, parameter :: VERSION_VOXEL(2) = [1, 0]
|
||||
integer, parameter :: VERSION_MGXS_LIBRARY(2) = [1, 0]
|
||||
|
|
@ -124,6 +124,9 @@ module constants
|
|||
FILL_MATERIAL = 1, & ! Cell with a specified material
|
||||
FILL_UNIVERSE = 2, & ! Cell filled by a separate universe
|
||||
FILL_LATTICE = 3 ! Cell filled with a lattice
|
||||
integer(C_INT), bind(C, name='FILL_MATERIAL') :: FILL_MATERIAL_C = FILL_MATERIAL
|
||||
integer(C_INT), bind(C, name='FILL_UNIVERSE') :: FILL_UNIVERSE_C = FILL_UNIVERSE
|
||||
integer(C_INT), bind(C, name='FILL_LATTICE') :: FILL_LATTICE_C = FILL_LATTICE
|
||||
|
||||
! Void material
|
||||
integer, parameter :: MATERIAL_VOID = -1
|
||||
|
|
@ -233,7 +236,7 @@ module constants
|
|||
PAIR_PROD_ELEC = 515, PAIR_PROD = 516, PAIR_PROD_NUC = 517
|
||||
|
||||
! Depletion reactions
|
||||
integer, parameter :: DEPLETION_RX(6) = [N_2N, N_3N, N_4N, N_GAMMA, N_P, N_A]
|
||||
integer, parameter :: DEPLETION_RX(6) = [N_GAMMA, N_P, N_A, N_2N, N_3N, N_4N]
|
||||
|
||||
! ACE table types
|
||||
integer, parameter :: &
|
||||
|
|
@ -246,6 +249,10 @@ module constants
|
|||
MGXS_ISOTROPIC = 1, & ! Isotropically Weighted Data
|
||||
MGXS_ANGLE = 2 ! Data by Angular Bins
|
||||
|
||||
! Flag to denote this was a macroscopic data object
|
||||
real(8), parameter :: &
|
||||
MACROSCOPIC_AWR = -TWO
|
||||
|
||||
! Fission neutron emission (nu) type
|
||||
integer, parameter :: &
|
||||
NU_NONE = 0, & ! No nu values (non-fissionable)
|
||||
|
|
@ -306,7 +313,7 @@ module constants
|
|||
! Tally type
|
||||
integer, parameter :: &
|
||||
TALLY_VOLUME = 1, &
|
||||
TALLY_MESH_CURRENT = 2, &
|
||||
TALLY_MESH_SURFACE = 2, &
|
||||
TALLY_SURFACE = 3
|
||||
|
||||
! Tally estimator types
|
||||
|
|
@ -324,55 +331,33 @@ module constants
|
|||
|
||||
! Tally score type -- if you change these, make sure you also update the
|
||||
! _SCORES dictionary in openmc/capi/tally.py
|
||||
integer, parameter :: N_SCORE_TYPES = 24
|
||||
integer, parameter :: N_SCORE_TYPES = 16
|
||||
integer, parameter :: &
|
||||
SCORE_FLUX = -1, & ! flux
|
||||
SCORE_TOTAL = -2, & ! total reaction rate
|
||||
SCORE_SCATTER = -3, & ! scattering rate
|
||||
SCORE_NU_SCATTER = -4, & ! scattering production rate
|
||||
SCORE_SCATTER_N = -5, & ! arbitrary scattering moment
|
||||
SCORE_SCATTER_PN = -6, & ! system for scoring 0th through nth moment
|
||||
SCORE_NU_SCATTER_N = -7, & ! arbitrary nu-scattering moment
|
||||
SCORE_NU_SCATTER_PN = -8, & ! system for scoring 0th through nth nu-scatter moment
|
||||
SCORE_ABSORPTION = -9, & ! absorption rate
|
||||
SCORE_FISSION = -10, & ! fission rate
|
||||
SCORE_NU_FISSION = -11, & ! neutron production rate
|
||||
SCORE_KAPPA_FISSION = -12, & ! fission energy production rate
|
||||
SCORE_CURRENT = -13, & ! current
|
||||
SCORE_FLUX_YN = -14, & ! angular moment of flux
|
||||
SCORE_TOTAL_YN = -15, & ! angular moment of total reaction rate
|
||||
SCORE_SCATTER_YN = -16, & ! angular flux-weighted scattering moment (0:N)
|
||||
SCORE_NU_SCATTER_YN = -17, & ! angular flux-weighted nu-scattering moment (0:N)
|
||||
SCORE_EVENTS = -18, & ! number of events
|
||||
SCORE_DELAYED_NU_FISSION = -19, & ! delayed neutron production rate
|
||||
SCORE_PROMPT_NU_FISSION = -20, & ! prompt neutron production rate
|
||||
SCORE_INVERSE_VELOCITY = -21, & ! flux-weighted inverse velocity
|
||||
SCORE_FISS_Q_PROMPT = -22, & ! prompt fission Q-value
|
||||
SCORE_FISS_Q_RECOV = -23, & ! recoverable fission Q-value
|
||||
SCORE_DECAY_RATE = -24 ! delayed neutron precursor decay rate
|
||||
SCORE_ABSORPTION = -5, & ! absorption rate
|
||||
SCORE_FISSION = -6, & ! fission rate
|
||||
SCORE_NU_FISSION = -7, & ! neutron production rate
|
||||
SCORE_KAPPA_FISSION = -8, & ! fission energy production rate
|
||||
SCORE_CURRENT = -9, & ! current
|
||||
SCORE_EVENTS = -10, & ! number of events
|
||||
SCORE_DELAYED_NU_FISSION = -11, & ! delayed neutron production rate
|
||||
SCORE_PROMPT_NU_FISSION = -12, & ! prompt neutron production rate
|
||||
SCORE_INVERSE_VELOCITY = -13, & ! flux-weighted inverse velocity
|
||||
SCORE_FISS_Q_PROMPT = -14, & ! prompt fission Q-value
|
||||
SCORE_FISS_Q_RECOV = -15, & ! recoverable fission Q-value
|
||||
SCORE_DECAY_RATE = -16 ! delayed neutron precursor decay rate
|
||||
|
||||
! Maximum scattering order supported
|
||||
integer, parameter :: MAX_ANG_ORDER = 10
|
||||
|
||||
! Names of *-PN & *-YN scores (MOMENT_STRS) and *-N moment scores
|
||||
character(*), parameter :: &
|
||||
MOMENT_STRS(6) = (/ "scatter-p ", &
|
||||
"nu-scatter-p", &
|
||||
"flux-y ", &
|
||||
"total-y ", &
|
||||
"scatter-y ", &
|
||||
"nu-scatter-y"/), &
|
||||
MOMENT_N_STRS(2) = (/ "scatter- ", &
|
||||
"nu-scatter- "/)
|
||||
|
||||
! Location in MOMENT_STRS where the YN data begins
|
||||
integer, parameter :: YN_LOC = 3
|
||||
|
||||
! Tally map bin finding
|
||||
integer, parameter :: NO_BIN_FOUND = -1
|
||||
|
||||
! Tally filter and map types
|
||||
integer, parameter :: N_FILTER_TYPES = 16
|
||||
integer, parameter :: N_FILTER_TYPES = 21
|
||||
integer, parameter :: &
|
||||
FILTER_UNIVERSE = 1, &
|
||||
FILTER_MATERIAL = 2, &
|
||||
|
|
@ -389,7 +374,12 @@ module constants
|
|||
FILTER_DELAYEDGROUP = 13, &
|
||||
FILTER_ENERGYFUNCTION = 14, &
|
||||
FILTER_CELLFROM = 15, &
|
||||
FILTER_PARTICLE = 16
|
||||
FILTER_MESHSURFACE = 16, &
|
||||
FILTER_LEGENDRE = 17, &
|
||||
FILTER_SPH_HARMONICS = 18, &
|
||||
FILTER_SPTL_LEGENDRE = 19, &
|
||||
FILTER_ZERNIKE = 20, &
|
||||
FILTER_PARTICLE = 21
|
||||
|
||||
! Mesh types
|
||||
integer, parameter :: &
|
||||
|
|
@ -447,6 +437,7 @@ module constants
|
|||
|
||||
! indicates that an array index hasn't been set
|
||||
integer, parameter :: NONE = 0
|
||||
integer, parameter :: C_NONE = -1
|
||||
|
||||
! Codes for read errors -- better hope these numbers are never used in an
|
||||
! input file!
|
||||
|
|
|
|||
16
src/constants.h
Normal file
16
src/constants.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#include <limits>
|
||||
|
||||
|
||||
namespace openmc{
|
||||
|
||||
extern "C" double FP_COINCIDENT;
|
||||
extern "C" double FP_PRECISION;
|
||||
constexpr double INFTY {std::numeric_limits<double>::max()};
|
||||
constexpr int C_NONE {-1};
|
||||
|
||||
} // namespace openmc
|
||||
|
||||
#endif // CONSTANTS_H
|
||||
|
|
@ -119,7 +119,7 @@ contains
|
|||
else
|
||||
! Sample azimuthal angle
|
||||
phi = this % phi % sample()
|
||||
uvw(:) = rotate_angle(this % reference_uvw, mu, phi)
|
||||
uvw = rotate_angle(this % reference_uvw, mu, phi)
|
||||
end if
|
||||
end function polar_azimuthal_sample
|
||||
|
||||
|
|
|
|||
16
src/endf.F90
16
src/endf.F90
|
|
@ -26,14 +26,6 @@ contains
|
|||
string = "scatter"
|
||||
case (SCORE_NU_SCATTER)
|
||||
string = "nu-scatter"
|
||||
case (SCORE_SCATTER_N)
|
||||
string = "scatter-n"
|
||||
case (SCORE_SCATTER_PN)
|
||||
string = "scatter-pn"
|
||||
case (SCORE_NU_SCATTER_N)
|
||||
string = "nu-scatter-n"
|
||||
case (SCORE_NU_SCATTER_PN)
|
||||
string = "nu-scatter-pn"
|
||||
case (SCORE_ABSORPTION)
|
||||
string = "absorption"
|
||||
case (SCORE_FISSION)
|
||||
|
|
@ -50,14 +42,6 @@ contains
|
|||
string = "kappa-fission"
|
||||
case (SCORE_CURRENT)
|
||||
string = "current"
|
||||
case (SCORE_FLUX_YN)
|
||||
string = "flux-yn"
|
||||
case (SCORE_TOTAL_YN)
|
||||
string = "total-yn"
|
||||
case (SCORE_SCATTER_YN)
|
||||
string = "scatter-yn"
|
||||
case (SCORE_NU_SCATTER_YN)
|
||||
string = "nu-scatter-yn"
|
||||
case (SCORE_EVENTS)
|
||||
string = "events"
|
||||
case (SCORE_INVERSE_VELOCITY)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
module endf_header
|
||||
|
||||
use hdf5, only: HID_T, HSIZE_T
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants, only: ZERO, HISTOGRAM, LINEAR_LINEAR, LINEAR_LOG, &
|
||||
LOG_LINEAR, LOG_LOG
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
module energy_distribution
|
||||
|
||||
use hdf5
|
||||
|
||||
use algorithm, only: binary_search
|
||||
use constants, only: ZERO, ONE, HALF, TWO, PI, HISTOGRAM, LINEAR_LINEAR
|
||||
use endf_header, only: Tabulated1D
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ module error
|
|||
public :: write_message
|
||||
|
||||
! Error codes
|
||||
integer(C_INT), public, bind(C) :: E_UNASSIGNED = -1
|
||||
integer(C_INT), public, bind(C) :: E_ALLOCATE = -2
|
||||
integer(C_INT), public, bind(C) :: E_OUT_OF_BOUNDS = -3
|
||||
integer(C_INT), public, bind(C) :: E_INVALID_SIZE = -4
|
||||
integer(C_INT), public, bind(C) :: E_INVALID_ARGUMENT = -5
|
||||
integer(C_INT), public, bind(C) :: E_INVALID_TYPE = -6
|
||||
integer(C_INT), public, bind(C) :: E_INVALID_ID = -7
|
||||
integer(C_INT), public, bind(C) :: E_GEOMETRY = -8
|
||||
integer(C_INT), public, bind(C) :: E_DATA = -9
|
||||
integer(C_INT), public, bind(C) :: E_PHYSICS = -10
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_UNASSIGNED') :: E_UNASSIGNED = -1
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_ALLOCATE') :: E_ALLOCATE = -2
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_OUT_OF_BOUNDS') :: E_OUT_OF_BOUNDS = -3
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_SIZE') :: E_INVALID_SIZE = -4
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ARGUMENT') :: E_INVALID_ARGUMENT = -5
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_TYPE') :: E_INVALID_TYPE = -6
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_INVALID_ID') :: E_INVALID_ID = -7
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_GEOMETRY') :: E_GEOMETRY = -8
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_DATA') :: E_DATA = -9
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_PHYSICS') :: E_PHYSICS = -10
|
||||
|
||||
! Warning codes
|
||||
integer(C_INT), public, bind(C) :: E_WARNING = 1
|
||||
integer(C_INT), public, bind(C, name='OPENMC_E_WARNING') :: E_WARNING = 1
|
||||
|
||||
! Error message
|
||||
character(kind=C_CHAR), public, bind(C) :: openmc_err_msg(256)
|
||||
|
|
@ -111,6 +111,14 @@ contains
|
|||
|
||||
end subroutine warning
|
||||
|
||||
subroutine warning_from_c(message, message_len) bind(C)
|
||||
integer(C_INT), intent(in), value :: message_len
|
||||
character(kind=C_CHAR), intent(in) :: message(message_len)
|
||||
character(message_len+1) :: message_out
|
||||
write(message_out, *) message
|
||||
call warning(message_out)
|
||||
end subroutine
|
||||
|
||||
!===============================================================================
|
||||
! FATAL_ERROR alerts the user that an error has been encountered and displays a
|
||||
! message about the particular problem. Errors are considered 'fatal' and hence
|
||||
|
|
|
|||
23
src/error.h
23
src/error.h
|
|
@ -9,25 +9,38 @@
|
|||
namespace openmc {
|
||||
|
||||
|
||||
extern "C" void fatal_error_from_c(const char *message, int message_len);
|
||||
extern "C" void fatal_error_from_c(const char* message, int message_len);
|
||||
extern "C" void warning_from_c(const char* message, int message_len);
|
||||
|
||||
|
||||
inline
|
||||
void fatal_error(const char *message)
|
||||
{
|
||||
fatal_error_from_c(message, strlen(message));
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void fatal_error(const std::string &message)
|
||||
{
|
||||
fatal_error_from_c(message.c_str(), message.length());
|
||||
}
|
||||
|
||||
|
||||
inline
|
||||
void fatal_error(const std::stringstream &message)
|
||||
{
|
||||
std::string out {message.str()};
|
||||
fatal_error_from_c(out.c_str(), out.length());
|
||||
fatal_error(message.str());
|
||||
}
|
||||
|
||||
inline
|
||||
void warning(const std::string& message)
|
||||
{
|
||||
warning_from_c(message.c_str(), message.length());
|
||||
}
|
||||
|
||||
inline
|
||||
void warning(const std::stringstream& message)
|
||||
{
|
||||
warning(message.str());
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* Copyright (c) 2012 Massachusetts Institute of Technology
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
|
|
@ -7,17 +7,17 @@
|
|||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Available at: http://ab-initio.mit.edu/Faddeeva
|
||||
|
|
|
|||
10
src/finalize.cpp
Normal file
10
src/finalize.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include "finalize.h"
|
||||
|
||||
#include "message_passing.h"
|
||||
|
||||
void openmc_free_bank()
|
||||
{
|
||||
#ifdef OPENMC_MPI
|
||||
MPI_Type_free(&openmc::mpi::bank);
|
||||
#endif
|
||||
}
|
||||
6
src/finalize.h
Normal file
6
src/finalize.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef FINALIZE_H
|
||||
#define FINALIZE_H
|
||||
|
||||
extern "C" void openmc_free_bank();
|
||||
|
||||
#endif // FINALIZE_H
|
||||
797
src/geometry.F90
797
src/geometry.F90
|
|
@ -14,125 +14,36 @@ module geometry
|
|||
|
||||
implicit none
|
||||
|
||||
interface
|
||||
function cell_contains_c(cell_ptr, xyz, uvw, on_surface) &
|
||||
bind(C, name="cell_contains") result(in_cell)
|
||||
import C_PTR, C_DOUBLE, C_INT32_T, C_BOOL
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
integer(C_INT32_T), intent(in), value :: on_surface
|
||||
logical(C_BOOL) :: in_cell
|
||||
end function cell_contains_c
|
||||
|
||||
function count_universe_instances(search_univ, target_univ_id) bind(C) &
|
||||
result(count)
|
||||
import C_INT32_T, C_INT
|
||||
integer(C_INT32_T), intent(in), value :: search_univ
|
||||
integer(C_INT32_T), intent(in), value :: target_univ_id
|
||||
integer(C_INT) :: count
|
||||
end function
|
||||
end interface
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
! CELL_CONTAINS determines if a cell contains the particle at a given
|
||||
! location. The bounds of the cell are detemined by a logical expression
|
||||
! involving surface half-spaces. At initialization, the expression was converted
|
||||
! to RPN notation.
|
||||
!
|
||||
! The function is split into two cases, one for simple cells (those involving
|
||||
! only the intersection of half-spaces) and one for complex cells. Simple cells
|
||||
! can be evaluated with short circuit evaluation, i.e., as soon as we know that
|
||||
! one half-space is not satisfied, we can exit. This provides a performance
|
||||
! benefit for the common case. In complex_cell_contains, we evaluate the RPN
|
||||
! expression using a stack, similar to how a RPN calculator would work.
|
||||
!===============================================================================
|
||||
|
||||
pure function cell_contains(c, p) result(in_cell)
|
||||
function cell_contains(c, p) result(in_cell)
|
||||
type(Cell), intent(in) :: c
|
||||
type(Particle), intent(in) :: p
|
||||
logical :: in_cell
|
||||
|
||||
if (c%simple) then
|
||||
in_cell = simple_cell_contains(c, p)
|
||||
else
|
||||
in_cell = complex_cell_contains(c, p)
|
||||
end if
|
||||
in_cell = cell_contains_c(c%ptr, p%coord(p%n_coord)%xyz, &
|
||||
p%coord(p%n_coord)%uvw, p%surface)
|
||||
end function cell_contains
|
||||
|
||||
pure function simple_cell_contains(c, p) result(in_cell)
|
||||
type(Cell), intent(in) :: c
|
||||
type(Particle), intent(in) :: p
|
||||
logical :: in_cell
|
||||
|
||||
integer :: i
|
||||
integer :: token
|
||||
logical :: actual_sense ! sense of particle wrt surface
|
||||
|
||||
in_cell = .true.
|
||||
do i = 1, size(c%rpn)
|
||||
token = c%rpn(i)
|
||||
if (token < OP_UNION) then
|
||||
! If the token is not an operator, evaluate the sense of particle with
|
||||
! respect to the surface and see if the token matches the sense. If the
|
||||
! particle's surface attribute is set and matches the token, that
|
||||
! overrides the determination based on sense().
|
||||
if (token == p%surface) then
|
||||
cycle
|
||||
elseif (-token == p%surface) then
|
||||
in_cell = .false.
|
||||
exit
|
||||
else
|
||||
actual_sense = surfaces(abs(token)) % sense( &
|
||||
p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw)
|
||||
if (actual_sense .neqv. (token > 0)) then
|
||||
in_cell = .false.
|
||||
exit
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
end function simple_cell_contains
|
||||
|
||||
pure function complex_cell_contains(c, p) result(in_cell)
|
||||
type(Cell), intent(in) :: c
|
||||
type(Particle), intent(in) :: p
|
||||
logical :: in_cell
|
||||
|
||||
integer :: i
|
||||
integer :: token
|
||||
integer :: i_stack
|
||||
logical :: actual_sense ! sense of particle wrt surface
|
||||
logical :: stack(size(c%rpn))
|
||||
|
||||
i_stack = 0
|
||||
do i = 1, size(c%rpn)
|
||||
token = c%rpn(i)
|
||||
|
||||
! If the token is a binary operator (intersection/union), apply it to
|
||||
! the last two items on the stack. If the token is a unary operator
|
||||
! (complement), apply it to the last item on the stack.
|
||||
select case (token)
|
||||
case (OP_UNION)
|
||||
stack(i_stack - 1) = stack(i_stack - 1) .or. stack(i_stack)
|
||||
i_stack = i_stack - 1
|
||||
case (OP_INTERSECTION)
|
||||
stack(i_stack - 1) = stack(i_stack - 1) .and. stack(i_stack)
|
||||
i_stack = i_stack - 1
|
||||
case (OP_COMPLEMENT)
|
||||
stack(i_stack) = .not. stack(i_stack)
|
||||
case default
|
||||
! If the token is not an operator, evaluate the sense of particle with
|
||||
! respect to the surface and see if the token matches the sense. If the
|
||||
! particle's surface attribute is set and matches the token, that
|
||||
! overrides the determination based on sense().
|
||||
i_stack = i_stack + 1
|
||||
if (token == p%surface) then
|
||||
stack(i_stack) = .true.
|
||||
elseif (-token == p%surface) then
|
||||
stack(i_stack) = .false.
|
||||
else
|
||||
actual_sense = surfaces(abs(token)) % sense( &
|
||||
p%coord(p%n_coord)%xyz, p%coord(p%n_coord)%uvw)
|
||||
stack(i_stack) = (actual_sense .eqv. (token > 0))
|
||||
end if
|
||||
end select
|
||||
|
||||
end do
|
||||
|
||||
if (i_stack == 1) then
|
||||
! The one remaining logical on the stack indicates whether the particle is
|
||||
! in the cell.
|
||||
in_cell = stack(i_stack)
|
||||
else
|
||||
! This case occurs if there is no region specification since i_stack will
|
||||
! still be zero.
|
||||
in_cell = .true.
|
||||
end if
|
||||
end function complex_cell_contains
|
||||
|
||||
!===============================================================================
|
||||
! CHECK_CELL_OVERLAP checks for overlapping cells at the current particle's
|
||||
! position using cell_contains and the LocalCoord's built up by find_cell
|
||||
|
|
@ -166,8 +77,8 @@ contains
|
|||
! the particle should only be contained in one cell per level
|
||||
if (index_cell /= p % coord(j) % cell) then
|
||||
call fatal_error("Overlapping cells detected: " &
|
||||
&// trim(to_str(cells(index_cell) % id)) // ", " &
|
||||
&// trim(to_str(cells(p % coord(j) % cell) % id)) &
|
||||
&// trim(to_str(cells(index_cell) % id())) // ", " &
|
||||
&// trim(to_str(cells(p % coord(j) % cell) % id())) &
|
||||
&// " on universe " // trim(to_str(univ % id)))
|
||||
end if
|
||||
|
||||
|
|
@ -229,7 +140,7 @@ contains
|
|||
if (use_search_cells) then
|
||||
i_cell = search_cells(i)
|
||||
! check to make sure search cell is in same universe
|
||||
if (cells(i_cell) % universe /= i_universe) cycle
|
||||
if (cells(i_cell) % universe() /= i_universe) cycle
|
||||
else
|
||||
i_cell = universes(i_universe) % cells(i)
|
||||
end if
|
||||
|
|
@ -242,7 +153,7 @@ contains
|
|||
! Show cell information on trace
|
||||
if (verbosity >= 10 .or. trace) then
|
||||
call write_message(" Entering cell " // trim(to_str(&
|
||||
cells(i_cell) % id)))
|
||||
cells(i_cell) % id())))
|
||||
end if
|
||||
|
||||
found = .true.
|
||||
|
|
@ -252,7 +163,7 @@ contains
|
|||
|
||||
if (found) then
|
||||
associate(c => cells(i_cell))
|
||||
CELL_TYPE: if (c % type == FILL_MATERIAL) then
|
||||
CELL_TYPE: if (c % type() == FILL_MATERIAL) then
|
||||
! ======================================================================
|
||||
! AT LOWEST UNIVERSE, TERMINATE SEARCH
|
||||
|
||||
|
|
@ -268,20 +179,20 @@ contains
|
|||
distribcell_index = c % distribcell_index
|
||||
offset = 0
|
||||
do k = 1, p % n_coord
|
||||
if (cells(p % coord(k) % cell) % type == FILL_UNIVERSE) then
|
||||
offset = offset + cells(p % coord(k) % cell) % &
|
||||
offset(distribcell_index)
|
||||
elseif (cells(p % coord(k) % cell) % type == FILL_LATTICE) then
|
||||
if (cells(p % coord(k) % cell) % type() == FILL_UNIVERSE) then
|
||||
offset = offset + cells(p % coord(k) % cell) &
|
||||
% offset(distribcell_index-1)
|
||||
elseif (cells(p % coord(k) % cell) % type() == FILL_LATTICE) then
|
||||
if (lattices(p % coord(k + 1) % lattice) % obj &
|
||||
% are_valid_indices([&
|
||||
p % coord(k + 1) % lattice_x, &
|
||||
p % coord(k + 1) % lattice_y, &
|
||||
p % coord(k + 1) % lattice_z])) then
|
||||
offset = offset + lattices(p % coord(k + 1) % lattice) % obj % &
|
||||
offset(distribcell_index, &
|
||||
p % coord(k + 1) % lattice_x, &
|
||||
offset = offset + lattices(p % coord(k + 1) % lattice) % obj &
|
||||
% offset(distribcell_index - 1, &
|
||||
[p % coord(k + 1) % lattice_x, &
|
||||
p % coord(k + 1) % lattice_y, &
|
||||
p % coord(k + 1) % lattice_z)
|
||||
p % coord(k + 1) % lattice_z])
|
||||
end if
|
||||
end if
|
||||
end do
|
||||
|
|
@ -306,7 +217,7 @@ contains
|
|||
p % sqrtkT = c % sqrtkT(1)
|
||||
end if
|
||||
|
||||
elseif (c % type == FILL_UNIVERSE) then CELL_TYPE
|
||||
elseif (c % type() == FILL_UNIVERSE) then CELL_TYPE
|
||||
! ======================================================================
|
||||
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
|
||||
|
||||
|
|
@ -317,7 +228,7 @@ contains
|
|||
! Move particle to next level and set universe
|
||||
j = j + 1
|
||||
p % n_coord = j
|
||||
p % coord(j) % universe = c % fill
|
||||
p % coord(j) % universe = c % fill() + 1
|
||||
|
||||
! Apply translation
|
||||
if (allocated(c % translation)) then
|
||||
|
|
@ -334,11 +245,11 @@ contains
|
|||
call find_cell(p, found)
|
||||
j = p % n_coord
|
||||
|
||||
elseif (c % type == FILL_LATTICE) then CELL_TYPE
|
||||
elseif (c % type() == FILL_LATTICE) then CELL_TYPE
|
||||
! ======================================================================
|
||||
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
|
||||
|
||||
associate (lat => lattices(c % fill) % obj)
|
||||
associate (lat => lattices(c % fill() + 1) % obj)
|
||||
! Determine lattice indices
|
||||
i_xyz = lat % get_indices(p % coord(j) % xyz + TINY_BIT * p % coord(j) % uvw)
|
||||
|
||||
|
|
@ -347,7 +258,7 @@ contains
|
|||
p % coord(j + 1) % uvw = p % coord(j) % uvw
|
||||
|
||||
! set particle lattice indices
|
||||
p % coord(j + 1) % lattice = c % fill
|
||||
p % coord(j + 1) % lattice = c % fill() + 1
|
||||
p % coord(j + 1) % lattice_x = i_xyz(1)
|
||||
p % coord(j + 1) % lattice_y = i_xyz(2)
|
||||
p % coord(j + 1) % lattice_z = i_xyz(3)
|
||||
|
|
@ -356,17 +267,18 @@ contains
|
|||
if (lat % are_valid_indices(i_xyz)) then
|
||||
! Particle is inside the lattice.
|
||||
p % coord(j + 1) % universe = &
|
||||
lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
|
||||
lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1
|
||||
|
||||
else
|
||||
! Particle is outside the lattice.
|
||||
if (lat % outer == NO_OUTER_UNIVERSE) then
|
||||
call p % mark_as_lost("Particle " // trim(to_str(p %id)) &
|
||||
// " is outside lattice " // trim(to_str(lat % id)) &
|
||||
if (lat % outer() == NO_OUTER_UNIVERSE) then
|
||||
call warning("Particle " // trim(to_str(p %id)) &
|
||||
// " is outside lattice " // trim(to_str(lat % id())) &
|
||||
// " but the lattice has no defined outer universe.")
|
||||
found = .false.
|
||||
return
|
||||
else
|
||||
p % coord(j + 1) % universe = lat % outer
|
||||
p % coord(j + 1) % universe = lat % outer() + 1
|
||||
end if
|
||||
end if
|
||||
end associate
|
||||
|
|
@ -401,7 +313,7 @@ contains
|
|||
lat => lattices(p % coord(j) % lattice) % obj
|
||||
|
||||
if (verbosity >= 10 .or. trace) then
|
||||
call write_message(" Crossing lattice " // trim(to_str(lat % id)) &
|
||||
call write_message(" Crossing lattice " // trim(to_str(lat % id())) &
|
||||
&// ". Current position (" // trim(to_str(p % coord(j) % lattice_x)) &
|
||||
&// "," // trim(to_str(p % coord(j) % lattice_y)) // "," &
|
||||
&// trim(to_str(p % coord(j) % lattice_z)) // ")")
|
||||
|
|
@ -433,7 +345,8 @@ contains
|
|||
else OUTSIDE_LAT
|
||||
|
||||
! Find cell in next lattice element
|
||||
p % coord(j) % universe = lat % universes(i_xyz(1), i_xyz(2), i_xyz(3))
|
||||
p % coord(j) % universe = &
|
||||
lat % get([i_xyz(1), i_xyz(2), i_xyz(3)]) + 1
|
||||
|
||||
call find_cell(p, found)
|
||||
if (.not. found) then
|
||||
|
|
@ -470,26 +383,15 @@ contains
|
|||
integer, intent(out) :: lattice_translation(3)
|
||||
integer, intent(out) :: next_level
|
||||
|
||||
integer :: i ! index for surface in cell
|
||||
integer :: j
|
||||
integer :: index_surf ! index in surfaces array (with sign)
|
||||
integer :: i_xyz(3) ! lattice indices
|
||||
integer :: level_surf_cross ! surface crossed on current level
|
||||
integer :: level_lat_trans(3) ! lattice translation on current level
|
||||
real(8) :: x,y,z ! particle coordinates
|
||||
real(8) :: xyz_t(3) ! local particle coordinates
|
||||
real(8) :: beta, gama ! skewed particle coordiantes
|
||||
real(8) :: u,v,w ! particle directions
|
||||
real(8) :: beta_dir ! skewed particle direction
|
||||
real(8) :: gama_dir ! skewed particle direction
|
||||
real(8) :: edge ! distance to oncoming edge
|
||||
real(8) :: d ! evaluated distance
|
||||
real(8) :: d_lat ! distance to lattice boundary
|
||||
real(8) :: d_surf ! distance to surface
|
||||
real(8) :: x0,y0,z0 ! coefficients for surface
|
||||
real(8) :: xyz_cross(3) ! coordinates at projected surface crossing
|
||||
real(8) :: surf_uvw(3) ! surface normal direction
|
||||
logical :: coincident ! is particle on surface?
|
||||
type(Cell), pointer :: c
|
||||
class(Lattice), pointer :: lat
|
||||
|
||||
|
|
@ -507,35 +409,11 @@ contains
|
|||
! get pointer to cell on this level
|
||||
c => cells(p % coord(j) % cell)
|
||||
|
||||
! copy directional cosines
|
||||
u = p % coord(j) % uvw(1)
|
||||
v = p % coord(j) % uvw(2)
|
||||
w = p % coord(j) % uvw(3)
|
||||
|
||||
! =======================================================================
|
||||
! FIND MINIMUM DISTANCE TO SURFACE IN THIS CELL
|
||||
|
||||
SURFACE_LOOP: do i = 1, size(c % region)
|
||||
index_surf = c % region(i)
|
||||
coincident = (index_surf == p % surface)
|
||||
|
||||
! ignore this token if it corresponds to an operator rather than a
|
||||
! region.
|
||||
index_surf = abs(index_surf)
|
||||
if (index_surf >= OP_UNION) cycle
|
||||
|
||||
! Calculate distance to surface
|
||||
d = surfaces(index_surf) % distance(p % coord(j) % xyz, &
|
||||
p % coord(j) % uvw, logical(coincident, kind=C_BOOL))
|
||||
|
||||
! Check if calculated distance is new minimum
|
||||
if (d < d_surf) then
|
||||
if (abs(d - d_surf)/d_surf >= FP_PRECISION) then
|
||||
d_surf = d
|
||||
level_surf_cross = -c % region(i)
|
||||
end if
|
||||
end if
|
||||
end do SURFACE_LOOP
|
||||
call c % distance(p % coord(j) % xyz, p % coord(j) % uvw, p % surface, &
|
||||
d_surf, level_surf_cross)
|
||||
|
||||
! =======================================================================
|
||||
! FIND MINIMUM DISTANCE TO LATTICE SURFACES
|
||||
|
|
@ -543,185 +421,22 @@ contains
|
|||
LAT_COORD: if (p % coord(j) % lattice /= NONE) then
|
||||
lat => lattices(p % coord(j) % lattice) % obj
|
||||
|
||||
i_xyz(1) = p % coord(j) % lattice_x
|
||||
i_xyz(2) = p % coord(j) % lattice_y
|
||||
i_xyz(3) = p % coord(j) % lattice_z
|
||||
|
||||
LAT_TYPE: select type(lat)
|
||||
|
||||
type is (RectLattice)
|
||||
! copy local coordinates
|
||||
x = p % coord(j) % xyz(1)
|
||||
y = p % coord(j) % xyz(2)
|
||||
z = p % coord(j) % xyz(3)
|
||||
|
||||
! determine oncoming edge
|
||||
x0 = sign(lat % pitch(1) * HALF, u)
|
||||
y0 = sign(lat % pitch(2) * HALF, v)
|
||||
|
||||
! left and right sides
|
||||
if (abs(x - x0) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
elseif (u == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (x0 - x)/u
|
||||
end if
|
||||
|
||||
d_lat = d
|
||||
if (u > 0) then
|
||||
level_lat_trans(:) = [1, 0, 0]
|
||||
else
|
||||
level_lat_trans(:) = [-1, 0, 0]
|
||||
end if
|
||||
|
||||
! front and back sides
|
||||
if (abs(y - y0) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
elseif (v == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (y0 - y)/v
|
||||
end if
|
||||
|
||||
if (d < d_lat) then
|
||||
d_lat = d
|
||||
if (v > 0) then
|
||||
level_lat_trans(:) = [0, 1, 0]
|
||||
else
|
||||
level_lat_trans(:) = [0, -1, 0]
|
||||
end if
|
||||
end if
|
||||
|
||||
if (lat % is_3d) then
|
||||
z0 = sign(lat % pitch(3) * HALF, w)
|
||||
|
||||
! top and bottom sides
|
||||
if (abs(z - z0) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
elseif (w == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (z0 - z)/w
|
||||
end if
|
||||
|
||||
if (d < d_lat) then
|
||||
d_lat = d
|
||||
if (w > 0) then
|
||||
level_lat_trans(:) = [0, 0, 1]
|
||||
else
|
||||
level_lat_trans(:) = [0, 0, -1]
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
call lat % distance(p % coord(j) % xyz, p % coord(j) % uvw, &
|
||||
i_xyz, d_lat, level_lat_trans)
|
||||
|
||||
type is (HexLattice) LAT_TYPE
|
||||
! Copy local coordinates.
|
||||
z = p % coord(j) % xyz(3)
|
||||
i_xyz(1) = p % coord(j) % lattice_x
|
||||
i_xyz(2) = p % coord(j) % lattice_y
|
||||
i_xyz(3) = p % coord(j) % lattice_z
|
||||
|
||||
! Compute velocities along the hexagonal axes.
|
||||
beta_dir = u*sqrt(THREE)/TWO + v/TWO
|
||||
gama_dir = u*sqrt(THREE)/TWO - v/TWO
|
||||
|
||||
! Note that hexagonal lattice distance calculations are performed
|
||||
! using the particle's coordinates relative to the neighbor lattice
|
||||
! cells, not relative to the particle's current cell. This is done
|
||||
! because there is significant disagreement between neighboring cells
|
||||
! on where the lattice boundary is due to the worse finite precision
|
||||
! of hex lattices.
|
||||
|
||||
! Upper right and lower left sides.
|
||||
edge = -sign(lat % pitch(1)/TWO, beta_dir) ! Oncoming edge
|
||||
if (beta_dir > ZERO) then
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, 0, 0])
|
||||
else
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 0, 0])
|
||||
end if
|
||||
beta = xyz_t(1)*sqrt(THREE)/TWO + xyz_t(2)/TWO
|
||||
if (abs(beta - edge) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
else if (beta_dir == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (edge - beta)/beta_dir
|
||||
end if
|
||||
|
||||
d_lat = d
|
||||
if (beta_dir > 0) then
|
||||
level_lat_trans(:) = [1, 0, 0]
|
||||
else
|
||||
level_lat_trans(:) = [-1, 0, 0]
|
||||
end if
|
||||
|
||||
! Lower right and upper left sides.
|
||||
edge = -sign(lat % pitch(1)/TWO, gama_dir) ! Oncoming edge
|
||||
if (gama_dir > ZERO) then
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[1, -1, 0])
|
||||
else
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[-1, 1, 0])
|
||||
end if
|
||||
gama = xyz_t(1)*sqrt(THREE)/TWO - xyz_t(2)/TWO
|
||||
if (abs(gama - edge) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
else if (gama_dir == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (edge - gama)/gama_dir
|
||||
end if
|
||||
|
||||
if (d < d_lat) then
|
||||
d_lat = d
|
||||
if (gama_dir > 0) then
|
||||
level_lat_trans(:) = [1, -1, 0]
|
||||
else
|
||||
level_lat_trans(:) = [-1, 1, 0]
|
||||
end if
|
||||
end if
|
||||
|
||||
! Upper and lower sides.
|
||||
edge = -sign(lat % pitch(1)/TWO, v) ! Oncoming edge
|
||||
if (v > ZERO) then
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, 1, 0])
|
||||
else
|
||||
xyz_t = lat % get_local_xyz(p % coord(j - 1) % xyz, i_xyz+[0, -1, 0])
|
||||
end if
|
||||
if (abs(xyz_t(2) - edge) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
else if (v == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (edge - xyz_t(2))/v
|
||||
end if
|
||||
|
||||
if (d < d_lat) then
|
||||
d_lat = d
|
||||
if (v > 0) then
|
||||
level_lat_trans(:) = [0, 1, 0]
|
||||
else
|
||||
level_lat_trans(:) = [0, -1, 0]
|
||||
end if
|
||||
end if
|
||||
|
||||
! Top and bottom sides.
|
||||
if (lat % is_3d) then
|
||||
z0 = sign(lat % pitch(2) * HALF, w)
|
||||
|
||||
if (abs(z - z0) < FP_PRECISION) then
|
||||
d = INFINITY
|
||||
elseif (w == ZERO) then
|
||||
d = INFINITY
|
||||
else
|
||||
d = (z0 - z)/w
|
||||
end if
|
||||
|
||||
if (d < d_lat) then
|
||||
d_lat = d
|
||||
if (w > 0) then
|
||||
level_lat_trans(:) = [0, 0, 1]
|
||||
else
|
||||
level_lat_trans(:) = [0, 0, -1]
|
||||
end if
|
||||
end if
|
||||
end if
|
||||
xyz_t(1) = p % coord(j-1) % xyz(1)
|
||||
xyz_t(2) = p % coord(j-1) % xyz(2)
|
||||
xyz_t(3) = p % coord(j) % xyz(3)
|
||||
call lat % distance(xyz_t, p % coord(j) % uvw, &
|
||||
i_xyz, d_lat, level_lat_trans)
|
||||
end select LAT_TYPE
|
||||
|
||||
if (d_lat < ZERO) then
|
||||
|
|
@ -743,7 +458,7 @@ contains
|
|||
! positive half-space were given in the region specification. Thus, we
|
||||
! have to explicitly check which half-space the particle would be
|
||||
! traveling into if the surface is crossed
|
||||
if (.not. c % simple) then
|
||||
if (.not. c % simple()) then
|
||||
xyz_cross(:) = p % coord(j) % xyz + d_surf*p % coord(j) % uvw
|
||||
call surfaces(abs(level_surf_cross)) % normal(xyz_cross, surf_uvw)
|
||||
if (dot_product(p % coord(j) % uvw, surf_uvw) > ZERO) then
|
||||
|
|
@ -825,388 +540,4 @@ contains
|
|||
|
||||
end subroutine neighbor_lists
|
||||
|
||||
!===============================================================================
|
||||
! CALC_OFFSETS calculates and stores the offsets in all fill cells. This
|
||||
! routine is called once upon initialization.
|
||||
!===============================================================================
|
||||
|
||||
subroutine calc_offsets(univ_id, map, univ, counts, found)
|
||||
|
||||
integer, intent(in) :: univ_id ! target universe ID
|
||||
integer, intent(in) :: map ! map index in vector of maps
|
||||
type(Universe), intent(in) :: univ ! universe searching in
|
||||
integer, intent(inout) :: counts(:,:) ! target count
|
||||
logical, intent(inout) :: found(:,:) ! target found
|
||||
|
||||
integer :: i ! index over cells
|
||||
integer :: j, k, m ! indices in lattice
|
||||
integer :: n ! number of cells to search
|
||||
integer :: offset ! total offset for a given cell
|
||||
integer :: cell_index ! index in cells array
|
||||
type(Cell), pointer :: c ! pointer to current cell
|
||||
type(Universe), pointer :: next_univ ! next universe to cycle through
|
||||
class(Lattice), pointer :: lat ! pointer to current lattice
|
||||
|
||||
n = size(univ % cells)
|
||||
offset = 0
|
||||
|
||||
do i = 1, n
|
||||
|
||||
cell_index = univ % cells(i)
|
||||
|
||||
! get pointer to cell
|
||||
c => cells(cell_index)
|
||||
|
||||
! ====================================================================
|
||||
! AT LOWEST UNIVERSE, TERMINATE SEARCH
|
||||
if (c % type == FILL_MATERIAL) then
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_UNIVERSE) then
|
||||
! Set offset for the cell on this level
|
||||
c % offset(map) = offset
|
||||
|
||||
! Count contents of this cell
|
||||
next_univ => universes(c % fill)
|
||||
offset = offset + count_target(next_univ, counts, found, univ_id, map)
|
||||
|
||||
! Move into the next universe
|
||||
next_univ => universes(c % fill)
|
||||
c => cells(cell_index)
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_LATTICE) then
|
||||
|
||||
! Set current lattice
|
||||
lat => lattices(c % fill) % obj
|
||||
|
||||
select type (lat)
|
||||
|
||||
type is (RectLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do j = 1, lat % n_cells(1)
|
||||
do k = 1, lat % n_cells(2)
|
||||
do m = 1, lat % n_cells(3)
|
||||
lat % offset(map, j, k, m) = offset
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
offset = offset + &
|
||||
count_target(next_univ, counts, found, univ_id, map)
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
type is (HexLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do m = 1, lat % n_axial
|
||||
do k = 1, 2*lat % n_rings - 1
|
||||
do j = 1, 2*lat % n_rings - 1
|
||||
! This array location is never used
|
||||
if (j + k < lat % n_rings + 1) then
|
||||
cycle
|
||||
! This array location is never used
|
||||
else if (j + k > 3*lat % n_rings - 1) then
|
||||
cycle
|
||||
else
|
||||
lat % offset(map, j, k, m) = offset
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
offset = offset + &
|
||||
count_target(next_univ, counts, found, univ_id, map)
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
end select
|
||||
|
||||
end if
|
||||
end do
|
||||
|
||||
end subroutine calc_offsets
|
||||
|
||||
!===============================================================================
|
||||
! COUNT_TARGET recursively totals the numbers of occurances of a given
|
||||
! universe ID beginning with the universe given.
|
||||
!===============================================================================
|
||||
|
||||
recursive function count_target(univ, counts, found, univ_id, map) result(count)
|
||||
|
||||
type(Universe), intent(in) :: univ ! universe to search through
|
||||
integer, intent(inout) :: counts(:,:) ! target count
|
||||
logical, intent(inout) :: found(:,:) ! target found
|
||||
integer, intent(in) :: univ_id ! target universe ID
|
||||
integer, intent(in) :: map ! current map
|
||||
|
||||
integer :: i ! index over cells
|
||||
integer :: j, k, m ! indices in lattice
|
||||
integer :: n ! number of cells to search
|
||||
integer :: cell_index ! index in cells array
|
||||
integer :: count ! number of times target located
|
||||
type(Cell), pointer :: c ! pointer to current cell
|
||||
type(Universe), pointer :: next_univ ! next univ to loop through
|
||||
class(Lattice), pointer :: lat ! pointer to current lattice
|
||||
|
||||
! Don't research places already checked
|
||||
if (found(universe_dict % get(univ % id), map)) then
|
||||
count = counts(universe_dict % get(univ % id), map)
|
||||
return
|
||||
end if
|
||||
|
||||
! If this is the target, it can't contain itself.
|
||||
! Count = 1, then quit
|
||||
if (univ % id == univ_id) then
|
||||
count = 1
|
||||
counts(universe_dict % get(univ % id), map) = 1
|
||||
found(universe_dict % get(univ % id), map) = .true.
|
||||
return
|
||||
end if
|
||||
|
||||
count = 0
|
||||
n = size(univ % cells)
|
||||
|
||||
do i = 1, n
|
||||
|
||||
cell_index = univ % cells(i)
|
||||
|
||||
! get pointer to cell
|
||||
c => cells(cell_index)
|
||||
|
||||
! ====================================================================
|
||||
! AT LOWEST UNIVERSE, TERMINATE SEARCH
|
||||
if (c % type == FILL_MATERIAL) then
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_UNIVERSE) then
|
||||
|
||||
next_univ => universes(c % fill)
|
||||
|
||||
! Found target - stop since target cannot contain itself
|
||||
if (next_univ % id == univ_id) then
|
||||
count = count + 1
|
||||
return
|
||||
end if
|
||||
|
||||
count = count + count_target(next_univ, counts, found, univ_id, map)
|
||||
c => cells(cell_index)
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_LATTICE) then
|
||||
|
||||
! Set current lattice
|
||||
lat => lattices(c % fill) % obj
|
||||
|
||||
select type (lat)
|
||||
|
||||
type is (RectLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do j = 1, lat % n_cells(1)
|
||||
do k = 1, lat % n_cells(2)
|
||||
do m = 1, lat % n_cells(3)
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
|
||||
! Found target - stop since target cannot contain itself
|
||||
if (next_univ % id == univ_id) then
|
||||
count = count + 1
|
||||
cycle
|
||||
end if
|
||||
|
||||
count = count + &
|
||||
count_target(next_univ, counts, found, univ_id, map)
|
||||
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
type is (HexLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do m = 1, lat % n_axial
|
||||
do k = 1, 2*lat % n_rings - 1
|
||||
do j = 1, 2*lat % n_rings - 1
|
||||
! This array location is never used
|
||||
if (j + k < lat % n_rings + 1) then
|
||||
cycle
|
||||
! This array location is never used
|
||||
else if (j + k > 3*lat % n_rings - 1) then
|
||||
cycle
|
||||
else
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
|
||||
! Found target - stop since target cannot contain itself
|
||||
if (next_univ % id == univ_id) then
|
||||
count = count + 1
|
||||
cycle
|
||||
end if
|
||||
|
||||
count = count + &
|
||||
count_target(next_univ, counts, found, univ_id, map)
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
end select
|
||||
|
||||
end if
|
||||
end do
|
||||
|
||||
counts(universe_dict % get(univ % id), map) = count
|
||||
found(universe_dict % get(univ % id), map) = .true.
|
||||
|
||||
end function count_target
|
||||
|
||||
!===============================================================================
|
||||
! COUNT_INSTANCE recursively totals the number of occurrences of all cells
|
||||
! beginning with the universe given.
|
||||
!===============================================================================
|
||||
|
||||
recursive subroutine count_instance(univ)
|
||||
|
||||
type(Universe), intent(in) :: univ ! universe to search through
|
||||
|
||||
integer :: i ! index over cells
|
||||
integer :: j, k, m ! indices in lattice
|
||||
integer :: n ! number of cells to search
|
||||
|
||||
n = size(univ % cells)
|
||||
|
||||
do i = 1, n
|
||||
associate (c => cells(univ % cells(i)))
|
||||
c % instances = c % instances + 1
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
|
||||
if (c % type == FILL_UNIVERSE) then
|
||||
|
||||
call count_instance(universes(c % fill))
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_LATTICE) then
|
||||
|
||||
! Set current lattice
|
||||
associate (lat => lattices(c % fill) % obj)
|
||||
|
||||
select type (lat)
|
||||
type is (RectLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do j = 1, lat % n_cells(1)
|
||||
do k = 1, lat % n_cells(2)
|
||||
do m = 1, lat % n_cells(3)
|
||||
call count_instance(universes(lat % universes(j, k, m)))
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
type is (HexLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do m = 1, lat % n_axial
|
||||
do k = 1, 2*lat % n_rings - 1
|
||||
do j = 1, 2*lat % n_rings - 1
|
||||
! This array location is never used
|
||||
if (j + k < lat % n_rings + 1) then
|
||||
cycle
|
||||
! This array location is never used
|
||||
else if (j + k > 3*lat % n_rings - 1) then
|
||||
cycle
|
||||
else
|
||||
call count_instance(universes(lat % universes(j, k, m)))
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
end select
|
||||
end associate
|
||||
end if
|
||||
end associate
|
||||
end do
|
||||
|
||||
end subroutine count_instance
|
||||
|
||||
!===============================================================================
|
||||
! MAXIMUM_LEVELS determines the maximum number of nested coordinate levels in
|
||||
! the geometry
|
||||
!===============================================================================
|
||||
|
||||
recursive function maximum_levels(univ) result(levels)
|
||||
|
||||
type(Universe), intent(in) :: univ ! universe to search through
|
||||
integer :: levels ! maximum number of levels for this universe
|
||||
|
||||
integer :: i ! index over cells
|
||||
integer :: j, k, m ! indices in lattice
|
||||
integer :: levels_below ! max levels below this universe
|
||||
type(Cell), pointer :: c ! pointer to current cell
|
||||
type(Universe), pointer :: next_univ ! next universe to loop through
|
||||
class(Lattice), pointer :: lat ! pointer to current lattice
|
||||
|
||||
levels_below = 0
|
||||
do i = 1, size(univ % cells)
|
||||
c => cells(univ % cells(i))
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LOWER UNIVERSE, RECURSIVELY FIND CELL
|
||||
if (c % type == FILL_UNIVERSE) then
|
||||
|
||||
next_univ => universes(c % fill)
|
||||
levels_below = max(levels_below, maximum_levels(next_univ))
|
||||
|
||||
! ====================================================================
|
||||
! CELL CONTAINS LATTICE, RECURSIVELY FIND CELL
|
||||
elseif (c % type == FILL_LATTICE) then
|
||||
|
||||
! Set current lattice
|
||||
lat => lattices(c % fill) % obj
|
||||
|
||||
select type (lat)
|
||||
|
||||
type is (RectLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do j = 1, lat % n_cells(1)
|
||||
do k = 1, lat % n_cells(2)
|
||||
do m = 1, lat % n_cells(3)
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
levels_below = max(levels_below, maximum_levels(next_univ))
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
type is (HexLattice)
|
||||
|
||||
! Loop over lattice coordinates
|
||||
do m = 1, lat % n_axial
|
||||
do k = 1, 2*lat % n_rings - 1
|
||||
do j = 1, 2*lat % n_rings - 1
|
||||
! This array location is never used
|
||||
if (j + k < lat % n_rings + 1) then
|
||||
cycle
|
||||
! This array location is never used
|
||||
else if (j + k > 3*lat % n_rings - 1) then
|
||||
cycle
|
||||
else
|
||||
next_univ => universes(lat % universes(j, k, m))
|
||||
levels_below = max(levels_below, maximum_levels(next_univ))
|
||||
end if
|
||||
end do
|
||||
end do
|
||||
end do
|
||||
|
||||
end select
|
||||
|
||||
end if
|
||||
end do
|
||||
|
||||
levels = 1 + levels_below
|
||||
|
||||
end function maximum_levels
|
||||
|
||||
end module geometry
|
||||
|
|
|
|||
341
src/geometry_aux.cpp
Normal file
341
src/geometry_aux.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
#include "geometry_aux.h"
|
||||
|
||||
#include <algorithm> // for std::max
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "cell.h"
|
||||
#include "constants.h"
|
||||
#include "error.h"
|
||||
#include "lattice.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
adjust_indices_c()
|
||||
{
|
||||
// Adjust material/fill idices.
|
||||
for (Cell *c : global_cells) {
|
||||
if (c->material[0] == C_NONE) {
|
||||
int32_t id = c->fill;
|
||||
auto search_univ = universe_map.find(id);
|
||||
auto search_lat = lattice_map.find(id);
|
||||
if (search_univ != universe_map.end()) {
|
||||
c->type = FILL_UNIVERSE;
|
||||
c->fill = search_univ->second;
|
||||
} else if (search_lat != lattice_map.end()) {
|
||||
c->type = FILL_LATTICE;
|
||||
c->fill = search_lat->second;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Specified fill " << id << " on cell " << c->id
|
||||
<< " is neither a universe nor a lattice.";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
} else {
|
||||
//TODO: materials
|
||||
c->type = FILL_MATERIAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Change cell.universe values from IDs to indices.
|
||||
for (Cell *c : global_cells) {
|
||||
auto search = universe_map.find(c->universe);
|
||||
if (search != universe_map.end()) {
|
||||
//TODO: Remove this off-by-one indexing.
|
||||
c->universe = search->second + 1;
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Could not find universe " << c->universe
|
||||
<< " specified on cell " << c->id;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Change all lattice universe values from IDs to indices.
|
||||
for (Lattice *l : lattices_c) {
|
||||
l->adjust_indices();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int32_t
|
||||
find_root_universe()
|
||||
{
|
||||
// Find all the universes listed as a cell fill.
|
||||
std::unordered_set<int32_t> fill_univ_ids;
|
||||
for (Cell *c : global_cells) {
|
||||
fill_univ_ids.insert(c->fill);
|
||||
}
|
||||
|
||||
// Find all the universes contained in a lattice.
|
||||
for (Lattice *lat : lattices_c) {
|
||||
for (auto it = lat->begin(); it != lat->end(); ++it) {
|
||||
fill_univ_ids.insert(*it);
|
||||
}
|
||||
if (lat->outer != NO_OUTER_UNIVERSE) {
|
||||
fill_univ_ids.insert(lat->outer);
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out which universe is not in the set. This is the root universe.
|
||||
bool root_found {false};
|
||||
int32_t root_univ;
|
||||
for (int32_t i = 0; i < global_universes.size(); i++) {
|
||||
auto search = fill_univ_ids.find(global_universes[i]->id);
|
||||
if (search == fill_univ_ids.end()) {
|
||||
if (root_found) {
|
||||
fatal_error("Two or more universes are not used as fill universes, so "
|
||||
"it is not possible to distinguish which one is the root "
|
||||
"universe.");
|
||||
} else {
|
||||
root_found = true;
|
||||
root_univ = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!root_found) fatal_error("Could not find a root universe. Make sure "
|
||||
"there are no circular dependencies in the geometry.");
|
||||
|
||||
return root_univ;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
allocate_offset_tables(int n_maps)
|
||||
{
|
||||
for (Cell *c : global_cells) {
|
||||
if (c->type != FILL_MATERIAL) {
|
||||
c->offset.resize(n_maps, C_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
for (Lattice *lat : lattices_c) {
|
||||
lat->allocate_offset_table(n_maps);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
count_cell_instances(int32_t univ_indx)
|
||||
{
|
||||
for (int32_t cell_indx : global_universes[univ_indx]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
++c.n_instances;
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
// This cell contains another universe. Recurse into that universe.
|
||||
count_cell_instances(c.fill);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
// This cell contains a lattice. Recurse into the lattice universes.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
count_cell_instances(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int
|
||||
count_universe_instances(int32_t search_univ, int32_t target_univ_id)
|
||||
{
|
||||
// If this is the target, it can't contain itself.
|
||||
if (global_universes[search_univ]->id == target_univ_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int count {0};
|
||||
for (int32_t cell_indx : global_universes[search_univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
count += count_universe_instances(next_univ, target_univ_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
fill_offset_tables(int32_t target_univ_id, int map)
|
||||
{
|
||||
for (Universe *univ : global_universes) {
|
||||
int32_t offset {0}; // TODO: is this a bug? It matches F90 implementation.
|
||||
for (int32_t cell_indx : univ->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
c.offset[map] = offset;
|
||||
int32_t search_univ = c.fill;
|
||||
offset += count_universe_instances(search_univ, target_univ_id);
|
||||
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
offset = lat.fill_offset_table(offset, target_univ_id, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
std::string
|
||||
distribcell_path_inner(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
const Universe &search_univ, int32_t offset)
|
||||
{
|
||||
std::stringstream path;
|
||||
|
||||
path << "u" << search_univ.id << "->";
|
||||
|
||||
// Check to see if this universe directly contains the target cell. If so,
|
||||
// write to the path and return.
|
||||
for (int32_t cell_indx : search_univ.cells) {
|
||||
if ((cell_indx == target_cell) && (offset == target_offset)) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
path << "c" << c.id;
|
||||
return path.str();
|
||||
}
|
||||
}
|
||||
|
||||
// The target must be further down the geometry tree and contained in a fill
|
||||
// cell or lattice cell in this universe. Find which cell contains the
|
||||
// target.
|
||||
std::vector<std::int32_t>::const_reverse_iterator cell_it
|
||||
{search_univ.cells.crbegin()};
|
||||
for (; cell_it != search_univ.cells.crend(); ++cell_it) {
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
|
||||
// Material cells don't contain other cells so ignore them.
|
||||
if (c.type != FILL_MATERIAL) {
|
||||
int32_t temp_offset;
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
temp_offset = offset + c.offset[map];
|
||||
} else {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
int32_t indx = lat.universes.size()*map + lat.begin().indx;
|
||||
temp_offset = offset + lat.offsets[indx];
|
||||
}
|
||||
|
||||
// The desired cell is the first cell that gives an offset smaller or
|
||||
// equal to the target offset.
|
||||
if (temp_offset <= target_offset) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the cell to the path string.
|
||||
Cell &c = *global_cells[*cell_it];
|
||||
path << "c" << c.id << "->";
|
||||
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
// Recurse into the fill cell.
|
||||
offset += c.offset[map];
|
||||
path << distribcell_path_inner(target_cell, map, target_offset,
|
||||
*global_universes[c.fill], offset);
|
||||
return path.str();
|
||||
} else {
|
||||
// Recurse into the lattice cell.
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
path << "l" << lat.id;
|
||||
for (ReverseLatticeIter it = lat.rbegin(); it != lat.rend(); ++it) {
|
||||
int32_t indx = lat.universes.size()*map + it.indx;
|
||||
int32_t temp_offset = offset + lat.offsets[indx];
|
||||
if (temp_offset <= target_offset) {
|
||||
offset = temp_offset;
|
||||
path << "(" << lat.index_to_string(it.indx) << ")->";
|
||||
path << distribcell_path_inner(target_cell, map, target_offset,
|
||||
*global_universes[*it], offset);
|
||||
return path.str();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int
|
||||
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
return path_.size() + 1;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ, char *path)
|
||||
{
|
||||
Universe &root = *global_universes[root_univ];
|
||||
std::string path_ {distribcell_path_inner(target_cell, map, target_offset,
|
||||
root, 0)};
|
||||
path_.copy(path, path_.size());
|
||||
path[path_.size()] = '\0';
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int
|
||||
maximum_levels(int32_t univ)
|
||||
{
|
||||
int levels_below {0};
|
||||
|
||||
for (int32_t cell_indx : global_universes[univ]->cells) {
|
||||
Cell &c = *global_cells[cell_indx];
|
||||
if (c.type == FILL_UNIVERSE) {
|
||||
int32_t next_univ = c.fill;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
} else if (c.type == FILL_LATTICE) {
|
||||
Lattice &lat = *lattices_c[c.fill];
|
||||
for (auto it = lat.begin(); it != lat.end(); ++it) {
|
||||
int32_t next_univ = *it;
|
||||
levels_below = std::max(levels_below, maximum_levels(next_univ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++levels_below;
|
||||
return levels_below;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
void
|
||||
free_memory_geometry_c()
|
||||
{
|
||||
for (Cell *c : global_cells) {delete c;}
|
||||
global_cells.clear();
|
||||
cell_map.clear();
|
||||
n_cells = 0;
|
||||
|
||||
for (Universe *u : global_universes) {delete u;}
|
||||
global_universes.clear();
|
||||
universe_map.clear();
|
||||
|
||||
for (Lattice *lat : lattices_c) {delete lat;}
|
||||
lattices_c.clear();
|
||||
lattice_map.clear();
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
112
src/geometry_aux.h
Normal file
112
src/geometry_aux.h
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
//! \file geometry_aux.h
|
||||
//! Auxilary functions for geometry initialization and general data handling.
|
||||
|
||||
#ifndef GEOMETRY_AUX_H
|
||||
#define GEOMETRY_AUX_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
//==============================================================================
|
||||
//! Replace Universe, Lattice, and Material IDs with indices.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void adjust_indices_c();
|
||||
|
||||
//==============================================================================
|
||||
//! Figure out which Universe is the root universe.
|
||||
//!
|
||||
//! This function looks for a universe that is not listed in a Cell::fill or in
|
||||
//! a Lattice.
|
||||
//! @return The index of the root universe.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int32_t find_root_universe();
|
||||
|
||||
//==============================================================================
|
||||
//! Allocate storage in Lattice and Cell objects for distribcell offset tables.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void allocate_offset_tables(int n_maps);
|
||||
|
||||
//==============================================================================
|
||||
//! Recursively search through the geometry and count cell instances.
|
||||
//!
|
||||
//! This function will update the Cell::n_instances value for each cell in the
|
||||
//! geometry.
|
||||
//! @param univ_indx The index of the universe to begin searching from (probably
|
||||
//! the root universe).
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void count_cell_instances(int32_t univ_indx);
|
||||
|
||||
//==============================================================================
|
||||
//! Recursively search through universes and count universe instances.
|
||||
//! @param search_univ The index of the universe to begin searching from.
|
||||
//! @param target_univ_id The ID of the universe to be counted.
|
||||
//! @return The number of instances of target_univ_id in the geometry tree under
|
||||
//! search_univ.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
count_universe_instances(int32_t search_univ, int32_t target_univ_id);
|
||||
|
||||
//==============================================================================
|
||||
//! Populate Cell and Lattice distribcell offset tables.
|
||||
//! @param target_univ_id The ID of the universe to be counted.
|
||||
//! @param map The index of the distribcell map that defines the offsets for the
|
||||
//! target universe.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void fill_offset_tables(int32_t target_univ_id, int map);
|
||||
|
||||
//==============================================================================
|
||||
//! Find the length necessary for a string to contain a distribcell path.
|
||||
//! @param target_cell The index of the Cell in the global Cell array.
|
||||
//! @param map The index of the distribcell mapping corresponding to the target
|
||||
//! cell.
|
||||
//! @param target_offset An instance number for a distributed cell.
|
||||
//! @param root_univ The index of the root Universe in the global Universe
|
||||
//! array.
|
||||
//! @return The size of a character array needed to fit the distribcell path.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int
|
||||
distribcell_path_len(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ);
|
||||
|
||||
//==============================================================================
|
||||
//! Build a character array representing the path to a distribcell instance.
|
||||
//! @param target_cell The index of the Cell in the global Cell array.
|
||||
//! @param map The index of the distribcell mapping corresponding to the target
|
||||
//! cell.
|
||||
//! @param target_offset An instance number for a distributed cell.
|
||||
//! @param root_univ The index of the root Universe in the global Universe
|
||||
//! array.
|
||||
//! @param[out] path The unique traversal through the geometry tree that leads
|
||||
//! to the desired instance of the target cell.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void
|
||||
distribcell_path(int32_t target_cell, int32_t map, int32_t target_offset,
|
||||
int32_t root_univ, char *path);
|
||||
|
||||
//==============================================================================
|
||||
//! Determine the maximum number of nested coordinate levels in the geometry.
|
||||
//! @param univ The index of the universe to begin seraching from (probably the
|
||||
//! root universe).
|
||||
//! @return The number of coordinate levels.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" int maximum_levels(int32_t univ);
|
||||
|
||||
//==============================================================================
|
||||
//! Deallocates global vectors and maps for cells, universes, and lattices.
|
||||
//==============================================================================
|
||||
|
||||
extern "C" void free_memory_geometry_c();
|
||||
|
||||
} // namespace openmc
|
||||
#endif // GEOMETRY_AUX_H
|
||||
|
|
@ -6,6 +6,7 @@ module geometry_header
|
|||
use constants, only: HALF, TWO, THREE, INFINITY, K_BOLTZMANN, &
|
||||
MATERIAL_VOID, NONE
|
||||
use dict_header, only: DictCharInt, DictIntInt
|
||||
use hdf5_interface, only: HID_T
|
||||
use material_header, only: Material, materials, material_dict, n_materials
|
||||
use nuclide_header
|
||||
use sab_header
|
||||
|
|
@ -14,13 +15,192 @@ module geometry_header
|
|||
|
||||
implicit none
|
||||
|
||||
interface
|
||||
function cell_pointer_c(cell_ind) bind(C, name='cell_pointer') result(ptr)
|
||||
import C_PTR, C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: cell_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function cell_pointer_c
|
||||
|
||||
function cell_id_c(cell_ptr) bind(C, name='cell_id') result(id)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T) :: id
|
||||
end function cell_id_c
|
||||
|
||||
subroutine cell_set_id_c(cell_ptr, id) bind(C, name='cell_set_id')
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T), intent(in), value :: id
|
||||
end subroutine cell_set_id_c
|
||||
|
||||
function cell_type_c(cell_ptr) bind(C, name='cell_type') result(type)
|
||||
import C_PTR, C_INT
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT) :: type
|
||||
end function cell_type_c
|
||||
|
||||
subroutine cell_set_type_c(cell_ptr, type) bind(C, name='cell_set_type')
|
||||
import C_PTR, C_INT
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT), intent(in), value :: type
|
||||
end subroutine cell_set_type_c
|
||||
|
||||
function cell_universe_c(cell_ptr) bind(C, name='cell_universe') &
|
||||
result(universe)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T) :: universe
|
||||
end function cell_universe_c
|
||||
|
||||
subroutine cell_set_universe_c(cell_ptr, universe) &
|
||||
bind(C, name='cell_set_universe')
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T), intent(in), value :: universe
|
||||
end subroutine cell_set_universe_c
|
||||
|
||||
function cell_fill_c(cell_ptr) bind(C, name="cell_fill") result(fill)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T) :: fill
|
||||
end function cell_fill_c
|
||||
|
||||
function cell_fill_ptr(cell_ptr) bind(C) result(fill_ptr)
|
||||
import C_PTR
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
type(C_PTR) :: fill_ptr
|
||||
end function cell_fill_ptr
|
||||
|
||||
function cell_n_instances_c(cell_ptr) bind(C, name='cell_n_instances') &
|
||||
result(n_instances)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT32_T) :: n_instances
|
||||
end function cell_n_instances_c
|
||||
|
||||
function cell_simple_c(cell_ptr) bind(C, name='cell_simple') result(simple)
|
||||
import C_PTR, C_BOOL
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
logical(C_BOOL) :: simple
|
||||
end function cell_simple_c
|
||||
|
||||
subroutine cell_distance_c(cell_ptr, xyz, uvw, on_surface, min_dist, &
|
||||
i_surf) bind(C, name="cell_distance")
|
||||
import C_PTR, C_INT32_T, C_DOUBLE
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
integer(C_INT32_T), intent(in), value :: on_surface
|
||||
real(C_DOUBLE), intent(out) :: min_dist
|
||||
integer(C_INT32_T), intent(out) :: i_surf
|
||||
end subroutine cell_distance_c
|
||||
|
||||
function cell_offset_c(cell_ptr, map) bind(C, name="cell_offset") &
|
||||
result(offset)
|
||||
import C_PTR, C_INT, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(C_INT), intent(in), value :: map
|
||||
integer(C_INT32_T) :: offset
|
||||
end function cell_offset_c
|
||||
|
||||
subroutine cell_to_hdf5_c(cell_ptr, group) bind(C, name='cell_to_hdf5')
|
||||
import HID_T, C_PTR
|
||||
type(C_PTR), intent(in), value :: cell_ptr
|
||||
integer(HID_T), intent(in), value :: group
|
||||
end subroutine cell_to_hdf5_c
|
||||
|
||||
function lattice_pointer_c(lat_ind) bind(C, name='lattice_pointer') &
|
||||
result(ptr)
|
||||
import C_PTR, C_INT32_T
|
||||
integer(C_INT32_T), intent(in), value :: lat_ind
|
||||
type(C_PTR) :: ptr
|
||||
end function lattice_pointer_c
|
||||
|
||||
function lattice_id_c(lat_ptr) bind(C, name='lattice_id') result(id)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(C_INT32_T) :: id
|
||||
end function lattice_id_c
|
||||
|
||||
function lattice_are_valid_indices_c(lat_ptr, i_xyz) &
|
||||
bind(C, name='lattice_are_valid_indices') result (is_valid)
|
||||
import C_PTR, C_INT, C_BOOL
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
logical(C_BOOL) :: is_valid
|
||||
end function lattice_are_valid_indices_c
|
||||
|
||||
subroutine lattice_distance_c(lat_ptr, xyz, uvw, i_xyz, d, lattice_trans) &
|
||||
bind(C, name='lattice_distance')
|
||||
import C_PTR, C_INT, C_DOUBLE
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
real(C_DOUBLE), intent(out) :: d
|
||||
integer(C_INT), intent(out) :: lattice_trans(3)
|
||||
end subroutine lattice_distance_c
|
||||
|
||||
subroutine lattice_get_indices_c(lat_ptr, xyz, i_xyz) &
|
||||
bind(C, name='lattice_get_indices')
|
||||
import C_PTR, C_INT, C_DOUBLE
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
integer(C_INT), intent(out) :: i_xyz(3)
|
||||
end subroutine lattice_get_indices_c
|
||||
|
||||
subroutine lattice_get_local_xyz_c(lat_ptr, global_xyz, i_xyz, local_xyz) &
|
||||
bind(C, name='lattice_get_local_xyz')
|
||||
import C_PTR, C_INT, C_DOUBLE
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
real(C_DOUBLE), intent(in) :: global_xyz(3)
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
real(C_DOUBLE), intent(out) :: local_xyz(3)
|
||||
end subroutine lattice_get_local_xyz_c
|
||||
|
||||
subroutine lattice_to_hdf5_c(lat_ptr, group) bind(C, name='lattice_to_hdf5')
|
||||
import HID_T, C_PTR
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(HID_T), intent(in), value :: group
|
||||
end subroutine lattice_to_hdf5_c
|
||||
|
||||
function lattice_offset_c(lat_ptr, map, i_xyz) &
|
||||
bind(C, name='lattice_offset') result(offset)
|
||||
import C_PTR, C_INT, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(C_INT), intent(in), value :: map
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
integer(C_INT32_T) :: offset
|
||||
end function lattice_offset_c
|
||||
|
||||
function lattice_outer_c(lat_ptr) bind(C, name='lattice_outer') &
|
||||
result(outer)
|
||||
import C_PTR, C_INT32_T
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(C_INT32_T) :: outer
|
||||
end function lattice_outer_c
|
||||
|
||||
function lattice_universe_c(lat_ptr, i_xyz) &
|
||||
bind(C, name='lattice_universe') result(univ)
|
||||
import C_PTR, C_INT32_t, C_INT
|
||||
type(C_PTR), intent(in), value :: lat_ptr
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
integer(C_INT32_T) :: univ
|
||||
end function lattice_universe_c
|
||||
|
||||
subroutine extend_cells_c(n) bind(C)
|
||||
import C_INT32_t
|
||||
integer(C_INT32_T), intent(in), value :: n
|
||||
end subroutine extend_cells_c
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! UNIVERSE defines a geometry that fills all phase space
|
||||
!===============================================================================
|
||||
|
||||
type Universe
|
||||
integer :: id ! Unique ID
|
||||
integer :: type ! Type
|
||||
integer, allocatable :: cells(:) ! List of cells within
|
||||
real(8) :: x0 ! Translation in x-coordinate
|
||||
real(8) :: y0 ! Translation in y-coordinate
|
||||
|
|
@ -32,68 +212,24 @@ module geometry_header
|
|||
!===============================================================================
|
||||
|
||||
type, abstract :: Lattice
|
||||
integer :: id ! Universe number for lattice
|
||||
character(len=104) :: name = "" ! User-defined name
|
||||
real(8), allocatable :: pitch(:) ! Pitch along each axis
|
||||
integer, allocatable :: universes(:,:,:) ! Specified universes
|
||||
integer :: outside ! Material to fill area outside
|
||||
integer :: outer ! universe to tile outside the lat
|
||||
logical :: is_3d ! Lattice has cells on z axis
|
||||
integer, allocatable :: offset(:,:,:,:) ! Distribcell offsets
|
||||
type(C_PTR) :: ptr
|
||||
contains
|
||||
procedure(lattice_are_valid_indices_), deferred :: are_valid_indices
|
||||
procedure(lattice_get_indices_), deferred :: get_indices
|
||||
procedure(lattice_get_local_xyz_), deferred :: get_local_xyz
|
||||
procedure :: id => lattice_id
|
||||
procedure :: are_valid_indices => lattice_are_valid_indices
|
||||
procedure :: distance => lattice_distance
|
||||
procedure :: get => lattice_get
|
||||
procedure :: get_indices => lattice_get_indices
|
||||
procedure :: get_local_xyz => lattice_get_local_xyz
|
||||
procedure :: offset => lattice_offset
|
||||
procedure :: outer => lattice_outer
|
||||
procedure :: to_hdf5 => lattice_to_hdf5
|
||||
end type Lattice
|
||||
|
||||
abstract interface
|
||||
|
||||
!===============================================================================
|
||||
! ARE_VALID_INDICES returns .true. if the given lattice indices fit within the
|
||||
! bounds of the lattice. Returns false otherwise.
|
||||
|
||||
function lattice_are_valid_indices_(this, i_xyz) result(is_valid)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
logical :: is_valid
|
||||
end function lattice_are_valid_indices_
|
||||
|
||||
!===============================================================================
|
||||
! GET_INDICES returns the indices in a lattice for the given global xyz.
|
||||
|
||||
function lattice_get_indices_(this, global_xyz) result(i_xyz)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer :: i_xyz(3)
|
||||
end function lattice_get_indices_
|
||||
|
||||
!===============================================================================
|
||||
! GET_LOCAL_XYZ returns the translated local version of the given global xyz.
|
||||
|
||||
function lattice_get_local_xyz_(this, global_xyz, i_xyz) result(local_xyz)
|
||||
import Lattice
|
||||
class(Lattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
real(8) :: local_xyz(3)
|
||||
end function lattice_get_local_xyz_
|
||||
end interface
|
||||
|
||||
!===============================================================================
|
||||
! RECTLATTICE extends LATTICE for rectilinear arrays.
|
||||
!===============================================================================
|
||||
|
||||
type, extends(Lattice) :: RectLattice
|
||||
integer :: n_cells(3) ! Number of cells along each axis
|
||||
real(8), allocatable :: lower_left(:) ! Global lower-left corner of lat
|
||||
|
||||
contains
|
||||
|
||||
procedure :: are_valid_indices => valid_inds_rect
|
||||
procedure :: get_indices => get_inds_rect
|
||||
procedure :: get_local_xyz => get_local_rect
|
||||
end type RectLattice
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -101,15 +237,6 @@ module geometry_header
|
|||
!===============================================================================
|
||||
|
||||
type, extends(Lattice) :: HexLattice
|
||||
integer :: n_rings ! Number of radial ring cell positoins
|
||||
integer :: n_axial ! Number of axial cell positions
|
||||
real(8), allocatable :: center(:) ! Global center of lattice
|
||||
|
||||
contains
|
||||
|
||||
procedure :: are_valid_indices => valid_inds_hex
|
||||
procedure :: get_indices => get_inds_hex
|
||||
procedure :: get_local_xyz => get_local_hex
|
||||
end type HexLattice
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -125,25 +252,13 @@ module geometry_header
|
|||
!===============================================================================
|
||||
|
||||
type Cell
|
||||
integer :: id ! Unique ID
|
||||
character(len=104) :: name = "" ! User-defined name
|
||||
integer :: type ! Type of cell (normal, universe,
|
||||
! lattice)
|
||||
integer :: universe ! universe # this cell is in
|
||||
integer :: fill ! universe # filling this cell
|
||||
integer :: instances ! number of instances of this cell in
|
||||
! the geom
|
||||
type(C_PTR) :: ptr
|
||||
|
||||
integer, allocatable :: material(:) ! Material within cell. Multiple
|
||||
! materials for distribcell
|
||||
! instances. 0 signifies a universe
|
||||
integer, allocatable :: offset(:) ! Distribcell offset for tally
|
||||
! counter
|
||||
integer, allocatable :: region(:) ! Definition of spatial region as
|
||||
! Boolean expression of half-spaces
|
||||
integer, allocatable :: rpn(:) ! Reverse Polish notation for region
|
||||
! expression
|
||||
logical :: simple ! Is the region simple (intersections
|
||||
! only)
|
||||
integer :: distribcell_index ! Index corresponding to this cell in
|
||||
! distribcell arrays
|
||||
real(8), allocatable :: sqrtkT(:) ! Square root of k_Boltzmann *
|
||||
|
|
@ -154,6 +269,22 @@ module geometry_header
|
|||
real(8), allocatable :: translation(:)
|
||||
real(8), allocatable :: rotation(:)
|
||||
real(8), allocatable :: rotation_matrix(:,:)
|
||||
|
||||
contains
|
||||
|
||||
procedure :: id => cell_id
|
||||
procedure :: set_id => cell_set_id
|
||||
procedure :: type => cell_type
|
||||
procedure :: set_type => cell_set_type
|
||||
procedure :: universe => cell_universe
|
||||
procedure :: set_universe => cell_set_universe
|
||||
procedure :: fill => cell_fill
|
||||
procedure :: n_instances => cell_n_instances
|
||||
procedure :: simple => cell_simple
|
||||
procedure :: distance => cell_distance
|
||||
procedure :: offset => cell_offset
|
||||
procedure :: to_hdf5 => cell_to_hdf5
|
||||
|
||||
end type Cell
|
||||
|
||||
! array index of the root universe
|
||||
|
|
@ -161,7 +292,6 @@ module geometry_header
|
|||
|
||||
integer(C_INT32_T), bind(C) :: n_cells ! # of cells
|
||||
integer(C_INT32_T), bind(C) :: n_universes ! # of universes
|
||||
integer(C_INT32_T), bind(C) :: n_lattices ! # of lattices
|
||||
|
||||
type(Cell), allocatable, target :: cells(:)
|
||||
type(Universe), allocatable, target :: universes(:)
|
||||
|
|
@ -174,172 +304,150 @@ module geometry_header
|
|||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
function lattice_id(this) result(id)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(C_INT32_T) :: id
|
||||
id = lattice_id_c(this % ptr)
|
||||
end function lattice_id
|
||||
|
||||
function valid_inds_rect(this, i_xyz) result(is_valid)
|
||||
class(RectLattice), intent(in) :: this
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
logical :: is_valid
|
||||
function lattice_are_valid_indices(this, i_xyz) result (is_valid)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
logical(C_BOOL) :: is_valid
|
||||
is_valid = lattice_are_valid_indices_c(this % ptr, i_xyz)
|
||||
end function lattice_are_valid_indices
|
||||
|
||||
is_valid = all(i_xyz > 0 .and. i_xyz <= this % n_cells)
|
||||
end function valid_inds_rect
|
||||
subroutine lattice_distance(this, xyz, uvw, i_xyz, d, lattice_trans)
|
||||
class(Lattice), intent(in) :: this
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
real(C_DOUBLE), intent(out) :: d
|
||||
integer(C_INT), intent(out) :: lattice_trans(3)
|
||||
call lattice_distance_c(this % ptr, xyz, uvw, i_xyz, d, lattice_trans)
|
||||
end subroutine lattice_distance
|
||||
|
||||
function lattice_get(this, i_xyz) result(univ)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
integer(C_INT32_T) :: univ
|
||||
univ = lattice_universe_c(this % ptr, i_xyz)
|
||||
end function lattice_get
|
||||
|
||||
function lattice_get_indices(this, xyz) result(i_xyz)
|
||||
class(Lattice), intent(in) :: this
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
integer(C_INT) :: i_xyz(3)
|
||||
call lattice_get_indices_c(this % ptr, xyz, i_xyz)
|
||||
end function lattice_get_indices
|
||||
|
||||
function lattice_get_local_xyz(this, global_xyz, i_xyz) &
|
||||
result(local_xyz)
|
||||
class(Lattice), intent(in) :: this
|
||||
real(C_DOUBLE), intent(in) :: global_xyz(3)
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
real(C_DOUBLE) :: local_xyz(3)
|
||||
call lattice_get_local_xyz_c(this % ptr, global_xyz, i_xyz, local_xyz)
|
||||
end function lattice_get_local_xyz
|
||||
|
||||
function lattice_offset(this, map, i_xyz) result(offset)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(C_INT), intent(in) :: map
|
||||
integer(C_INT), intent(in) :: i_xyz(3)
|
||||
integer(C_INT32_T) :: offset
|
||||
offset = lattice_offset_c(this % ptr, map, i_xyz)
|
||||
end function lattice_offset
|
||||
|
||||
function lattice_outer(this) result(outer)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(C_INT32_T) :: outer
|
||||
outer = lattice_outer_c(this % ptr)
|
||||
end function lattice_outer
|
||||
|
||||
subroutine lattice_to_hdf5(this, group)
|
||||
class(Lattice), intent(in) :: this
|
||||
integer(HID_T), intent(in) :: group
|
||||
call lattice_to_hdf5_c(this % ptr, group)
|
||||
end subroutine lattice_to_hdf5
|
||||
|
||||
!===============================================================================
|
||||
|
||||
function valid_inds_hex(this, i_xyz) result(is_valid)
|
||||
class(HexLattice), intent(in) :: this
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
logical :: is_valid
|
||||
function cell_id(this) result(id)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T) :: id
|
||||
id = cell_id_c(this % ptr)
|
||||
end function cell_id
|
||||
|
||||
is_valid = (all(i_xyz > 0) .and. &
|
||||
&i_xyz(1) < 2*this % n_rings .and. &
|
||||
&i_xyz(2) < 2*this % n_rings .and. &
|
||||
&i_xyz(1) + i_xyz(2) > this % n_rings .and. &
|
||||
&i_xyz(1) + i_xyz(2) < 3*this % n_rings .and. &
|
||||
&i_xyz(3) <= this % n_axial)
|
||||
end function valid_inds_hex
|
||||
subroutine cell_set_id(this, id)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T), intent(in) :: id
|
||||
call cell_set_id_c(this % ptr, id)
|
||||
end subroutine cell_set_id
|
||||
|
||||
!===============================================================================
|
||||
function cell_type(this) result(type)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT) :: type
|
||||
type = cell_type_c(this % ptr)
|
||||
end function cell_type
|
||||
|
||||
function get_inds_rect(this, global_xyz) result(i_xyz)
|
||||
class(RectLattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer :: i_xyz(3)
|
||||
subroutine cell_set_type(this, type)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT), intent(in) :: type
|
||||
call cell_set_type_c(this % ptr, type)
|
||||
end subroutine cell_set_type
|
||||
|
||||
real(8) :: xyz(3) ! global_xyz alias
|
||||
function cell_universe(this) result(universe)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T) :: universe
|
||||
universe = cell_universe_c(this % ptr)
|
||||
end function cell_universe
|
||||
|
||||
xyz = global_xyz
|
||||
subroutine cell_set_universe(this, universe)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T), intent(in) :: universe
|
||||
call cell_set_universe_c(this % ptr, universe)
|
||||
end subroutine cell_set_universe
|
||||
|
||||
i_xyz(1) = ceiling((xyz(1) - this % lower_left(1))/this % pitch(1))
|
||||
i_xyz(2) = ceiling((xyz(2) - this % lower_left(2))/this % pitch(2))
|
||||
if (this % is_3d) then
|
||||
i_xyz(3) = ceiling((xyz(3) - this % lower_left(3))/this % pitch(3))
|
||||
else
|
||||
i_xyz(3) = 1
|
||||
end if
|
||||
end function get_inds_rect
|
||||
function cell_fill(this) result(fill)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T) :: fill
|
||||
fill = cell_fill_c(this % ptr)
|
||||
end function cell_fill
|
||||
|
||||
!===============================================================================
|
||||
function cell_n_instances(this) result(n_instances)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT32_T) :: n_instances
|
||||
n_instances = cell_n_instances_c(this % ptr)
|
||||
end function cell_n_instances
|
||||
|
||||
function get_inds_hex(this, global_xyz) result(i_xyz)
|
||||
class(HexLattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer :: i_xyz(3)
|
||||
function cell_simple(this) result(simple)
|
||||
class(Cell), intent(in) :: this
|
||||
logical(C_BOOL) :: simple
|
||||
simple = cell_simple_c(this % ptr)
|
||||
end function cell_simple
|
||||
|
||||
real(8) :: xyz(3) ! global xyz relative to the center
|
||||
real(8) :: alpha ! Skewed coord axis
|
||||
real(8) :: xyz_t(3) ! Local xyz
|
||||
real(8) :: d, d_min ! Squared distance from cell centers
|
||||
integer :: i, j, k ! Iterators
|
||||
integer :: k_min ! Minimum distance index
|
||||
subroutine cell_distance(this, xyz, uvw, on_surface, min_dist, i_surf)
|
||||
class(Cell), intent(in) :: this
|
||||
real(C_DOUBLE), intent(in) :: xyz(3)
|
||||
real(C_DOUBLE), intent(in) :: uvw(3)
|
||||
integer(C_INT32_T), intent(in) :: on_surface
|
||||
real(C_DOUBLE), intent(out) :: min_dist
|
||||
integer(C_INT32_T), intent(out) :: i_surf
|
||||
call cell_distance_c(this % ptr, xyz, uvw, on_surface, min_dist, i_surf)
|
||||
end subroutine cell_distance
|
||||
|
||||
xyz(1) = global_xyz(1) - this % center(1)
|
||||
xyz(2) = global_xyz(2) - this % center(2)
|
||||
function cell_offset(this, map) result(offset)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(C_INT), intent(in) :: map
|
||||
integer(C_INT32_T) :: offset
|
||||
offset = cell_offset_c(this % ptr, map)
|
||||
end function cell_offset
|
||||
|
||||
! Index z direction.
|
||||
if (this % is_3d) then
|
||||
xyz(3) = global_xyz(3) - this % center(3)
|
||||
i_xyz(3) = ceiling(xyz(3)/this % pitch(2) + HALF*this % n_axial)
|
||||
else
|
||||
xyz(3) = global_xyz(3)
|
||||
i_xyz(3) = 1
|
||||
end if
|
||||
|
||||
! Convert coordinates into skewed bases. The (x, alpha) basis is used to
|
||||
! find the index of the global coordinates to within 4 cells.
|
||||
alpha = xyz(2) - xyz(1) / sqrt(THREE)
|
||||
i_xyz(1) = floor(xyz(1) / (sqrt(THREE) / TWO * this % pitch(1)))
|
||||
i_xyz(2) = floor(alpha / this % pitch(1))
|
||||
|
||||
! Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but
|
||||
! the array is offset so that the indices never go below 1).
|
||||
i_xyz(1) = i_xyz(1) + this % n_rings
|
||||
i_xyz(2) = i_xyz(2) + this % n_rings
|
||||
|
||||
! Calculate the (squared) distance between the particle and the centers of
|
||||
! the four possible cells. Regular hexagonal tiles form a centroidal
|
||||
! Voronoi tessellation so the global xyz should be in the hexagonal cell
|
||||
! that it is closest to the center of. This method is used over a
|
||||
! method that uses the remainders of the floor divisions above because it
|
||||
! provides better finite precision performance. Squared distances are
|
||||
! used becasue they are more computationally efficient than normal
|
||||
! distances.
|
||||
k = 1
|
||||
d_min = INFINITY
|
||||
do i = 0, 1
|
||||
do j = 0, 1
|
||||
xyz_t = this % get_local_xyz(global_xyz, i_xyz + [j, i, 0])
|
||||
d = xyz_t(1)**2 + xyz_t(2)**2
|
||||
if (d < d_min) then
|
||||
d_min = d
|
||||
k_min = k
|
||||
end if
|
||||
k = k + 1
|
||||
end do
|
||||
end do
|
||||
|
||||
! Select the minimum squared distance which corresponds to the cell the
|
||||
! coordinates are in.
|
||||
if (k_min == 2) then
|
||||
i_xyz(1) = i_xyz(1) + 1
|
||||
else if (k_min == 3) then
|
||||
i_xyz(2) = i_xyz(2) + 1
|
||||
else if (k_min == 4) then
|
||||
i_xyz(1) = i_xyz(1) + 1
|
||||
i_xyz(2) = i_xyz(2) + 1
|
||||
end if
|
||||
end function get_inds_hex
|
||||
|
||||
!===============================================================================
|
||||
|
||||
function get_local_rect(this, global_xyz, i_xyz) result(local_xyz)
|
||||
class(RectLattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
real(8) :: local_xyz(3)
|
||||
|
||||
real(8) :: xyz(3) ! global_xyz alias
|
||||
|
||||
xyz = global_xyz
|
||||
|
||||
local_xyz(1) = xyz(1) - (this % lower_left(1) + &
|
||||
(i_xyz(1) - HALF)*this % pitch(1))
|
||||
local_xyz(2) = xyz(2) - (this % lower_left(2) + &
|
||||
(i_xyz(2) - HALF)*this % pitch(2))
|
||||
if (this % is_3d) then
|
||||
local_xyz(3) = xyz(3) - (this % lower_left(3) + &
|
||||
(i_xyz(3) - HALF)*this % pitch(3))
|
||||
else
|
||||
local_xyz(3) = xyz(3)
|
||||
end if
|
||||
end function get_local_rect
|
||||
|
||||
!===============================================================================
|
||||
|
||||
function get_local_hex(this, global_xyz, i_xyz) result(local_xyz)
|
||||
class(HexLattice), intent(in) :: this
|
||||
real(8), intent(in) :: global_xyz(3)
|
||||
integer, intent(in) :: i_xyz(3)
|
||||
real(8) :: local_xyz(3)
|
||||
|
||||
real(8) :: xyz(3) ! global_xyz alias
|
||||
|
||||
xyz = global_xyz
|
||||
|
||||
! x_l = x_g - (center + pitch_x*cos(30)*index_x)
|
||||
local_xyz(1) = xyz(1) - (this % center(1) + &
|
||||
sqrt(THREE) / TWO * (i_xyz(1) - this % n_rings) * this % pitch(1))
|
||||
! y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y)
|
||||
local_xyz(2) = xyz(2) - (this % center(2) + &
|
||||
(i_xyz(2) - this % n_rings) * this % pitch(1) + &
|
||||
(i_xyz(1) - this % n_rings) * this % pitch(1) / TWO)
|
||||
if (this % is_3d) then
|
||||
local_xyz(3) = xyz(3) - this % center(3) &
|
||||
+ (HALF*this % n_axial - i_xyz(3) + HALF) * this % pitch(2)
|
||||
else
|
||||
local_xyz(3) = xyz(3)
|
||||
end if
|
||||
end function get_local_hex
|
||||
subroutine cell_to_hdf5(this, group)
|
||||
class(Cell), intent(in) :: this
|
||||
integer(HID_T), intent(in) :: group
|
||||
call cell_to_hdf5_c(this % ptr, group)
|
||||
end subroutine cell_to_hdf5
|
||||
|
||||
!===============================================================================
|
||||
! GET_TEMPERATURES returns a list of temperatures that each nuclide/S(a,b) table
|
||||
|
|
@ -408,10 +516,15 @@ contains
|
|||
!===============================================================================
|
||||
|
||||
subroutine free_memory_geometry()
|
||||
interface
|
||||
subroutine free_memory_geometry_c() bind(C)
|
||||
end subroutine free_memory_geometry_c
|
||||
end interface
|
||||
|
||||
call free_memory_geometry_c()
|
||||
|
||||
n_cells = 0
|
||||
n_universes = 0
|
||||
n_lattices = 0
|
||||
|
||||
if (allocated(cells)) deallocate(cells)
|
||||
if (allocated(universes)) deallocate(universes)
|
||||
|
|
@ -432,6 +545,7 @@ contains
|
|||
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_INT32_T) :: i
|
||||
integer(C_INT) :: err
|
||||
|
||||
type(Cell), allocatable :: temp(:) ! temporary cells array
|
||||
|
|
@ -453,7 +567,12 @@ contains
|
|||
! Return indices in cells array
|
||||
if (present(index_start)) index_start = n_cells + 1
|
||||
if (present(index_end)) index_end = n_cells + n
|
||||
n_cells = n_cells + n
|
||||
|
||||
! Extend the C++ cells array and get pointers to the C++ objects
|
||||
call extend_cells_c(n)
|
||||
do i = n_cells - n, n_cells
|
||||
cells(i) % ptr = cell_pointer_c(i - 1)
|
||||
end do
|
||||
|
||||
err = 0
|
||||
end function openmc_extend_cells
|
||||
|
|
@ -490,14 +609,14 @@ contains
|
|||
err = 0
|
||||
if (index >= 1 .and. index <= size(cells)) then
|
||||
associate (c => cells(index))
|
||||
type = c % type
|
||||
type = c % type()
|
||||
select case (type)
|
||||
case (FILL_MATERIAL)
|
||||
n = size(c % material)
|
||||
indices = C_LOC(c % material(1))
|
||||
case (FILL_UNIVERSE, FILL_LATTICE)
|
||||
n = 1
|
||||
indices = C_LOC(c % fill)
|
||||
indices = cell_fill_ptr(c % ptr)
|
||||
end select
|
||||
end associate
|
||||
else
|
||||
|
|
@ -514,7 +633,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= size(cells)) then
|
||||
id = cells(index) % id
|
||||
id = cells(index) % id()
|
||||
err = 0
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
|
|
@ -541,25 +660,21 @@ contains
|
|||
if (allocated(c % material)) deallocate(c % material)
|
||||
allocate(c % material(n))
|
||||
|
||||
c % type = FILL_MATERIAL
|
||||
call c % set_type(FILL_MATERIAL)
|
||||
do i = 1, n
|
||||
j = indices(i)
|
||||
if (j == 0) then
|
||||
c % material(i) = MATERIAL_VOID
|
||||
if ((j >= 1 .and. j <= n_materials) .or. j == MATERIAL_VOID) then
|
||||
c % material(i) = j
|
||||
else
|
||||
if (j >= 1 .and. j <= n_materials) then
|
||||
c % material(i) = j
|
||||
else
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index " // trim(to_str(j)) // " in the &
|
||||
&materials array is out of bounds.")
|
||||
end if
|
||||
err = E_OUT_OF_BOUNDS
|
||||
call set_errmsg("Index " // trim(to_str(j)) // " in the &
|
||||
&materials array is out of bounds.")
|
||||
end if
|
||||
end do
|
||||
case (FILL_UNIVERSE)
|
||||
c % type = FILL_UNIVERSE
|
||||
call c % set_type(FILL_UNIVERSE)
|
||||
case (FILL_LATTICE)
|
||||
c % type = FILL_LATTICE
|
||||
call c % set_type(FILL_LATTICE)
|
||||
end select
|
||||
end associate
|
||||
else
|
||||
|
|
@ -577,7 +692,7 @@ contains
|
|||
integer(C_INT) :: err
|
||||
|
||||
if (index >= 1 .and. index <= n_cells) then
|
||||
cells(index) % id = id
|
||||
call cells(index) % set_id(id)
|
||||
call cell_dict % set(id, index)
|
||||
err = 0
|
||||
else
|
||||
|
|
@ -613,7 +728,7 @@ contains
|
|||
if (index >= 1 .and. index <= size(cells)) then
|
||||
|
||||
! error if the cell is filled with another universe
|
||||
if (cells(index) % fill /= NONE) then
|
||||
if (cells(index) % fill() /= C_NONE) then
|
||||
err = E_GEOMETRY
|
||||
call set_errmsg("Cannot set temperature on a cell filled &
|
||||
&with a universe.")
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
658
src/hdf5_interface.cpp
Normal file
658
src/hdf5_interface.cpp
Normal file
|
|
@ -0,0 +1,658 @@
|
|||
#include "hdf5_interface.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
#ifdef OPENMC_MPI
|
||||
#include "mpi.h"
|
||||
#include "message_passing.h"
|
||||
#endif
|
||||
#include "error.h"
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
bool
|
||||
attribute_exists(hid_t obj_id, const char* name)
|
||||
{
|
||||
htri_t out = H5Aexists_by_name(obj_id, ".", name, H5P_DEFAULT);
|
||||
return out > 0;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
attribute_typesize(hid_t obj_id, const char* name)
|
||||
{
|
||||
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);
|
||||
hid_t filetype = H5Aget_type(attr);
|
||||
size_t n = H5Tget_size(filetype);
|
||||
H5Tclose(filetype);
|
||||
H5Aclose(attr);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
get_shape(hid_t obj_id, hsize_t* dims)
|
||||
{
|
||||
auto type = H5Iget_type(obj_id);
|
||||
hid_t dspace;
|
||||
if (type == H5I_DATASET) {
|
||||
dspace = H5Dget_space(obj_id);
|
||||
} else if (type == H5I_ATTR) {
|
||||
dspace = H5Aget_space(obj_id);
|
||||
}
|
||||
H5Sget_simple_extent_dims(dspace, dims, nullptr);
|
||||
H5Sclose(dspace);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims)
|
||||
{
|
||||
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);
|
||||
hid_t dspace = H5Aget_space(attr);
|
||||
H5Sget_simple_extent_dims(dspace, dims, nullptr);
|
||||
H5Sclose(dspace);
|
||||
H5Aclose(attr);
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
create_group(hid_t parent_id, char const *name)
|
||||
{
|
||||
hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
if (out < 0) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Failed to create HDF5 group \"" << name << "\"";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
create_group(hid_t parent_id, const std::string &name)
|
||||
{
|
||||
return create_group(parent_id, name.c_str());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
close_dataset(hid_t dataset_id)
|
||||
{
|
||||
if (H5Dclose(dataset_id) < 0) fatal_error("Failed to close dataset");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
close_group(hid_t group_id)
|
||||
{
|
||||
if (H5Gclose(group_id) < 0) fatal_error("Failed to close group");
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
dataset_ndims(hid_t dset)
|
||||
{
|
||||
hid_t dspace = H5Dget_space(dset);
|
||||
int ndims = H5Sget_simple_extent_ndims(dspace);
|
||||
H5Sclose(dspace);
|
||||
return ndims;
|
||||
}
|
||||
|
||||
|
||||
size_t
|
||||
dataset_typesize(hid_t dset)
|
||||
{
|
||||
hid_t filetype = H5Dget_type(dset);
|
||||
size_t n = H5Tget_size(filetype);
|
||||
H5Tclose(filetype);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
file_open(const char* filename, char mode, bool parallel)
|
||||
{
|
||||
bool create;
|
||||
unsigned int flags;
|
||||
switch (mode) {
|
||||
case 'r':
|
||||
case 'a':
|
||||
create = false;
|
||||
flags = (mode == 'r' ? H5F_ACC_RDONLY : H5F_ACC_RDWR);
|
||||
break;
|
||||
case 'w':
|
||||
case 'x':
|
||||
create = true;
|
||||
flags = (mode == 'x' ? H5F_ACC_EXCL : H5F_ACC_TRUNC);
|
||||
break;
|
||||
default:
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Invalid file mode: " << mode;
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
|
||||
hid_t plist = H5P_DEFAULT;
|
||||
#ifdef PHDF5
|
||||
if (parallel) {
|
||||
// Setup file access property list with parallel I/O access
|
||||
plist = H5Pcreate(H5P_FILE_ACCESS);
|
||||
H5Pset_fapl_mpio(plist, openmc::mpi::intracomm, MPI_INFO_NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Open the file collectively
|
||||
hid_t file_id;
|
||||
if (create) {
|
||||
file_id = H5Fcreate(filename, flags, H5P_DEFAULT, plist);
|
||||
} else {
|
||||
file_id = H5Fopen(filename, flags, plist);
|
||||
}
|
||||
if (file_id < 0) {
|
||||
std::stringstream msg;
|
||||
msg << "Failed to open HDF5 file with mode '" << mode << "': " << filename;
|
||||
fatal_error(msg);
|
||||
}
|
||||
|
||||
#ifdef PHDF5
|
||||
// Close the property list
|
||||
if (parallel) H5Pclose(plist);
|
||||
#endif
|
||||
|
||||
return file_id;
|
||||
}
|
||||
|
||||
hid_t
|
||||
file_open(const std::string& filename, char mode, bool parallel=false)
|
||||
{
|
||||
file_open(filename.c_str(), mode, parallel);
|
||||
}
|
||||
|
||||
void file_close(hid_t file_id)
|
||||
{
|
||||
H5Fclose(file_id);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
get_name(hid_t obj_id, char* name)
|
||||
{
|
||||
size_t size = 1 + H5Iget_name(obj_id, nullptr, 0);
|
||||
H5Iget_name(obj_id, name, size);
|
||||
}
|
||||
|
||||
|
||||
int get_num_datasets(hid_t group_id)
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get number of groups
|
||||
H5O_info_t oinfo;
|
||||
int ndatasets = 0;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type == H5O_TYPE_DATASET) ndatasets += 1;
|
||||
}
|
||||
|
||||
return ndatasets;
|
||||
}
|
||||
|
||||
|
||||
int get_num_groups(hid_t group_id)
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get number of groups
|
||||
H5O_info_t oinfo;
|
||||
int ngroups = 0;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type == H5O_TYPE_GROUP) ngroups += 1;
|
||||
}
|
||||
|
||||
return ngroups;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
get_datasets(hid_t group_id, char* name[])
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get names
|
||||
H5O_info_t oinfo;
|
||||
hsize_t count = 0;
|
||||
size_t size;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type != H5O_TYPE_DATASET) continue;
|
||||
|
||||
// Get size of name
|
||||
size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC,
|
||||
i, nullptr, 0, H5P_DEFAULT);
|
||||
|
||||
// Read name
|
||||
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
|
||||
name[count], size, H5P_DEFAULT);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
get_groups(hid_t group_id, char* name[])
|
||||
{
|
||||
// Determine number of links in the group
|
||||
H5G_info_t info;
|
||||
H5Gget_info(group_id, &info);
|
||||
|
||||
// Iterate over links to get names
|
||||
H5O_info_t oinfo;
|
||||
hsize_t count = 0;
|
||||
size_t size;
|
||||
for (hsize_t i = 0; i < info.nlinks; ++i) {
|
||||
// Determine type of object (and skip non-group)
|
||||
H5Oget_info_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, &oinfo,
|
||||
H5P_DEFAULT);
|
||||
if (oinfo.type != H5O_TYPE_GROUP) continue;
|
||||
|
||||
// Get size of name
|
||||
size = 1 + H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC,
|
||||
i, nullptr, 0, H5P_DEFAULT);
|
||||
|
||||
// Read name
|
||||
H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i,
|
||||
name[count], size, H5P_DEFAULT);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
object_exists(hid_t object_id, const char* name)
|
||||
{
|
||||
htri_t out = H5LTpath_valid(object_id, name, true);
|
||||
if (out < 0) {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Failed to check if object \"" << name << "\" exists.";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
return (out > 0);
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
open_dataset(hid_t group_id, const char* name)
|
||||
{
|
||||
if (object_exists(group_id, name)) {
|
||||
return H5Dopen(group_id, name, H5P_DEFAULT);
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
open_group(hid_t group_id, const char* name)
|
||||
{
|
||||
if (object_exists(group_id, name)) {
|
||||
return H5Gopen(group_id, name, H5P_DEFAULT);
|
||||
} else {
|
||||
std::stringstream err_msg;
|
||||
err_msg << "Group \"" << name << "\" does not exist";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
read_attr(hid_t obj_id, const char* name, hid_t mem_type_id, void* buffer)
|
||||
{
|
||||
hid_t attr = H5Aopen(obj_id, name, H5P_DEFAULT);
|
||||
H5Aread(attr, mem_type_id, buffer);
|
||||
H5Aclose(attr);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_attr_double(hid_t obj_id, const char* name, double* buffer)
|
||||
{
|
||||
read_attr(obj_id, name, H5T_NATIVE_DOUBLE, buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_attr_int(hid_t obj_id, const char* name, int* buffer)
|
||||
{
|
||||
read_attr(obj_id, name, H5T_NATIVE_INT, buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_attr_string(hid_t obj_id, const char* name, size_t slen, char* buffer)
|
||||
{
|
||||
// Create datatype for a string
|
||||
hid_t datatype = H5Tcopy(H5T_C_S1);
|
||||
H5Tset_size(datatype, slen + 1);
|
||||
|
||||
// Read data into buffer
|
||||
read_attr(obj_id, name, datatype, buffer);
|
||||
|
||||
// Free resources
|
||||
H5Tclose(datatype);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
void* buffer, bool indep)
|
||||
{
|
||||
hid_t dset = obj_id;
|
||||
if (name) dset = open_dataset(obj_id, name);
|
||||
|
||||
if (using_mpio_device(dset)) {
|
||||
#ifdef PHDF5
|
||||
// Set up collective vs independent I/O
|
||||
auto data_xfer_mode = indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE;
|
||||
|
||||
// Create dataset transfer property list
|
||||
hid_t plist = H5Pcreate(H5P_DATASET_XFER);
|
||||
H5Pset_dxpl_mpio(plist, data_xfer_mode);
|
||||
|
||||
// Read data
|
||||
H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer);
|
||||
H5Pclose(plist);
|
||||
#endif
|
||||
} else {
|
||||
H5Dread(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer);
|
||||
}
|
||||
|
||||
if (name) H5Dclose(dset);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_double(hid_t obj_id, const char* name, double* buffer, bool indep)
|
||||
{
|
||||
read_dataset(obj_id, name, H5T_NATIVE_DOUBLE, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_int(hid_t obj_id, const char* name, int* buffer, bool indep)
|
||||
{
|
||||
read_dataset(obj_id, name, H5T_NATIVE_INT, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_llong(hid_t obj_id, const char* name, long long* buffer, bool indep)
|
||||
{
|
||||
read_dataset(obj_id, name, H5T_NATIVE_LLONG, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_string(hid_t obj_id, const char* name, size_t slen, char* buffer, bool indep)
|
||||
{
|
||||
// Create datatype for a string
|
||||
hid_t datatype = H5Tcopy(H5T_C_S1);
|
||||
H5Tset_size(datatype, slen + 1);
|
||||
|
||||
// Read data into buffer
|
||||
read_dataset(obj_id, name, datatype, buffer, indep);
|
||||
|
||||
// Free resources
|
||||
H5Tclose(datatype);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_complex(hid_t obj_id, const char* name, double _Complex* buffer, bool indep)
|
||||
{
|
||||
// Create compound datatype for complex numbers
|
||||
struct complex_t {
|
||||
double re;
|
||||
double im;
|
||||
};
|
||||
complex_t tmp;
|
||||
hid_t complex_id = H5Tcreate(H5T_COMPOUND, sizeof tmp);
|
||||
H5Tinsert(complex_id, "r", HOFFSET(complex_t, re), H5T_NATIVE_DOUBLE);
|
||||
H5Tinsert(complex_id, "i", HOFFSET(complex_t, im), H5T_NATIVE_DOUBLE);
|
||||
|
||||
// Read data
|
||||
read_dataset(obj_id, name, complex_id, buffer, indep);
|
||||
|
||||
// Free resources
|
||||
H5Tclose(complex_id);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
read_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, double* results)
|
||||
{
|
||||
// Create dataspace for hyperslab in memory
|
||||
hsize_t dims[] {n_filter, n_score, 3};
|
||||
hsize_t start[] {0, 0, 1};
|
||||
hsize_t count[] {n_filter, n_score, 2};
|
||||
hid_t memspace = H5Screate_simple(3, dims, nullptr);
|
||||
H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr);
|
||||
|
||||
// Create and write dataset
|
||||
hid_t dset = H5Dopen(group_id, "results", H5P_DEFAULT);
|
||||
H5Dread(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results);
|
||||
|
||||
// Free resources
|
||||
H5Dclose(dset);
|
||||
H5Sclose(memspace);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer)
|
||||
{
|
||||
// If array is given, create a simple dataspace. Otherwise, create a scalar
|
||||
// datascape.
|
||||
hid_t dspace;
|
||||
if (ndim > 0) {
|
||||
dspace = H5Screate_simple(ndim, dims, nullptr);
|
||||
} else {
|
||||
dspace = H5Screate(H5S_SCALAR);
|
||||
}
|
||||
|
||||
// Create attribute and Write data
|
||||
hid_t attr = H5Acreate(obj_id, name, mem_type_id, dspace,
|
||||
H5P_DEFAULT, H5P_DEFAULT);
|
||||
H5Awrite(attr, mem_type_id, buffer);
|
||||
|
||||
// Free resources
|
||||
H5Aclose(attr);
|
||||
H5Sclose(dspace);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
const double* buffer)
|
||||
{
|
||||
write_attr(obj_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
const int* buffer)
|
||||
{
|
||||
write_attr(obj_id, ndim, dims, name, H5T_NATIVE_INT, buffer);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_attr_string(hid_t obj_id, const char* name, const char* buffer)
|
||||
{
|
||||
size_t n = strlen(buffer);
|
||||
if (n > 0) {
|
||||
// Set up appropriate datatype for a fixed-length string
|
||||
hid_t datatype = H5Tcopy(H5T_C_S1);
|
||||
H5Tset_size(datatype, n);
|
||||
|
||||
write_attr(obj_id, 0, nullptr, name, datatype, buffer);
|
||||
|
||||
// Free resources
|
||||
H5Tclose(datatype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer, bool indep)
|
||||
{
|
||||
// If array is given, create a simple dataspace. Otherwise, create a scalar
|
||||
// datascape.
|
||||
hid_t dspace;
|
||||
if (ndim > 0) {
|
||||
dspace = H5Screate_simple(ndim, dims, nullptr);
|
||||
} else {
|
||||
dspace = H5Screate(H5S_SCALAR);
|
||||
}
|
||||
|
||||
hid_t dset = H5Dcreate(group_id, name, mem_type_id, dspace,
|
||||
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
if (using_mpio_device(group_id)) {
|
||||
#ifdef PHDF5
|
||||
// Set up collective vs independent I/O
|
||||
auto data_xfer_mode = indep ? H5FD_MPIO_INDEPENDENT : H5FD_MPIO_COLLECTIVE;
|
||||
|
||||
// Create dataset transfer property list
|
||||
hid_t plist = H5Pcreate(H5P_DATASET_XFER);
|
||||
H5Pset_dxpl_mpio(plist, data_xfer_mode);
|
||||
|
||||
// Write data
|
||||
H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, plist, buffer);
|
||||
H5Pclose(plist);
|
||||
#endif
|
||||
} else {
|
||||
H5Dwrite(dset, mem_type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer);
|
||||
}
|
||||
|
||||
// Free resources
|
||||
H5Dclose(dset);
|
||||
H5Sclose(dspace);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_double(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
const double* buffer, bool indep)
|
||||
{
|
||||
write_dataset(group_id, ndim, dims, name, H5T_NATIVE_DOUBLE, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_int(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
const int* buffer, bool indep)
|
||||
{
|
||||
write_dataset(group_id, ndim, dims, name, H5T_NATIVE_INT, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_llong(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
const long long* buffer, bool indep)
|
||||
{
|
||||
write_dataset(group_id, ndim, dims, name, H5T_NATIVE_LLONG, buffer, indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen,
|
||||
const char* name, const char* buffer, bool indep)
|
||||
{
|
||||
if (slen > 0) {
|
||||
// Set up appropriate datatype for a fixed-length string
|
||||
hid_t datatype = H5Tcopy(H5T_C_S1);
|
||||
H5Tset_size(datatype, slen);
|
||||
|
||||
write_dataset(group_id, ndim, dims, name, datatype, buffer, indep);
|
||||
|
||||
// Free resources
|
||||
H5Tclose(datatype);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep)
|
||||
{
|
||||
write_string(group_id, 0, nullptr, buffer.length(), name, buffer.c_str(), indep);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score, const double* results)
|
||||
{
|
||||
// Set dimensions of sum/sum_sq hyperslab to store
|
||||
hsize_t count[] {n_filter, n_score, 2};
|
||||
hid_t dspace = H5Screate_simple(3, count, nullptr);
|
||||
|
||||
// Set dimensions of results array
|
||||
hsize_t dims[] {n_filter, n_score, 3};
|
||||
hsize_t start[] {0, 0, 1};
|
||||
hid_t memspace = H5Screate_simple(3, dims, nullptr);
|
||||
H5Sselect_hyperslab(memspace, H5S_SELECT_SET, start, nullptr, count, nullptr);
|
||||
|
||||
// Create and write dataset
|
||||
hid_t dset = H5Dcreate(group_id, "results", H5T_NATIVE_DOUBLE, dspace,
|
||||
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
H5Dwrite(dset, H5T_NATIVE_DOUBLE, memspace, H5S_ALL, H5P_DEFAULT, results);
|
||||
|
||||
// Free resources
|
||||
H5Dclose(dset);
|
||||
H5Sclose(memspace);
|
||||
H5Sclose(dspace);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
using_mpio_device(hid_t obj_id)
|
||||
{
|
||||
// Determine file that this object is part of
|
||||
hid_t file_id = H5Iget_file_id(obj_id);
|
||||
|
||||
// Get file access property list
|
||||
hid_t fapl_id = H5Fget_access_plist(file_id);
|
||||
|
||||
// Get low-level driver identifier
|
||||
hid_t driver = H5Pget_driver(fapl_id);
|
||||
|
||||
// Free resources
|
||||
H5Pclose(fapl_id);
|
||||
H5Fclose(file_id);
|
||||
|
||||
return driver == H5FD_MPIO;
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
@ -1,90 +1,105 @@
|
|||
#ifndef HDF5_INTERFACE_H
|
||||
#define HDF5_INTERFACE_H
|
||||
|
||||
#include <array>
|
||||
#include <string.h>
|
||||
|
||||
#include "hdf5.h"
|
||||
#include "hdf5_hl.h"
|
||||
|
||||
#include "error.h"
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <complex.h>
|
||||
|
||||
|
||||
namespace openmc {
|
||||
|
||||
extern "C" bool attribute_exists(hid_t obj_id, const char* name);
|
||||
extern "C" size_t attribute_typesize(hid_t obj_id, const char* name);
|
||||
extern "C" hid_t create_group(hid_t parent_id, const char* name);
|
||||
hid_t create_group(hid_t parent_id, const std::string& name);
|
||||
extern "C" void close_dataset(hid_t dataset_id);
|
||||
extern "C" void close_group(hid_t group_id);
|
||||
extern "C" int dataset_ndims(hid_t dset);
|
||||
extern "C" size_t dataset_typesize(hid_t dset);
|
||||
extern "C" hid_t file_open(const char* filename, char mode, bool parallel);
|
||||
hid_t file_open(const std::string& filename, char mode, bool parallel);
|
||||
extern "C" void file_close(hid_t file_id);
|
||||
extern "C" void get_name(hid_t obj_id, char* name);
|
||||
extern "C" int get_num_datasets(hid_t group_id);
|
||||
extern "C" int get_num_groups(hid_t group_id);
|
||||
extern "C" void get_datasets(hid_t group_id, char* name[]);
|
||||
extern "C" void get_groups(hid_t group_id, char* name[]);
|
||||
extern "C" void get_shape(hid_t obj_id, hsize_t* dims);
|
||||
extern "C" void get_shape_attr(hid_t obj_id, const char* name, hsize_t* dims);
|
||||
extern "C" bool object_exists(hid_t object_id, const char* name);
|
||||
extern "C" hid_t open_dataset(hid_t group_id, const char* name);
|
||||
extern "C" hid_t open_group(hid_t group_id, const char* name);
|
||||
bool using_mpio_device(hid_t obj_id);
|
||||
|
||||
hid_t
|
||||
create_group(hid_t parent_id, char const *name)
|
||||
void read_attr(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
const void* buffer);
|
||||
extern "C" void read_attr_double(hid_t obj_id, const char* name, double* buffer);
|
||||
extern "C" void read_attr_int(hid_t obj_id, const char* name, int* buffer);
|
||||
extern "C" void read_attr_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer);
|
||||
|
||||
void read_dataset(hid_t obj_id, const char* name, hid_t mem_type_id,
|
||||
void* buffer, bool indep);
|
||||
extern "C" void read_double(hid_t obj_id, const char* name, double* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_int(hid_t obj_id, const char* name, int* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_llong(hid_t obj_id, const char* name, long long* buffer,
|
||||
bool indep);
|
||||
extern "C" void read_string(hid_t obj_id, const char* name, size_t slen,
|
||||
char* buffer, bool indep);
|
||||
extern "C" void read_complex(hid_t obj_id, const char* name,
|
||||
double _Complex* buffer, bool indep);
|
||||
|
||||
extern "C" void read_tally_results(hid_t group_id, hsize_t n_filter,
|
||||
hsize_t n_score, double* results);
|
||||
|
||||
void write_attr(hid_t obj_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer);
|
||||
extern "C" void write_attr_double(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const double* buffer);
|
||||
extern "C" void write_attr_int(hid_t obj_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const int* buffer);
|
||||
extern "C" void write_attr_string(hid_t obj_id, const char* name, const char* buffer);
|
||||
|
||||
|
||||
void write_dataset(hid_t group_id, int ndim, const hsize_t* dims, const char* name,
|
||||
hid_t mem_type_id, const void* buffer, bool indep);
|
||||
extern "C" void write_double(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const double* buffer, bool indep);
|
||||
extern "C" void write_int(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const int* buffer, bool indep);
|
||||
extern "C" void write_llong(hid_t group_id, int ndim, const hsize_t* dims,
|
||||
const char* name, const long long* buffer, bool indep);
|
||||
|
||||
extern "C" void write_string(hid_t group_id, int ndim, const hsize_t* dims, size_t slen,
|
||||
const char* name, char const* buffer, bool indep);
|
||||
void write_string(hid_t group_id, const char* name, const std::string& buffer, bool indep);
|
||||
|
||||
|
||||
extern "C" void write_tally_results(hid_t group_id, hsize_t n_filter, hsize_t n_score,
|
||||
const double* results);
|
||||
|
||||
template<std::size_t array_len> void
|
||||
write_int(hid_t group_id, char const *name,
|
||||
const std::array<int, array_len> &buffer, bool indep)
|
||||
{
|
||||
hid_t out = H5Gcreate(parent_id, name, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
if (out < 0) {
|
||||
std::string err_msg{"Failed to create HDF5 group \""};
|
||||
err_msg += name;
|
||||
err_msg += "\"";
|
||||
fatal_error(err_msg);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
hid_t
|
||||
create_group(hid_t parent_id, const std::string &name)
|
||||
{
|
||||
return create_group(parent_id, name.c_str());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
close_group(hid_t group_id)
|
||||
{
|
||||
herr_t err = H5Gclose(group_id);
|
||||
if (err < 0) {
|
||||
fatal_error("Failed to close HDF5 group");
|
||||
}
|
||||
hsize_t dims[1] {array_len};
|
||||
write_dataset(group_id, 1, dims, name, H5T_NATIVE_INT, buffer.data(), indep);
|
||||
}
|
||||
|
||||
|
||||
template<std::size_t array_len> void
|
||||
write_double_1D(hid_t group_id, char const *name,
|
||||
std::array<double, array_len> &buffer)
|
||||
write_double(hid_t group_id, char const *name,
|
||||
const std::array<double, array_len> &buffer, bool indep)
|
||||
{
|
||||
hsize_t dims[1]{array_len};
|
||||
hid_t dataspace = H5Screate_simple(1, dims, NULL);
|
||||
|
||||
hid_t dataset = H5Dcreate(group_id, name, H5T_NATIVE_DOUBLE, dataspace,
|
||||
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT,
|
||||
&buffer[0]);
|
||||
|
||||
H5Sclose(dataspace);
|
||||
H5Dclose(dataset);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_string(hid_t group_id, char const *name, char const *buffer)
|
||||
{
|
||||
size_t buffer_len = strlen(buffer);
|
||||
hid_t datatype = H5Tcopy(H5T_C_S1);
|
||||
H5Tset_size(datatype, buffer_len);
|
||||
|
||||
hid_t dataspace = H5Screate(H5S_SCALAR);
|
||||
|
||||
hid_t dataset = H5Dcreate(group_id, name, datatype, dataspace,
|
||||
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
|
||||
|
||||
H5Dwrite(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer);
|
||||
|
||||
H5Tclose(datatype);
|
||||
H5Sclose(dataspace);
|
||||
H5Dclose(dataset);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
write_string(hid_t group_id, char const *name, const std::string &buffer)
|
||||
{
|
||||
write_string(group_id, name, buffer.c_str());
|
||||
hsize_t dims[1] {array_len};
|
||||
write_dataset(group_id, 1, dims, name, H5T_NATIVE_DOUBLE,
|
||||
buffer.data(), indep);
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
|
|
|
|||
|
|
@ -1,36 +1,27 @@
|
|||
module initialize
|
||||
|
||||
use, intrinsic :: ISO_C_BINDING, only: c_loc
|
||||
use, intrinsic :: ISO_C_BINDING
|
||||
|
||||
use hdf5
|
||||
#ifdef _OPENMP
|
||||
use omp_lib
|
||||
#endif
|
||||
|
||||
use bank_header, only: Bank
|
||||
use constants
|
||||
use set_header, only: SetInt
|
||||
use error, only: fatal_error, warning, write_message
|
||||
use geometry_header, only: Cell, Universe, Lattice, RectLattice, HexLattice
|
||||
use hdf5_interface, only: file_open, read_attribute, file_close, &
|
||||
hdf5_bank_t, hdf5_integer8_t
|
||||
use input_xml, only: read_input_xml
|
||||
use material_header, only: Material
|
||||
use message_passing
|
||||
use mgxs_data, only: read_mgxs, create_macro_xs
|
||||
use output, only: print_version, print_usage
|
||||
use random_lcg, only: openmc_set_seed
|
||||
use settings
|
||||
#ifdef _OPENMP
|
||||
use simulation_header, only: n_threads
|
||||
#endif
|
||||
use string, only: to_str, starts_with, ends_with, str_to_int
|
||||
use tally_header, only: TallyObject
|
||||
use tally_filter
|
||||
use string, only: ends_with, to_f_string
|
||||
use timer_header
|
||||
|
||||
implicit none
|
||||
|
||||
type(C_PTR), bind(C) :: openmc_path_input
|
||||
type(C_PTR), bind(C) :: openmc_path_statepoint
|
||||
type(C_PTR), bind(C) :: openmc_path_sourcepoint
|
||||
type(C_PTR), bind(C) :: openmc_path_particle_restart
|
||||
|
||||
contains
|
||||
|
||||
!===============================================================================
|
||||
|
|
@ -40,8 +31,9 @@ contains
|
|||
! setting up timers, etc.
|
||||
!===============================================================================
|
||||
|
||||
subroutine openmc_init(intracomm) bind(C)
|
||||
function openmc_init_f(intracomm) result(err) bind(C)
|
||||
integer, intent(in), optional :: intracomm ! MPI intracommunicator
|
||||
integer(C_INT) :: err
|
||||
|
||||
#ifdef _OPENMP
|
||||
character(MAX_WORD_LEN) :: envvar
|
||||
|
|
@ -85,9 +77,6 @@ contains
|
|||
end if
|
||||
#endif
|
||||
|
||||
! Initialize HDF5 interface
|
||||
call hdf5_initialize()
|
||||
|
||||
! Read command line arguments
|
||||
call read_command_line()
|
||||
|
||||
|
|
@ -104,7 +93,8 @@ contains
|
|||
! Stop initialization timer
|
||||
call time_initialize%stop()
|
||||
|
||||
end subroutine openmc_init
|
||||
err = 0
|
||||
end function openmc_init_f
|
||||
|
||||
#ifdef OPENMC_MPI
|
||||
!===============================================================================
|
||||
|
|
@ -121,34 +111,18 @@ contains
|
|||
#endif
|
||||
|
||||
integer :: mpi_err ! MPI error code
|
||||
integer :: bank_blocks(6) ! Count for each datatype
|
||||
integer :: bank_blocks(5) ! Count for each datatype
|
||||
#ifdef OPENMC_MPIF08
|
||||
type(MPI_Datatype) :: bank_types(6)
|
||||
type(MPI_Datatype) :: bank_types(5)
|
||||
#else
|
||||
integer :: bank_types(6) ! Datatypes
|
||||
integer :: bank_types(5) ! Datatypes
|
||||
#endif
|
||||
integer(MPI_ADDRESS_KIND) :: bank_disp(6) ! Displacements
|
||||
logical :: init_called
|
||||
integer(MPI_ADDRESS_KIND) :: bank_disp(5) ! Displacements
|
||||
type(Bank) :: b
|
||||
|
||||
! Indicate that MPI is turned on
|
||||
mpi_enabled = .true.
|
||||
|
||||
! Initialize MPI
|
||||
call MPI_INITIALIZED(init_called, mpi_err)
|
||||
if (.not. init_called) call MPI_INIT(mpi_err)
|
||||
|
||||
! Determine number of processors and rank of each processor
|
||||
mpi_intracomm = intracomm
|
||||
call MPI_COMM_SIZE(mpi_intracomm, n_procs, mpi_err)
|
||||
call MPI_COMM_RANK(mpi_intracomm, rank, mpi_err)
|
||||
|
||||
! Determine master
|
||||
if (rank == 0) then
|
||||
master = .true.
|
||||
else
|
||||
master = .false.
|
||||
end if
|
||||
|
||||
! ==========================================================================
|
||||
! CREATE MPI_BANK TYPE
|
||||
|
|
@ -159,221 +133,60 @@ contains
|
|||
call MPI_GET_ADDRESS(b % uvw, bank_disp(3), mpi_err)
|
||||
call MPI_GET_ADDRESS(b % E, bank_disp(4), mpi_err)
|
||||
call MPI_GET_ADDRESS(b % delayed_group, bank_disp(5), mpi_err)
|
||||
call MPI_GET_ADDRESS(b % particle, bank_disp(6), mpi_err)
|
||||
|
||||
! Adjust displacements
|
||||
bank_disp = bank_disp - bank_disp(1)
|
||||
|
||||
! Define MPI_BANK for fission sites
|
||||
bank_blocks = (/ 1, 3, 3, 1, 1, 1 /)
|
||||
bank_types = (/ MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, &
|
||||
MPI_INT, MPI_INT /)
|
||||
call MPI_TYPE_CREATE_STRUCT(6, bank_blocks, bank_disp, &
|
||||
bank_blocks = (/ 1, 3, 3, 1, 1 /)
|
||||
bank_types = (/ MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_REAL8, MPI_INTEGER /)
|
||||
call MPI_TYPE_CREATE_STRUCT(5, bank_blocks, bank_disp, &
|
||||
bank_types, MPI_BANK, mpi_err)
|
||||
call MPI_TYPE_COMMIT(MPI_BANK, mpi_err)
|
||||
|
||||
end subroutine initialize_mpi
|
||||
#endif
|
||||
|
||||
!===============================================================================
|
||||
! HDF5_INITIALIZE
|
||||
!===============================================================================
|
||||
|
||||
subroutine hdf5_initialize()
|
||||
|
||||
type(Bank), target :: tmpb(2) ! temporary Bank
|
||||
integer :: hdf5_err
|
||||
integer(HID_T) :: coordinates_t ! HDF5 type for 3 reals
|
||||
integer(HSIZE_T) :: dims(1) = (/3/) ! size of coordinates
|
||||
|
||||
! Initialize FORTRAN interface.
|
||||
call h5open_f(hdf5_err)
|
||||
|
||||
! Create compound type for xyz and uvw
|
||||
call h5tarray_create_f(H5T_NATIVE_DOUBLE, 1, dims, coordinates_t, hdf5_err)
|
||||
|
||||
! Create the compound datatype for Bank
|
||||
call h5tcreate_f(H5T_COMPOUND_F, h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(2))), hdf5_bank_t, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "wgt", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%wgt)), H5T_NATIVE_DOUBLE, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "xyz", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%xyz)), coordinates_t, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "uvw", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%uvw)), coordinates_t, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "E", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%E)), H5T_NATIVE_DOUBLE, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "delayed_group", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%delayed_group)), H5T_NATIVE_INTEGER, hdf5_err)
|
||||
call h5tinsert_f(hdf5_bank_t, "particle", h5offsetof(c_loc(tmpb(1)), &
|
||||
c_loc(tmpb(1)%particle)), H5T_NATIVE_INTEGER, hdf5_err)
|
||||
|
||||
! Determine type for integer(8)
|
||||
hdf5_integer8_t = h5kind_to_type(8, H5_INTEGER_KIND)
|
||||
|
||||
end subroutine hdf5_initialize
|
||||
|
||||
!===============================================================================
|
||||
! READ_COMMAND_LINE reads all parameters from the command line
|
||||
!===============================================================================
|
||||
|
||||
subroutine read_command_line()
|
||||
! Arguments were already read on C++ side (initialize.cpp). Here we just
|
||||
! convert the C-style strings to Fortran style
|
||||
|
||||
integer :: i ! loop index
|
||||
integer :: argc ! number of command line arguments
|
||||
integer :: last_flag ! index of last flag
|
||||
character(MAX_WORD_LEN) :: filetype
|
||||
integer(HID_T) :: file_id
|
||||
character(MAX_WORD_LEN), allocatable :: argv(:) ! command line arguments
|
||||
character(kind=C_CHAR), pointer :: string(:)
|
||||
interface
|
||||
function is_null(ptr) result(x) bind(C)
|
||||
import C_PTR, C_BOOL
|
||||
type(C_PTR), value :: ptr
|
||||
logical(C_BOOL) :: x
|
||||
end function is_null
|
||||
end interface
|
||||
|
||||
! Check number of command line arguments and allocate argv
|
||||
argc = COMMAND_ARGUMENT_COUNT()
|
||||
|
||||
! Allocate and retrieve command arguments
|
||||
allocate(argv(argc))
|
||||
do i = 1, argc
|
||||
call GET_COMMAND_ARGUMENT(i, argv(i))
|
||||
end do
|
||||
|
||||
! Process command arguments
|
||||
last_flag = 0
|
||||
i = 1
|
||||
do while (i <= argc)
|
||||
! Check for flags
|
||||
if (starts_with(argv(i), "-")) then
|
||||
select case (argv(i))
|
||||
case ('-p', '-plot', '--plot')
|
||||
run_mode = MODE_PLOTTING
|
||||
check_overlaps = .true.
|
||||
|
||||
case ('-n', '-particles', '--particles')
|
||||
! Read number of particles per cycle
|
||||
i = i + 1
|
||||
n_particles = str_to_int(argv(i))
|
||||
|
||||
! Check that number specified was valid
|
||||
if (n_particles == ERROR_INT) then
|
||||
call fatal_error("Must specify integer after " // trim(argv(i-1)) &
|
||||
&// " command-line flag.")
|
||||
end if
|
||||
case ('-r', '-restart', '--restart')
|
||||
! Read path for state point/particle restart
|
||||
i = i + 1
|
||||
|
||||
! Check what type of file this is
|
||||
file_id = file_open(argv(i), 'r', parallel=.true.)
|
||||
call read_attribute(filetype, file_id, 'filetype')
|
||||
call file_close(file_id)
|
||||
|
||||
! Set path and flag for type of run
|
||||
select case (trim(filetype))
|
||||
case ('statepoint')
|
||||
path_state_point = argv(i)
|
||||
restart_run = .true.
|
||||
case ('particle restart')
|
||||
path_particle_restart = argv(i)
|
||||
particle_restart_run = .true.
|
||||
case default
|
||||
call fatal_error("Unrecognized file after restart flag: " // filetype // ".")
|
||||
end select
|
||||
|
||||
! If its a restart run check for additional source file
|
||||
if (restart_run .and. i + 1 <= argc) then
|
||||
|
||||
! Increment arg
|
||||
i = i + 1
|
||||
|
||||
! Check if it has extension we can read
|
||||
if (ends_with(argv(i), '.h5')) then
|
||||
|
||||
! Check file type is a source file
|
||||
file_id = file_open(argv(i), 'r', parallel=.true.)
|
||||
call read_attribute(filetype, file_id, 'filetype')
|
||||
call file_close(file_id)
|
||||
if (filetype /= 'source') then
|
||||
call fatal_error("Second file after restart flag must be a &
|
||||
&source file")
|
||||
end if
|
||||
|
||||
! It is a source file
|
||||
path_source_point = argv(i)
|
||||
|
||||
else ! Different option is specified not a source file
|
||||
|
||||
! Source is in statepoint file
|
||||
path_source_point = path_state_point
|
||||
|
||||
! Set argument back
|
||||
i = i - 1
|
||||
|
||||
end if
|
||||
|
||||
else ! No command line arg after statepoint
|
||||
|
||||
! Source is assumed to be in statepoint file
|
||||
path_source_point = path_state_point
|
||||
|
||||
end if
|
||||
|
||||
case ('-g', '-geometry-debug', '--geometry-debug')
|
||||
check_overlaps = .true.
|
||||
|
||||
case ('-c', '--volume')
|
||||
run_mode = MODE_VOLUME
|
||||
|
||||
case ('-s', '--threads')
|
||||
! Read number of threads
|
||||
i = i + 1
|
||||
|
||||
#ifdef _OPENMP
|
||||
! Read and set number of OpenMP threads
|
||||
n_threads = int(str_to_int(argv(i)), 4)
|
||||
if (n_threads < 1) then
|
||||
call fatal_error("Invalid number of threads specified on command &
|
||||
&line.")
|
||||
end if
|
||||
call omp_set_num_threads(n_threads)
|
||||
#else
|
||||
if (master) call warning("Ignoring number of threads specified on &
|
||||
&command line.")
|
||||
#endif
|
||||
|
||||
case ('-?', '-h', '-help', '--help')
|
||||
call print_usage()
|
||||
stop
|
||||
case ('-v', '-version', '--version')
|
||||
call print_version()
|
||||
stop
|
||||
case ('-t', '-track', '--track')
|
||||
write_all_tracks = .true.
|
||||
case default
|
||||
call fatal_error("Unknown command line option: " // argv(i))
|
||||
end select
|
||||
|
||||
last_flag = i
|
||||
end if
|
||||
|
||||
! Increment counter
|
||||
i = i + 1
|
||||
end do
|
||||
|
||||
! Determine directory where XML input files are
|
||||
if (argc > 0 .and. last_flag < argc) then
|
||||
path_input = argv(last_flag + 1)
|
||||
if (.not. is_null(openmc_path_input)) then
|
||||
call c_f_pointer(openmc_path_input, string, [255])
|
||||
path_input = to_f_string(string)
|
||||
else
|
||||
path_input = ''
|
||||
end if
|
||||
|
||||
! Add slash at end of directory if it isn't there
|
||||
if (.not. ends_with(path_input, "/") .and. len_trim(path_input) > 0) then
|
||||
path_input = trim(path_input) // "/"
|
||||
if (.not. is_null(openmc_path_statepoint)) then
|
||||
call c_f_pointer(openmc_path_statepoint, string, [255])
|
||||
path_state_point = to_f_string(string)
|
||||
end if
|
||||
if (.not. is_null(openmc_path_sourcepoint)) then
|
||||
call c_f_pointer(openmc_path_sourcepoint, string, [255])
|
||||
path_source_point = to_f_string(string)
|
||||
end if
|
||||
if (.not. is_null(openmc_path_particle_restart)) then
|
||||
call c_f_pointer(openmc_path_particle_restart, string, [255])
|
||||
path_particle_restart = to_f_string(string)
|
||||
end if
|
||||
|
||||
! Free memory from argv
|
||||
deallocate(argv)
|
||||
|
||||
! TODO: Check that directory exists
|
||||
|
||||
! Add slash at end of directory if it isn't there
|
||||
if (len_trim(path_input) > 0 .and. .not. ends_with(path_input, "/")) then
|
||||
path_input = trim(path_input) // "/"
|
||||
end if
|
||||
end subroutine read_command_line
|
||||
|
||||
end module initialize
|
||||
|
|
|
|||
229
src/initialize.cpp
Normal file
229
src/initialize.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
#include "initialize.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "error.h"
|
||||
#include "hdf5_interface.h"
|
||||
#include "message_passing.h"
|
||||
#include "openmc.h"
|
||||
#ifdef _OPENMP
|
||||
#include "omp.h"
|
||||
#endif
|
||||
|
||||
// data/functions from Fortran side
|
||||
extern "C" bool openmc_check_overlaps;
|
||||
extern "C" bool openmc_write_all_tracks;
|
||||
extern "C" bool openmc_particle_restart_run;
|
||||
extern "C" bool openmc_restart_run;
|
||||
extern "C" void print_usage();
|
||||
extern "C" void print_version();
|
||||
|
||||
|
||||
// Paths to various files
|
||||
extern "C" {
|
||||
char* openmc_path_input;
|
||||
char* openmc_path_statepoint;
|
||||
char* openmc_path_sourcepoint;
|
||||
char* openmc_path_particle_restart;
|
||||
bool is_null(void* ptr) {return !ptr;}
|
||||
}
|
||||
|
||||
int openmc_init(int argc, char* argv[], const void* intracomm)
|
||||
{
|
||||
#ifdef OPENMC_MPI
|
||||
// Check if intracomm was passed
|
||||
MPI_Comm comm;
|
||||
if (intracomm) {
|
||||
comm = *static_cast<const MPI_Comm *>(intracomm);
|
||||
} else {
|
||||
comm = MPI_COMM_WORLD;
|
||||
}
|
||||
|
||||
// Initialize MPI for C++
|
||||
openmc::initialize_mpi(comm);
|
||||
#endif
|
||||
|
||||
// Parse command-line arguments
|
||||
int err = openmc::parse_command_line(argc, argv);
|
||||
if (err) return err;
|
||||
|
||||
// Continue with rest of initialization
|
||||
#ifdef OPENMC_MPI
|
||||
MPI_Fint fcomm = MPI_Comm_c2f(comm);
|
||||
openmc_init_f(&fcomm);
|
||||
#else
|
||||
openmc_init_f(nullptr);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace openmc {
|
||||
|
||||
#ifdef OPENMC_MPI
|
||||
void initialize_mpi(MPI_Comm intracomm)
|
||||
{
|
||||
openmc::mpi::intracomm = intracomm;
|
||||
|
||||
// Initialize MPI
|
||||
int flag;
|
||||
MPI_Initialized(&flag);
|
||||
if (!flag) MPI_Init(nullptr, nullptr);
|
||||
|
||||
// Determine number of processes and rank for each
|
||||
MPI_Comm_size(intracomm, &openmc::mpi::n_procs);
|
||||
MPI_Comm_rank(intracomm, &openmc::mpi::rank);
|
||||
|
||||
// Set variable for Fortran side
|
||||
openmc_n_procs = openmc::mpi::n_procs;
|
||||
openmc_rank = openmc::mpi::rank;
|
||||
openmc_master = (openmc::mpi::rank == 0);
|
||||
|
||||
// Create bank datatype
|
||||
Bank b;
|
||||
MPI_Aint disp[] {
|
||||
offsetof(Bank, wgt),
|
||||
offsetof(Bank, xyz),
|
||||
offsetof(Bank, uvw),
|
||||
offsetof(Bank, E),
|
||||
offsetof(Bank, delayed_group)
|
||||
};
|
||||
int blocks[] {1, 3, 3, 1, 1};
|
||||
MPI_Datatype types[] {MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_DOUBLE, MPI_INT};
|
||||
MPI_Type_create_struct(5, blocks, disp, types, &openmc::mpi::bank);
|
||||
MPI_Type_commit(&openmc::mpi::bank);
|
||||
}
|
||||
#endif // OPENMC_MPI
|
||||
|
||||
|
||||
inline bool ends_with(std::string const& value, std::string const& ending)
|
||||
{
|
||||
if (ending.size() > value.size()) return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
parse_command_line(int argc, char* argv[])
|
||||
{
|
||||
char buffer[256]; // buffer for reading attribute
|
||||
int last_flag = 0;
|
||||
for (int i=1; i < argc; ++i) {
|
||||
std::string arg {argv[i]};
|
||||
if (arg[0] == '-') {
|
||||
if (arg == "-p" || arg == "--plot") {
|
||||
openmc_run_mode = RUN_MODE_PLOTTING;
|
||||
openmc_check_overlaps = true;
|
||||
|
||||
} else if (arg == "-n" || arg == "--particles") {
|
||||
i += 1;
|
||||
n_particles = std::stoll(argv[i]);
|
||||
|
||||
} else if (arg == "-r" || arg == "--restart") {
|
||||
i += 1;
|
||||
|
||||
// Check what type of file this is
|
||||
hid_t file_id = file_open(argv[i], 'r', true);
|
||||
size_t len = attribute_typesize(file_id, "filetype");
|
||||
read_attr_string(file_id, "filetype", len, buffer);
|
||||
file_close(file_id);
|
||||
std::string filetype {buffer};
|
||||
|
||||
// Set path and flag for type of run
|
||||
if (filetype == "statepoint") {
|
||||
openmc_path_statepoint = argv[i];
|
||||
openmc_restart_run = true;
|
||||
} else if (filetype == "particle restart") {
|
||||
openmc_path_particle_restart = argv[i];
|
||||
openmc_particle_restart_run = true;
|
||||
} else {
|
||||
std::stringstream msg;
|
||||
msg << "Unrecognized file after restart flag: " << filetype << ".";
|
||||
strcpy(openmc_err_msg, msg.str().c_str());
|
||||
return OPENMC_E_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// If its a restart run check for additional source file
|
||||
if (openmc_restart_run && i + 1 < argc) {
|
||||
// Check if it has extension we can read
|
||||
if (ends_with(argv[i+1], ".h5")) {
|
||||
|
||||
// Check file type is a source file
|
||||
file_id = file_open(argv[i+1], 'r', true);
|
||||
len = attribute_typesize(file_id, "filetype");
|
||||
read_attr_string(file_id, "filetype", len, buffer);
|
||||
file_close(file_id);
|
||||
if (filetype != "source") {
|
||||
std::string msg {"Second file after restart flag must be a source file"};
|
||||
strcpy(openmc_err_msg, msg.c_str());
|
||||
return OPENMC_E_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// It is a source file
|
||||
openmc_path_sourcepoint = argv[i+1];
|
||||
i += 1;
|
||||
|
||||
} else {
|
||||
// Source is in statepoint file
|
||||
openmc_path_sourcepoint = openmc_path_statepoint;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Source is assumed to be in statepoint file
|
||||
openmc_path_sourcepoint = openmc_path_statepoint;
|
||||
}
|
||||
|
||||
} else if (arg == "-g" || arg == "--geometry-debug") {
|
||||
openmc_check_overlaps = true;
|
||||
} else if (arg == "-c" || arg == "--volume") {
|
||||
openmc_run_mode = RUN_MODE_VOLUME;
|
||||
} else if (arg == "-s" || arg == "--threads") {
|
||||
// Read number of threads
|
||||
i += 1;
|
||||
|
||||
#ifdef _OPENMP
|
||||
// Read and set number of OpenMP threads
|
||||
openmc_n_threads = std::stoi(argv[i]);
|
||||
if (openmc_n_threads < 1) {
|
||||
std::string msg {"Number of threads must be positive."};
|
||||
strcpy(openmc_err_msg, msg.c_str());
|
||||
return OPENMC_E_INVALID_ARGUMENT;
|
||||
}
|
||||
omp_set_num_threads(openmc_n_threads);
|
||||
#else
|
||||
if (openmc_master)
|
||||
warning("Ignoring number of threads specified on command line.");
|
||||
#endif
|
||||
|
||||
} else if (arg == "-?" || arg == "-h" || arg == "--help") {
|
||||
print_usage();
|
||||
return OPENMC_E_UNASSIGNED;
|
||||
|
||||
} else if (arg == "-v" || arg == "--version") {
|
||||
print_version();
|
||||
return OPENMC_E_UNASSIGNED;
|
||||
|
||||
} else if (arg == "-t" || arg == "--track") {
|
||||
openmc_write_all_tracks = true;
|
||||
|
||||
} else {
|
||||
std::cerr << "Unknown option: " << argv[i] << '\n';
|
||||
print_usage();
|
||||
return OPENMC_E_UNASSIGNED;
|
||||
}
|
||||
|
||||
last_flag = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine directory where XML input files are
|
||||
if (argc > 1 && last_flag < argc) openmc_path_input = argv[last_flag + 1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace openmc
|
||||
20
src/initialize.h
Normal file
20
src/initialize.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef INITIALIZE_H
|
||||
#define INITIALIZE_H
|
||||
|
||||
#ifdef OPENMC_MPI
|
||||
#include "mpi.h"
|
||||
#endif
|
||||
|
||||
extern "C" void print_usage();
|
||||
extern "C" void print_version();
|
||||
|
||||
namespace openmc {
|
||||
|
||||
int parse_command_line(int argc, char* argv[]);
|
||||
#ifdef OPENMC_MPI
|
||||
void initialize_mpi(MPI_Comm intracomm);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // INITIALIZE_H
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue