diff --git a/.gitignore b/.gitignore index 3832322966..f733096466 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.pyc # Python distribution +.settings/ dist/ openmc.egg-info/ diff --git a/.travis.yml b/.travis.yml index ade182788d..096459b00a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,4 +53,5 @@ before_script: script: - ./tools/ci/travis-script.sh after_success: - - coveralls + - cpp-coveralls -i src -i include --exclude-pattern "/usr/*" --dump cpp_cov.json + - coveralls --merge=cpp_cov.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 4033d2466d..9f08124811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,9 @@ endif() # Set compile/link flags based on which compiler is being used #=============================================================================== +# Skip for Visual Stduio which has its own configurations through GUI +if(NOT MSVC) + if(openmp) # Requires CMake 3.1+ find_package(OpenMP) @@ -95,11 +98,17 @@ if(optimize) list(REMOVE_ITEM cxxflags -O2) list(APPEND cxxflags -O3) endif() +if(coverage) + list(APPEND cxxflags --coverage) + list(APPEND ldflags --coverage) +endif() # Show flags being used message(STATUS "OpenMC C++ flags: ${cxxflags}") message(STATUS "OpenMC Linker flags: ${ldflags}") +endif() + #=============================================================================== # pugixml library #=============================================================================== @@ -115,6 +124,16 @@ add_subdirectory(vendor/xtl) add_subdirectory(vendor/xtensor) target_link_libraries(xtensor INTERFACE xtl) +#=============================================================================== +# GSL header-only library +#=============================================================================== + +add_library(gsl INTERFACE) +target_include_directories(gsl INTERFACE vendor/gsl/include) + +# Make sure contract violations throw exceptions +target_compile_definitions(gsl INTERFACE GSL_THROW_ON_CONTRACT_VIOLATION) + #=============================================================================== # RPATH information #=============================================================================== @@ -155,7 +174,7 @@ target_compile_options(faddeeva PRIVATE ${cxxflags}) # libopenmc #=============================================================================== -add_library(libopenmc SHARED +list(APPEND libopenmc_SOURCES src/bank.cpp src/bremsstrahlung.cpp src/dagmc.cpp @@ -242,6 +261,18 @@ add_library(libopenmc SHARED src/xml_interface.cpp src/xsdata.cpp) +# For Visual Studio compilers +if(MSVC) + # Use static library (otherwise explicit symbol portings are needed) + add_library(libopenmc STATIC ${libopenmc_SOURCES}) + + # To use the shared HDF5 libraries on Windows, the H5_BUILT_AS_DYNAMIC_LIB + # compile definition must be specified. + target_compile_definitions(libopenmc PRIVATE -DH5_BUILT_AS_DYNAMIC_LIB) +else() + add_library(libopenmc SHARED ${libopenmc_SOURCES}) +endif() + set_target_properties(libopenmc PROPERTIES OUTPUT_NAME openmc) @@ -271,7 +302,7 @@ endif() # target_link_libraries treats any arguments starting with - but not -l as # linker flags. Thus, we can pass both linker flags and libraries together. target_link_libraries(libopenmc ${ldflags} ${HDF5_LIBRARIES} ${HDF5_HL_LIBRARIES} - pugixml faddeeva xtensor) + pugixml faddeeva xtensor gsl) if(dagmc) target_compile_definitions(libopenmc PRIVATE DAGMC) diff --git a/MANIFEST.in b/MANIFEST.in index be82928a15..5715bfb444 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,10 @@ include CMakeLists.txt include LICENSE +include CODE_OF_CONDUCT.md +include CONTRIBUTING.md include schemas.xml include pyproject.toml +include pytest.ini include openmc/data/reconstruct.pyx include docs/source/_templates/layout.html include docs/sphinxext/LICENSE @@ -33,4 +36,11 @@ recursive-include tests *.dat recursive-include tests *.h5 recursive-include tests *.py recursive-include tests *.xml +recursive-include vendor CMakeLists.txt +recursive-include vendor *.cmake.in +recursive-include vendor *.cc +recursive-include vendor *.cpp +recursive-include vendor *.hh +recursive-include vendor *.hpp prune docs/build +prune docs/source/pythonapi/generated/ diff --git a/docs/requirements-rtd.txt b/docs/requirements-rtd.txt index 652f880ff8..15345247eb 100644 --- a/docs/requirements-rtd.txt +++ b/docs/requirements-rtd.txt @@ -1,2 +1,3 @@ sphinx-numfig jupyter +sphinxcontrib-katex diff --git a/docs/source/capi/index.rst b/docs/source/capi/index.rst index 274a5240f0..90bef1909c 100644 --- a/docs/source/capi/index.rst +++ b/docs/source/capi/index.rst @@ -335,6 +335,15 @@ Functions :return: Return status (negative if an error occurs) :rtype: int +.. c:function:: int openmc_material_get_density(int32_t index, double* density) + + Get density of a material. + + :param int32_t index: Index in the materials array + :param double* denity: Pointer to a density + :return: Return status (negative if an error occurs) + :rtype: int + .. c:function:: int openmc_material_get_id(int32_t index, int32_t* id) Get the ID of a material diff --git a/docs/source/conf.py b/docs/source/conf.py index 22446213af..a24f9b398a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -49,10 +49,10 @@ sys.path.insert(0, os.path.abspath('../..')) # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', - 'sphinx.ext.mathjax', 'sphinx.ext.autosummary', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', + 'sphinxcontrib.katex', 'sphinx_numfig', 'notebook_sphinxext'] if not on_rtd: @@ -71,17 +71,17 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'OpenMC' -copyright = u'2011-2018, Massachusetts Institute of Technology and OpenMC contributors' +project = 'OpenMC' +copyright = '2011-2019, Massachusetts Institute of Technology and OpenMC contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = "0.10" +version = "0.11" # The full version, including alpha/beta/rc tags. -release = "0.10.0" +release = "0.11.0-dev" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -208,8 +208,8 @@ htmlhelp_basename = 'openmcdoc' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'openmc.tex', u'OpenMC Documentation', - u'OpenMC contributors', 'manual'), + ('index', 'openmc.tex', 'OpenMC Documentation', + 'OpenMC contributors', 'manual'), ] latex_elements = { diff --git a/docs/source/index.rst b/docs/source/index.rst index 4071b9794c..5500890e2b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,7 +35,7 @@ list `_. quickinstall examples/index - releasenotes + releasenotes/index methods/index usersguide/index devguide/index diff --git a/docs/source/io_formats/geometry.rst b/docs/source/io_formats/geometry.rst index 94511842dc..617ac6e870 100644 --- a/docs/source/io_formats/geometry.rst +++ b/docs/source/io_formats/geometry.rst @@ -318,6 +318,13 @@ the following attributes or sub-elements: *Default*: None + :orientation: + The orientation of the hexagonal lattice. The string "x" indicates that two + sides of the lattice are parallel to the x-axis, whereas the string "y" + indicates that two sides are parallel to the y-axis. + + *Default*: "y" + :center: The coordinates of the center of the lattice. If the lattice does not have axial sections then only the x- and y-coordinates are specified. diff --git a/docs/source/io_formats/tallies.rst b/docs/source/io_formats/tallies.rst index 23a0201ca9..0e1cd16f4e 100644 --- a/docs/source/io_formats/tallies.rst +++ b/docs/source/io_formats/tallies.rst @@ -312,21 +312,30 @@ a separate element with the tag name ````. This element has the following attributes/sub-elements: :type: - The type of structured mesh. The only valid option is "regular". + The type of structured mesh. This can be either "regular" or "rectilinear". :dimension: - The number of mesh cells in each direction. + The number of mesh cells in each direction. (For regular mesh only.) :lower_left: The lower-left corner of the structured mesh. If only two coordinates are - given, it is assumed that the mesh is an x-y mesh. + given, it is assumed that the mesh is an x-y mesh. (For regular mesh only.) :upper_right: The upper-right corner of the structured mesh. If only two coordinates are - given, it is assumed that the mesh is an x-y mesh. + given, it is assumed that the mesh is an x-y mesh. (For regular mesh only.) :width: - The width of mesh cells in each direction. + The width of mesh cells in each direction. (For regular mesh only.) + + :x_grid: + The mesh divisions along the x-axis. (For rectilinear mesh only.) + + :y_grid: + The mesh divisions along the y-axis. (For rectilinear mesh only.) + + :z_grid: + The mesh divisions along the z-axis. (For rectilinear mesh only.) .. note:: One of ```` or ```` must be specified, but not both diff --git a/docs/source/methods/neutron_physics.rst b/docs/source/methods/neutron_physics.rst index 3451ae83f0..60d3c1468a 100644 --- a/docs/source/methods/neutron_physics.rst +++ b/docs/source/methods/neutron_physics.rst @@ -86,7 +86,7 @@ Elastic Scattering ------------------ Note that the multi-group mode makes no distinction between elastic or -inelastic scattering reactions. The spceific multi-group scattering +inelastic scattering reactions. The specific multi-group scattering implementation is discussed in the :ref:`multi-group-scatter` section. Elastic scattering refers to the process by which a neutron scatters off a @@ -1177,12 +1177,12 @@ parts as such: .. math:: :label: divide-pdf + \begin{aligned} p(v_T, \mu) &= f_1(v_T, \mu) f_2(v_T) \\ - f_1(v_T, \mu) &= \frac{4\sigma_s}{\sqrt{\pi} C'} \frac{ \sqrt{v_n^2 + v_T^2 - 2v_n v_T \mu}}{v_n + v_T} \\ - f_2(v_T) &= (v_n + v_T) \beta^3 v_T^2 \exp \left ( -\beta^2 v_T^2 \right ). + \end{aligned} In general, any probability distribution function of the form :math:`p(x) = f_1(x) f_2(x)` with :math:`f_1(x)` bounded can be sampled by sampling diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index 2d9a62ef37..4494fc6431 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -50,10 +50,12 @@ The differential cross section for Rayleigh scattering is given by .. math:: :label: coherent-xs + \begin{aligned} \frac{d\sigma(E,E',\mu)}{d\mu} &= \pi r_e^2 ( 1 + \mu^2 )~\left| F(x,Z) + F' + iF'' \right|^2 \\ &= \pi r_e^2 ( 1 + \mu^2 ) \left [ ( F(x,Z) + F'(E) )^2 + F''(E)^2 \right ] + \end{aligned} where :math:`F(x,Z)` is a form factor as a function of the momentum transfer :math:`x` and the atomic number :math:`Z` and the term :math:`F' + iF''` @@ -93,7 +95,7 @@ mass, and the coefficient :math:`a` can be shown to be .. math:: :label: omega - a = \frac{m_e c^2}{\sqrt{2}hc} \approx 29.14329~\unicode{x212B}, + a = \frac{m_e c^2}{\sqrt{2}hc} \approx 2.914329\times10^{-9}~\text{m} where :math:`m_e` is the mass of the electron, :math:`c` is the speed of light in a vacuum, and :math:`h` is Planck's constant. Using :eq:`momentum-transfer`, @@ -371,9 +373,11 @@ where .. math:: :label: mu-pdf-factors + \begin{aligned} \psi(\mu_{-}) &= \frac{(1 - \beta_{-}^2)(1 - \mu_{-}^2)}{(1 - \beta_{-}\mu_{-})^2}, \\ g(\mu_{-}) &= \frac{1 - \beta_{-}^2}{2 (1 - \beta_{-}\mu_{-})^2}. + \end{aligned} In the interval :math:`[-1, 1]`, :math:`g(\mu_{-})` is a normalized PDF and :math:`\psi(\mu_{-})` satisfies the condition :math:`0 < \psi(\mu_{-}) < 1`. @@ -450,10 +454,12 @@ The Coulomb correction, given by .. math:: :label: coulomb-correction + \begin{aligned} f_C = \alpha^{2}Z^{2} \big[&(1 + \alpha^{2}Z^{2})^{-1} + 0.202059 - 0.03693\alpha^{2}Z^{2} + 0.00835\alpha^{4}Z^{4} \\ &- 0.00201\alpha^{6}Z^{6} + 0.00049\alpha^{8}Z^{8} - 0.00012\alpha^{10}Z^{10} + 0.00003\alpha^{12}Z^{12}\big] + \end{aligned} is introduced to correct for the fact that the Bethe-Heitler differential cross section was derived using the Born approximation, which treats the Coulomb @@ -469,9 +475,11 @@ approximations of the screening functions can be derived: .. math:: :label: screening-functions + \begin{aligned} \Phi_1 &= 2 - 2\ln(1 + b^2) - 4b\arctan(b^{-1}) + 4\ln(Rm_{e}c/\hbar) \\ \Phi_2 &= \frac{4}{3} - 2\ln(1 + b^2) + 2b^2 \left[ 4 - 4b\arctan(b^{-1}) - 3\ln(1 + b^{-2}) \right] + 4\ln(Rm_{e}c/\hbar) + \end{aligned} where @@ -492,10 +500,12 @@ upper boundary of :math:`\epsilon` will be shifted below .. math:: :label: correcting-factor + \begin{aligned} F_0(k, Z) =~& (0.1774 + 12.10\alpha Z - 11.18\alpha^{2}Z^{2})(2/k)^{1/2} \\ &+ (8.523 + 73.26\alpha Z - 44.41\alpha^{2}Z^{2})(2/k) \\ &- (13.52 + 121.1\alpha Z - 96.41\alpha^{2}Z^{2})(2/k)^{3/2} \\ &+ (8.946 + 62.05\alpha Z - 63.41\alpha^{2}Z^{2})(2/k)^{2}. + \end{aligned} To aid sampling, the differential cross section used to sample :math:`\epsilon` (minus the normalization constant) can now be expressed in the form @@ -512,23 +522,29 @@ where .. math:: :label: u + \begin{aligned} u_1 &= \frac{2}{3} \left(\frac{1}{2} - \frac{1}{k}\right)^2 \phi_1(1/2), \\ u_2 &= \phi_2(1/2), + \end{aligned} .. math:: :label: phi + \begin{aligned} \phi_1(\epsilon) &= \frac{1}{2}(3\Phi_1 - \Phi_2) - 4f_{C}(Z) + F_0(k, Z), \\ \phi_2(\epsilon) &= \frac{1}{4}(3\Phi_1 + \Phi_2) - 4f_{C}(Z) + F_0(k, Z), + \end{aligned} and .. math:: :label: pi + \begin{aligned} \pi_1(\epsilon) &= \frac{3}{2} \left(\frac{1}{2} - \frac{1}{k}\right)^{-3} \left(\frac{1}{2} - \epsilon\right)^2, \\ \pi_2(\epsilon) &= \frac{1}{2} \left(\frac{1}{2} - \frac{1}{k}\right)^{-1}. + \end{aligned} The functions in :eq:`phi` are non-negative and maximum at :math:`\epsilon = 1/2`. In the interval :math:`(\epsilon_{\text{min}}, \epsilon_{\text{max}})`, @@ -545,10 +561,12 @@ sample the reduced electron energy :math:`\epsilon`: .. math:: + \begin{aligned} \epsilon &= \frac{1}{2} + \left(\frac{1}{2} - \frac{1}{k}\right) (2\xi_1 - 1)^{1/3} ~~~~&\text{if}~~ i = 1 \\ \epsilon &= \frac{1}{k} + \left(\frac{1}{2} - \frac{1}{k}\right) 2\xi_1 ~~~~&\text{if}~~ i = 2. + \end{aligned} 3. If :math:`\xi_2 \le \phi_i(\epsilon)/\phi_i(1/2)`, accept :math:`\epsilon`. Otherwise, repeat the sampling from step 1. @@ -701,10 +719,12 @@ in Salvat_, .. math:: :label: positron-factor + \begin{aligned} F_{\text{p}}(Z,T) = & 1 - \text{exp}(-1.2359\times 10^{-1}t + 6.1274\times 10^{-2}t^2 - 3.1516\times 10^{-2}t^3 \\ & + 7.7446\times 10^{-3}t^4 - 1.0595\times 10^{-3}t^5 + 7.0568\times 10^{-5}t^6 \\ & - 1.8080\times 10^{-6}t^7), + \end{aligned} where @@ -840,8 +860,10 @@ defined as .. math:: :label: density-effect-li + \begin{aligned} l_i &= (\bar{\nu}_i^2 + 2/3f_i)^{1/2} ~~~~&\text{for}~~ \bar{\nu}_i > 0 \\ l_n &= f_n^{1/2} ~~~~&\text{for}~~ \bar{\nu}_n = 0, + \end{aligned} where the second case applies to conduction electrons. For a conductor, :math:`f_n` is given by :math:`n_c/Z`, where :math:`n_c` is the effective diff --git a/docs/source/pythonapi/base.rst b/docs/source/pythonapi/base.rst index 8cf65a997f..2c0ac8797d 100644 --- a/docs/source/pythonapi/base.rst +++ b/docs/source/pythonapi/base.rst @@ -124,7 +124,8 @@ Constructing Tallies openmc.ZernikeFilter openmc.ZernikeRadialFilter openmc.ParticleFilter - openmc.Mesh + openmc.RegularMesh + openmc.RectilinearMesh openmc.Trigger openmc.TallyDerivative openmc.Tally diff --git a/docs/source/pythonapi/capi.rst b/docs/source/pythonapi/capi.rst index 38ce61fb35..cacc5472e1 100644 --- a/docs/source/pythonapi/capi.rst +++ b/docs/source/pythonapi/capi.rst @@ -44,8 +44,8 @@ Classes EnergyFilter MaterialFilter Material - Mesh MeshFilter MeshSurfaceFilter Nuclide + RegularMesh Tally diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 8abb52528b..87d0d2a245 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -36,9 +36,7 @@ or class. .. tip:: Users are strongly encouraged to use the Python API to generate input files and analyze results. -------- -Modules -------- +.. rubric:: Modules .. toctree:: :maxdepth: 1 diff --git a/docs/source/releasenotes.rst b/docs/source/releasenotes/0.10.0.rst similarity index 97% rename from docs/source/releasenotes.rst rename to docs/source/releasenotes/0.10.0.rst index d5ea6c58cd..e847fd0be3 100644 --- a/docs/source/releasenotes.rst +++ b/docs/source/releasenotes/0.10.0.rst @@ -1,8 +1,6 @@ -.. _releasenotes: - -=============================== -Release Notes for OpenMC 0.10.0 -=============================== +==================== +What's New in 0.10.0 +==================== .. currentmodule:: openmc diff --git a/docs/source/releasenotes/0.11.0.rst b/docs/source/releasenotes/0.11.0.rst new file mode 100644 index 0000000000..0bf44be3d4 --- /dev/null +++ b/docs/source/releasenotes/0.11.0.rst @@ -0,0 +1,102 @@ +==================== +What's New in 0.11.0 +==================== + +.. note:: + These release notes are for a future release of OpenMC and are still subject + to change. The new features and bug fixes documented here reflect the + current status of the ``develop`` branch of OpenMC. + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC adds several major new features: :ref:`depletion +`, photon transport, and support for CAD geometries +through DAGMC. In addition, the core codebase has been rewritten in C++14 (it +was previously written in Fortran 2008). This makes compiling the code +cosiderably simpler as no Fortran compiler is needed. + +Functional expansion tallies are now supported through several new tally filters +that can be arbitrarily combined: + +- :class:`openmc.LegendreFilter` +- :class:`openmc.SpatialLegendreFilter` +- :class:`openmc.SphericalHarmonicsFilter` +- :class:`openmc.ZernikeFilter` +- :class:`openmc.ZernikeRadialFilter` + +Note that these filters replace the use expansion scores like ``scatter-P1``. +Instead, a normal ``scatter`` score should be used along with a +:class:`openmc.LegendreFilter`. + +The interface for random sphere packing has been significantly improved. A new +:func:`openmc.model.pack_spheres` function takes a region and generates a +random, non-overlapping configuration of spheres within the region. + +------------ +New Features +------------ + +- The :class:`Geometry`, :class:`Materials`, and :class:`Settings` classes now + have a ``from_xml`` method that will build an instance from an existing XML + file. +- Predefined energy group structures can be found in + :data:`openmc.mgxs.GROUP_STRUCTURES`. +- New tally scores: ``H1-production``, ``H2-production``, ``H3-production``, + ``He3-production``, ``He4-production``, ``heating``, and ``damage-energy`` +- Switched to cell-based neighor lists (`PR 1140 + `_) +- Two new probability distributions that can be used for source distributions: + :class:`openmc.stats.Normal` and :class:`openmc.stats.Muir` +- The :mod:`openmc.data` module now supports reading and sampling from ENDF File + 32 resonance covariance data (`PR 1024 + `_). + +--------- +Bug Fixes +--------- + +- `Fix reading ASCII ACE tables in Python 3 `_ +- `Fix bug for distributed temperatures `_ +- `Fix bug for distance to boundary in complex cells `_ +- `Bug fixes for precursor decay rate tallies `_ +- `Check for invalid surface IDs in region expression `_ +- `Support for 32-bit operating systems `_ +- `Avoid segfault from unused nuclides `_ +- `Avoid overflow when broadcasting tally results `_ + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Brody Bassett `_ +- `Will Boyd `_ +- `Andrew Davis `_ +- `Guillaume Giudicelli `_ +- `Brittany Grayson `_ +- `Zhuoran Han `_ +- `Sterling Harper `_ +- `Andrew Johnson `_ +- `Colin Josey `_ +- `Shikhar Kumar `_ +- `Travis Labossiere-Hickman `_ +- `Matias Lavista `_ +- `Jingang Liang `_ +- `Alex Lindsay `_ +- `Johnny Liu `_ +- `Amanda Lund `_ +- `Jan Malec `_ +- `Isaac Meyer `_ +- `April Novak `_ +- `Adam Nelson `_ +- `Jose Salcedo Perez `_ +- `Paul Romano `_ +- `Sam Shaner `_ +- `Jonathan Shimwell `_ +- `Patrick Shriwise `_ +- `John Tramm `_ diff --git a/docs/source/releasenotes/0.4.0.rst b/docs/source/releasenotes/0.4.0.rst new file mode 100644 index 0000000000..b243656694 --- /dev/null +++ b/docs/source/releasenotes/0.4.0.rst @@ -0,0 +1,34 @@ +=================== +What's New in 0.4.0 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions as well as +Mac OS X. However, it has not been tested yet on any releases of Microsoft +Windows. Memory requirements will vary depending on the size of the problem at +hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- The probability table method for treatment of energy self-shielding in the + unresolved resonance range has been implemented and is now turned on by + default. +- Calculation of Shannon entropy for assessing convergence of the fission source + distribution. +- Ability to compile with the PGI Fortran compiler. +- Ability to run on IBM BlueGene/P machines. +- Completely rewrote how nested universes are handled. Geometry is now much more + robust. + +--------- +Bug Fixes +--------- + +- Many geometry errors have been fixed. The Monte Carlo performance benchmark + can now be successfully run in OpenMC. diff --git a/docs/source/releasenotes/0.4.1.rst b/docs/source/releasenotes/0.4.1.rst new file mode 100644 index 0000000000..8ec96897ef --- /dev/null +++ b/docs/source/releasenotes/0.4.1.rst @@ -0,0 +1,53 @@ +=================== +What's New in 0.4.1 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions as well as +Mac OS X. However, it has not been tested yet on any releases of Microsoft +Windows. Memory requirements will vary depending on the size of the problem at +hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- A batching method has been implemented so that statistics can be calculated + based on multiple generations instead of a single generation. This can help to + overcome problems with underpredicted variance in problems where there is + correlation between successive fission source iterations. +- Users now have the option to select a non-unionized energy grid for problems + with many nuclides where the use of a unionized grid is not feasible. +- Improved plotting capability (Nick Horelik). The plotting input is now in + ``plots.xml`` instead of ``plot.xml``. +- Added multiple estimators for k-effective and added a global tally for + leakage. +- Moved cross section-related output into cross_sections.out. +- Improved timing capabilities. +- Can now use more than 2**31 - 1 particles per generation. +- Improved fission bank synchronization method. This also necessitated changing + the source bank to be of type Bank rather than of type Particle. +- Added HDF5 output (not complete yet). +- Major changes to tally implementation. + +--------- +Bug Fixes +--------- + +- `b206a8`_: Fixed subtle error in the sampling of energy distributions. +- `800742`_: Fixed error in sampling of angle and rotating angles. +- `a07c08`_: Fixed bug in linear-linear interpolation during sampling energy. +- `a75283`_: Fixed energy and energyout tally filters to support many bins. +- `95cfac`_: Fixed error in cell neighbor searches. +- `83a803`_: Fixed bug related to probability tables. + +.. _b206a8: https://github.com/mit-crpg/openmc/commit/b206a8 +.. _800742: https://github.com/mit-crpg/openmc/commit/800742 +.. _a07c08: https://github.com/mit-crpg/openmc/commit/a07c08 +.. _a75283: https://github.com/mit-crpg/openmc/commit/a75283 +.. _95cfac: https://github.com/mit-crpg/openmc/commit/95cfac +.. _83a803: https://github.com/mit-crpg/openmc/commit/83a803 diff --git a/docs/source/releasenotes/0.4.2.rst b/docs/source/releasenotes/0.4.2.rst new file mode 100644 index 0000000000..c0d399301e --- /dev/null +++ b/docs/source/releasenotes/0.4.2.rst @@ -0,0 +1,54 @@ +=================== +What's New in 0.4.2 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Ability to specify void materials. +- Option to not reduce tallies across processors at end of each batch. +- Uniform fission site method for reducing variance on local tallies. +- Reading/writing binary source files. +- Added more messages for or high verbosity. +- Estimator for diffusion coefficient. +- Ability to specify 'point' source type. +- Ability to change random number seed. +- Users can now specify units='sum' on a tag. This tells the code that + the total material density is the sum of the atom fractions listed for each + nuclide on the material. + +--------- +Bug Fixes +--------- + +- a27f8f_: Fixed runtime error bug when using Intel compiler with DEBUG on. +- afe121_: Fixed minor bug in fission bank algorithms. +- e0968e_: Force re-evaluation of cross-sections when each particle is born. +- 298db8_: Fixed bug in surface currents when using energy-in filter. +- 2f3bbe_: Fixed subtle bug in S(a,b) cross section calculation. +- 671f30_: Fixed surface currents on mesh not encompassing geometry. +- b2c40e_: Fixed bug in incoming energy filter for track-length tallies. +- 5524fd_: Mesh filter now works with track-length tallies. +- d050c7_: Added Bessel's correction to make estimate of variance unbiased. +- 2a5b9c_: Fixed regression in plotting. + +.. _a27f8f: https://github.com/mit-crpg/openmc/commit/a27f8f +.. _afe121: https://github.com/mit-crpg/openmc/commit/afe121 +.. _e0968e: https://github.com/mit-crpg/openmc/commit/e0968e +.. _298db8: https://github.com/mit-crpg/openmc/commit/298db8 +.. _2f3bbe: https://github.com/mit-crpg/openmc/commit/2f3bbe +.. _671f30: https://github.com/mit-crpg/openmc/commit/671f30 +.. _b2c40e: https://github.com/mit-crpg/openmc/commit/b2c40e +.. _5524fd: https://github.com/mit-crpg/openmc/commit/5524fd +.. _d050c7: https://github.com/mit-crpg/openmc/commit/d050c7 +.. _2a5b9c: https://github.com/mit-crpg/openmc/commit/2a5b9c diff --git a/docs/source/releasenotes/0.4.3.rst b/docs/source/releasenotes/0.4.3.rst new file mode 100644 index 0000000000..7ef12198d7 --- /dev/null +++ b/docs/source/releasenotes/0.4.3.rst @@ -0,0 +1,51 @@ +=================== +What's New in 0.4.3 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Option to report confidence intervals for tally results. +- Rotation and translation for filled cells. +- Ability to explicitly specify for tallies. +- Ability to store state points and use them to restart runs. +- Fixed source calculations (no subcritical multiplication however). +- Expanded options for external source distribution. +- Ability to tally reaction rates for individual nuclides within a material. +- Reduced memory usage by removing redundant storage or some cross-sections. +- 3bd35b_: Log-log interpolation for URR probability tables. +- Support to specify labels on tallies (nelsonag_). + +--------- +Bug Fixes +--------- + +- 33f29a_: Handle negative values in probability table. +- 1c472d_: Fixed survival biasing with probability tables. +- 3c6e80_: Fixed writing tallies with no filters. +- 460ef1_: Invalid results for duplicate tallies. +- 0069d5_: Fixed bug with 0 inactive batches. +- 7af2cf_: Fixed bug in score_analog_tallies. +- 85a60e_: Pick closest angular distribution for law 61. +- 3212f5_: Fixed issue with blank line at beginning of XML files. + +.. _nelsonag: https://github.com/nelsonag +.. _33f29a: https://github.com/mit-crpg/openmc/commit/33f29a +.. _1c472d: https://github.com/mit-crpg/openmc/commit/1c472d +.. _3c6e80: https://github.com/mit-crpg/openmc/commit/3c6e80 +.. _3bd35b: https://github.com/mit-crpg/openmc/commit/3bd35b +.. _0069d5: https://github.com/mit-crpg/openmc/commit/0069d5 +.. _7af2cf: https://github.com/mit-crpg/openmc/commit/7af2cf +.. _460ef1: https://github.com/mit-crpg/openmc/commit/460ef1 +.. _85a60e: https://github.com/mit-crpg/openmc/commit/85a60e +.. _3212f5: https://github.com/mit-crpg/openmc/commit/3212f5 diff --git a/docs/source/releasenotes/0.4.4.rst b/docs/source/releasenotes/0.4.4.rst new file mode 100644 index 0000000000..d700e610d2 --- /dev/null +++ b/docs/source/releasenotes/0.4.4.rst @@ -0,0 +1,43 @@ +=================== +What's New in 0.4.4 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Ability to write state points when using . +- Real-time XML validation in GNU Emacs with RELAX NG schemata. +- Writing state points every n batches with +- Suppress creation of summary.out and cross_sections.out by default with option + to turn them on with tag in settings.xml file. +- Ability to create HDF5 state points. +- Binary source file is now part of state point file by default. +- Enhanced state point usage and added state point Python scripts. +- Turning confidence intervals on affects k-effective. +- Option to specify for tally meshes. + +--------- +Bug Fixes +--------- + +- 4654ee_: Fixed plotting with void cells. +- 7ee461_: Fixed bug with multi-line input using type='word'. +- 792eb3_: Fixed degrees of freedom for confidence intervals. +- 7fd617_: Fixed bug with restart runs in parallel. +- dc4a8f_: Fixed bug with fixed source restart runs. + +.. _4654ee: https://github.com/mit-crpg/openmc/commit/4654ee +.. _7ee461: https://github.com/mit-crpg/openmc/commit/7ee461 +.. _792eb3: https://github.com/mit-crpg/openmc/commit/792eb3 +.. _7fd617: https://github.com/mit-crpg/openmc/commit/7fd617 +.. _dc4a8f: https://github.com/mit-crpg/openmc/commit/dc4a8f diff --git a/docs/source/releasenotes/0.5.0.rst b/docs/source/releasenotes/0.5.0.rst new file mode 100644 index 0000000000..443216e335 --- /dev/null +++ b/docs/source/releasenotes/0.5.0.rst @@ -0,0 +1,49 @@ +=================== +What's New in 0.5.0 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- All user input options that formerly accepted "off" or "on" should now be + "false" or "true" (the proper XML schema datatype). +- The element is deprecated and was replaced with . +- Added 'events' score that returns number of events that scored to a tally. +- Restructured tally filter implementation and user input. +- Source convergence acceleration via CMFD (implemented with PETSc). +- Ability to read source files in parallel when number of particles is greater + than that number of source sites. +- Cone surface types. + +--------- +Bug Fixes +--------- + +- 737b90_: Coincident surfaces from separate universes / particle traveling + tangent to a surface. +- a819b4_: Output of surface neighbors in summary.out file. +- b11696_: Reading long attribute lists in XML input. +- 2bd46a_: Search for tallying nuclides when no default_xs specified. +- 7a1f08_: Fix word wrapping when writing messages. +- c0e3ec_: Prevent underflow when compiling with MPI=yes and DEBUG=yes. +- 6f8d9d_: Set default tally labels. +- 6a3a5e_: Fix problem with corner-crossing in lattices. + +.. _737b90: https://github.com/mit-crpg/openmc/commit/737b90 +.. _a819b4: https://github.com/mit-crpg/openmc/commit/a819b4 +.. _b11696: https://github.com/mit-crpg/openmc/commit/b11696 +.. _2bd46a: https://github.com/mit-crpg/openmc/commit/2bd46a +.. _7a1f08: https://github.com/mit-crpg/openmc/commit/7a1f08 +.. _c0e3ec: https://github.com/mit-crpg/openmc/commit/c0e3ec +.. _6f8d9d: https://github.com/mit-crpg/openmc/commit/6f8d9d +.. _6a3a5e: https://github.com/mit-crpg/openmc/commit/6a3a5e diff --git a/docs/source/releasenotes/0.5.1.rst b/docs/source/releasenotes/0.5.1.rst new file mode 100644 index 0000000000..6fa584fcf0 --- /dev/null +++ b/docs/source/releasenotes/0.5.1.rst @@ -0,0 +1,43 @@ +=================== +What's New in 0.5.1 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Absorption and combined estimators for k-effective. +- Natural elements can now be specified in materials using rather than + . +- Support for multiple S(a,b) tables in a single material (e.g. BeO). +- Test suite using Python nosetests. +- Proper install capability with 'make install'. +- Lattices can now be 2 or 3 dimensions. +- New scatter-PN score type. +- New kappa-fission score type. +- Ability to tally any reaction by specifying MT. + +--------- +Bug Fixes +--------- + +- 94103e_: Two checks for outgoing energy filters. +- e77059_: Fix reaction name for MT=849. +- b0fe88_: Fix distance to surface for cones. +- 63bfd2_: Fix tracklength tallies with cell filter and universes. +- 88daf7_: Fix analog tallies with survival biasing. + +.. _94103e: https://github.com/mit-crpg/openmc/commit/94103e +.. _e77059: https://github.com/mit-crpg/openmc/commit/e77059 +.. _b0fe88: https://github.com/mit-crpg/openmc/commit/b0fe88 +.. _63bfd2: https://github.com/mit-crpg/openmc/commit/63bfd2 +.. _88daf7: https://github.com/mit-crpg/openmc/commit/88daf7 diff --git a/docs/source/releasenotes/0.5.2.rst b/docs/source/releasenotes/0.5.2.rst new file mode 100644 index 0000000000..ba6374ea7c --- /dev/null +++ b/docs/source/releasenotes/0.5.2.rst @@ -0,0 +1,55 @@ +=================== +What's New in 0.5.2 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Python script for mesh tally plotting +- Isotopic abundances based on IUPAC 2009 when using +- Particle restart files for debugging +- Code will abort after certain number of lost particles (defaults to 10) +- Region outside lattice can be filled with material (void by default) +- 3D voxel plots +- Full HDF5/PHDF5 support (including support in statepoint.py) +- Cell overlap checking with -g command line flag (or when plotting) + +--------- +Bug Fixes +--------- + +- 7632f3_: Fixed bug in statepoint.py for multiple generations per batch. +- f85ac4_: Fix infinite loop bug in error module. +- 49c36b_: Don't convert surface ids if surface filter is for current tallies. +- 5ccc78_: Fix bug in reassignment of bins for mesh filter. +- b1f52f_: Fixed bug in plot color specification. +- eae7e5_: Fixed many memory leaks. +- 10c1cc_: Minor CMFD fixes. +- afdb50_: Add compatibility for XML comments without whitespace. +- a3c593_: Fixed bug in use of free gas scattering for H-1. +- 3a66e3_: Fixed bug in 2D mesh tally implementation. +- ab0793_: Corrected PETSC_NULL references to their correct types. +- 182ebd_: Use analog estimator with energyout filter. + +.. _7632f3: https://github.com/mit-crpg/openmc/commit/7632f3 +.. _f85ac4: https://github.com/mit-crpg/openmc/commit/f85ac4 +.. _49c36b: https://github.com/mit-crpg/openmc/commit/49c36b +.. _5ccc78: https://github.com/mit-crpg/openmc/commit/5ccc78 +.. _b1f52f: https://github.com/mit-crpg/openmc/commit/b1f52f +.. _eae7e5: https://github.com/mit-crpg/openmc/commit/eae7e5 +.. _10c1cc: https://github.com/mit-crpg/openmc/commit/10c1cc +.. _afdb50: https://github.com/mit-crpg/openmc/commit/afdb50 +.. _a3c593: https://github.com/mit-crpg/openmc/commit/a3c593 +.. _3a66e3: https://github.com/mit-crpg/openmc/commit/3a66e3 +.. _ab0793: https://github.com/mit-crpg/openmc/commit/ab0793 +.. _182ebd: https://github.com/mit-crpg/openmc/commit/182ebd diff --git a/docs/source/releasenotes/0.5.3.rst b/docs/source/releasenotes/0.5.3.rst new file mode 100644 index 0000000000..a7305da716 --- /dev/null +++ b/docs/source/releasenotes/0.5.3.rst @@ -0,0 +1,47 @@ +=================== +What's New in 0.5.3 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Output interface enhanced to allow multiple files handles to be opened +- Particle restart file linked to output interface +- Particle restarts and state point restarts are both identified with the -r + command line flag. +- Particle instance no longer global, passed to all physics routines +- Physics routines refactored to rely less on global memory, more arguments + passed in +- CMFD routines refactored and now can compute dominance ratio on the fly +- PETSc 3.4.2 or higher must be used and compiled with fortran datatype support +- Memory leaks fixed except for ones from xml-fortran package +- Test suite enhanced to test output with different compiler options +- Description of OpenMC development workflow added +- OpenMP shared-memory parallelism added +- Special run mode --tallies removed. + +--------- +Bug Fixes +--------- + +- 2b1e8a_: Normalize direction vector after reflecting particle. +- 5853d2_: Set blank default for cross section listing alias. +- e178c7_: Fix infinite loop with words greater than 80 characters in write_message. +- c18a6e_: Check for valid secondary mode on S(a,b) tables. +- 82c456_: Fix bug where last process could have zero particles. + +.. _2b1e8a: https://github.com/mit-crpg/openmc/commit/2b1e8a +.. _5853d2: https://github.com/mit-crpg/openmc/commit/5853d2 +.. _e178c7: https://github.com/mit-crpg/openmc/commit/e178c7 +.. _c18a6e: https://github.com/mit-crpg/openmc/commit/c18a6e +.. _82c456: https://github.com/mit-crpg/openmc/commit/82c456 diff --git a/docs/source/releasenotes/0.5.4.rst b/docs/source/releasenotes/0.5.4.rst new file mode 100644 index 0000000000..36d9ec4d57 --- /dev/null +++ b/docs/source/releasenotes/0.5.4.rst @@ -0,0 +1,62 @@ +=================== +What's New in 0.5.4 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Source sites outside geometry are resampled +- XML-Fortran backend replaced by FoX XML +- Ability to write particle track files +- Handle lost particles more gracefully (via particle track files) +- Multiple random number generator streams +- Mesh tally plotting utility converted to use Tkinter rather than PyQt +- Script added to download ACE data from NNDC +- Mixed ASCII/binary cross_sections.xml now allowed +- Expanded options for writing source bank +- Re-enabled ability to use source file as starting source +- S(a,b) recalculation avoided when same nuclide and S(a,b) table are accessed + +--------- +Bug Fixes +--------- + +- 32c03c_: Check for valid data in cross_sections.xml +- c71ef5_: Fix bug in statepoint.py +- 8884fb_: Check for all ZAIDs for S(a,b) tables +- b38af0_: Fix XML reading on multiple levels of input +- d28750_: Fix bug in convert_xsdir.py +- cf567c_: ENDF/B-VI data checked for compatibility +- 6b9461_: Fix p_valid sampling inside of sample_energy + +.. _32c03c: https://github.com/mit-crpg/openmc/commit/32c03c +.. _c71ef5: https://github.com/mit-crpg/openmc/commit/c71ef5 +.. _8884fb: https://github.com/mit-crpg/openmc/commit/8884fb +.. _b38af0: https://github.com/mit-crpg/openmc/commit/b38af0 +.. _d28750: https://github.com/mit-crpg/openmc/commit/d28750 +.. _cf567c: https://github.com/mit-crpg/openmc/commit/cf567c +.. _6b9461: https://github.com/mit-crpg/openmc/commit/6b9461 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Nick Horelik `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Tuomas Viitanen `_ +- `Jon Walsh `_ diff --git a/docs/source/releasenotes/0.6.0.rst b/docs/source/releasenotes/0.6.0.rst new file mode 100644 index 0000000000..63e234583c --- /dev/null +++ b/docs/source/releasenotes/0.6.0.rst @@ -0,0 +1,57 @@ +=================== +What's New in 0.6.0 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Legendre and spherical harmonic expansion tally scores +- CMake is now default build system +- Regression test suite based on CTests and NNDC cross sections +- FoX is now a git submodule +- Support for older cross sections (e.g. MCNP 66c) +- Progress bar for plots +- Expanded support for natural elements via in settings.xml + +--------- +Bug Fixes +--------- + +- 41f7ca_: Fixed erroneous results from survival biasing +- 038736_: Fix tallies over void materials +- 46f9e8_: Check for negative values in probability tables +- d1ca35_: Fixed sampling of angular distribution +- 0291c0_: Fixed indexing error in plotting +- d7a7d0_: Fix bug with specifying xs attribute +- 85b3cb_: Fix out-of-bounds error with OpenMP threading + +.. _41f7ca: https://github.com/mit-crpg/openmc/commit/41f7ca +.. _038736: https://github.com/mit-crpg/openmc/commit/038736 +.. _46f9e8: https://github.com/mit-crpg/openmc/commit/46f9e8 +.. _d1ca35: https://github.com/mit-crpg/openmc/commit/d1ca35 +.. _0291c0: https://github.com/mit-crpg/openmc/commit/0291c0 +.. _d7a7d0: https://github.com/mit-crpg/openmc/commit/d7a7d0 +.. _85b3cb: https://github.com/mit-crpg/openmc/commit/85b3cb + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Nick Horelik `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Jon Walsh `_ diff --git a/docs/source/releasenotes/0.6.1.rst b/docs/source/releasenotes/0.6.1.rst new file mode 100644 index 0000000000..a8c59aa519 --- /dev/null +++ b/docs/source/releasenotes/0.6.1.rst @@ -0,0 +1,63 @@ +=================== +What's New in 0.6.1 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Coarse mesh finite difference (CMFD) acceleration no longer requires PETSc +- Statepoint file numbering is now zero-padded +- Python scripts now compatible with Python 2 or 3 +- Ability to run particle restarts in fixed source calculations +- Capability to filter box source by fissionable materials +- Nuclide/element names are now case insensitive in input files +- Improved treatment of resonance scattering for heavy nuclides + +--------- +Bug Fixes +--------- + +- 03e890_: Check for energy-dependent multiplicities in ACE files +- 4439de_: Fix distance-to-surface calculation for general plane surface +- 5808ed_: Account for differences in URR band probabilities at different energies +- 2e60c0_: Allow zero atom/weight percents in materials +- 3e0870_: Don't use PWD environment variable when setting path to input files +- dc4776_: Handle probability table resampling correctly +- 01178b_: Fix metastables nuclides in NNDC cross_sections.xml file +- 62ec43_: Don't read tallies.xml when OpenMC is run in plotting mode +- 2a95ef_: Prevent segmentation fault on "current" score without mesh filter +- 93e482_: Check for negative values in probability tables + +.. _03e890: https://github.com/mit-crpg/openmc/commit/03e890 +.. _4439de: https://github.com/mit-crpg/openmc/commit/4439de +.. _5808ed: https://github.com/mit-crpg/openmc/commit/5808ed +.. _2e60c0: https://github.com/mit-crpg/openmc/commit/2e60c0 +.. _3e0870: https://github.com/mit-crpg/openmc/commit/3e0870 +.. _dc4776: https://github.com/mit-crpg/openmc/commit/dc4776 +.. _01178b: https://github.com/mit-crpg/openmc/commit/01178b +.. _62ec43: https://github.com/mit-crpg/openmc/commit/62ec43 +.. _2a95ef: https://github.com/mit-crpg/openmc/commit/2a95ef +.. _93e482: https://github.com/mit-crpg/openmc/commit/93e482 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Jon Walsh `_ +- `Will Boyd `_ diff --git a/docs/source/releasenotes/0.6.2.rst b/docs/source/releasenotes/0.6.2.rst new file mode 100644 index 0000000000..6fe6c28dac --- /dev/null +++ b/docs/source/releasenotes/0.6.2.rst @@ -0,0 +1,56 @@ +=================== +What's New in 0.6.2 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Meshline plotting capability +- Support for plotting cells/materials on middle universe levels +- Ability to model cells with no surfaces +- Compatibility with PETSc 3.5 +- Compatability with OpenMPI 1.7/1.8 +- Improved overall performance via logarithmic-mapped energy grid search +- Improved multi-threaded performance with atomic operations +- Support for fixed source problems with fissionable materials + +--------- +Bug Fixes +--------- + +- 26fb93_: Fix problem with -t, --track command-line flag +- 2f07c0_: Improved evaporation spectrum algorithm +- e6abb9_: Fix segfault when tallying in a void material +- 291b45_: Handle metastable nuclides in NNDC data and multiplicities in MT=5 data + +.. _26fb93: https://github.com/mit-crpg/openmc/commit/26fb93 +.. _2f07c0: https://github.com/mit-crpg/openmc/commit/2f07c0 +.. _e6abb9: https://github.com/mit-crpg/openmc/commit/e6abb9 +.. _291b45: https://github.com/mit-crpg/openmc/commit/291b45 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Will Boyd `_ +- `Matt Ellis `_ +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Nicholas Horelik `_ +- `Anton Leontiev `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Jon Walsh `_ +- `John Xia `_ diff --git a/docs/source/releasenotes/0.7.0.rst b/docs/source/releasenotes/0.7.0.rst new file mode 100644 index 0000000000..8f86631ea5 --- /dev/null +++ b/docs/source/releasenotes/0.7.0.rst @@ -0,0 +1,65 @@ +=================== +What's New in 0.7.0 +=================== + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Complete Python API +- Python 3 compatability for all scripts +- All scripts consistently named openmc-* and installed together +- New 'distribcell' tally filter for repeated cells +- Ability to specify outer lattice universe +- XML input validation utility (openmc-validate-xml) +- Support for hexagonal lattices +- Material union energy grid method +- Tally triggers +- Remove dependence on PETSc +- Significant OpenMP performance improvements +- Support for Fortran 2008 MPI interface +- Use of Travis CI for continuous integration +- Simplifications and improvements to test suite + +--------- +Bug Fixes +--------- + +- b5f712_: Fix bug in spherical harmonics tallies +- e6675b_: Ensure all constants are double precision +- 04e2c1_: Fix potential bug in sample_nuclide routine +- 6121d9_: Fix bugs related to particle track files +- 2f0e89_: Fixes for nuclide specification in tallies + +.. _b5f712: https://github.com/mit-crpg/openmc/commit/b5f712 +.. _e6675b: https://github.com/mit-crpg/openmc/commit/e6675b +.. _04e2c1: https://github.com/mit-crpg/openmc/commit/04e2c1 +.. _6121d9: https://github.com/mit-crpg/openmc/commit/6121d9 +.. _2f0e89: https://github.com/mit-crpg/openmc/commit/2f0e89 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Will Boyd `_ +- `Matt Ellis `_ +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Nicholas Horelik `_ +- `Colin Josey `_ +- `William Lyu `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Anthony Scopatz `_ +- `Jon Walsh `_ diff --git a/docs/source/releasenotes/0.7.1.rst b/docs/source/releasenotes/0.7.1.rst new file mode 100644 index 0000000000..5ee2c9f69f --- /dev/null +++ b/docs/source/releasenotes/0.7.1.rst @@ -0,0 +1,89 @@ +=================== +What's New in 0.7.1 +=================== + +This release of OpenMC provides some substantial improvements over version +0.7.0. Non-simple cell regions can now be defined through the ``|`` (union) and +``~`` (complement) operators. Similar changes in the Python API also allow +complex cell regions to be defined. A true secondary particle bank now exists; +this is crucial for photon transport (to be added in the next minor release). A +rich API for multi-group cross section generation has been added via the +``openmc.mgxs`` Python module. + +Various improvements to tallies have also been made. It is now possible to +explicitly specify that a collision estimator be used in a tally. A new +``delayedgroup`` filter and ``delayed-nu-fission`` score allow a user to obtain +delayed fission neutron production rates filtered by delayed group. Finally, the +new ``inverse-velocity`` score may be useful for calculating kinetics +parameters. + +.. caution:: In previous versions, depending on how OpenMC was compiled binary + output was either given in HDF5 or a flat binary format. With this + version, all binary output is now HDF5 which means you **must** + have HDF5 in order to install OpenMC. Please consult the user's + guide for instructions on how to compile with HDF5. + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions, Mac OS X, +and Microsoft Windows 7. Memory requirements will vary depending on the size of +the problem at hand (mostly on the number of nuclides in the problem). + +------------ +New Features +------------ + +- Support for complex cell regions (union and complement operators) +- Generic quadric surface type +- Improved handling of secondary particles +- Binary output is now solely HDF5 +- ``openmc.mgxs`` Python module enabling multi-group cross section generation +- Collision estimator for tallies +- Delayed fission neutron production tallies with ability to filter by delayed + group +- Inverse velocity tally score +- Performance improvements for binary search +- Performance improvements for reaction rate tallies + +--------- +Bug Fixes +--------- + +- 299322_: Bug with material filter when void material present +- d74840_: Fix triggers on tallies with multiple filters +- c29a81_: Correctly handle maximum transport energy +- 3edc23_: Fixes in the nu-scatter score +- 629e3b_: Assume unspecified surface coefficients are zero in Python API +- 5dbe8b_: Fix energy filters for openmc-plot-mesh-tally +- ff66f4_: Fixes in the openmc-plot-mesh-tally script +- 441fd4_: Fix bug in kappa-fission score +- 7e5974_: Allow fixed source simulations from Python API + +.. _299322: https://github.com/mit-crpg/openmc/commit/299322 +.. _d74840: https://github.com/mit-crpg/openmc/commit/d74840 +.. _c29a81: https://github.com/mit-crpg/openmc/commit/c29a81 +.. _3edc23: https://github.com/mit-crpg/openmc/commit/3edc23 +.. _629e3b: https://github.com/mit-crpg/openmc/commit/629e3b +.. _5dbe8b: https://github.com/mit-crpg/openmc/commit/5dbe8b +.. _ff66f4: https://github.com/mit-crpg/openmc/commit/ff66f4 +.. _441fd4: https://github.com/mit-crpg/openmc/commit/441fd4 +.. _7e5974: https://github.com/mit-crpg/openmc/commit/7e5974 + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Will Boyd `_ +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Colin Josey `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Kelly Rowland `_ +- `Sam Shaner `_ +- `Jon Walsh `_ diff --git a/docs/source/releasenotes/0.8.0.rst b/docs/source/releasenotes/0.8.0.rst new file mode 100644 index 0000000000..bd8a89a64a --- /dev/null +++ b/docs/source/releasenotes/0.8.0.rst @@ -0,0 +1,94 @@ +=================== +What's New in 0.8.0 +=================== + +This release of OpenMC includes a few new major features including the +capability to perform neutron transport with multi-group cross section data as +well as experimental support for the windowed multipole method being developed +at MIT. Source sampling options have also been expanded significantly, with the +option to supply arbitrary tabular and discrete distributions for energy, angle, +and spatial coordinates. + +The Python API has been significantly restructured in this release compared to +version 0.7.1. Any scripts written based on the version 0.7.1 API will likely +need to be rewritten. Some of the most visible changes include the following: + +- ``SettingsFile`` is now ``Settings``, ``MaterialsFile`` is now ``Materials``, + and ``TalliesFile`` is now ``Tallies``. +- The ``GeometryFile`` class no longer exists and is replaced by the + ``Geometry`` class which now has an ``export_to_xml()`` method. +- Source distributions are defined using the ``Source`` class and assigned to + the ``Settings.source`` property. +- The ``Executor`` class no longer exists and is replaced by ``openmc.run()`` + and ``openmc.plot_geometry()`` functions. + +The Python API documentation has also been significantly expanded. + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions and Mac +OS X. Numerous users have reported working builds on Microsoft Windows, but your +mileage may vary. Memory requirements will vary depending on the size of the +problem at hand (mostly on the number of nuclides and tallies in the problem). + +------------ +New Features +------------ + +- Multi-group mode +- Vast improvements to the Python API +- Experimental windowed multipole capability +- Periodic boundary conditions +- Expanded source sampling options +- Distributed materials +- Subcritical multiplication support +- Improved method for reproducible URR table sampling +- Refactor of continuous-energy reaction data +- Improved documentation and new Jupyter notebooks + +--------- +Bug Fixes +--------- + +- 70daa7_: Make sure MT=3 cross section is not used +- 40b05f_: Ensure source bank is resampled for fixed source runs +- 9586ed_: Fix two hexagonal lattice bugs +- a855e8_: Make sure graphite models don't error out on max events +- 7294a1_: Fix incorrect check on cmfd.xml +- 12f246_: Ensure number of realizations is written to statepoint +- 0227f4_: Fix bug when sampling multiple energy distributions +- 51deaa_: Prevent segfault when user specifies '18' on tally scores +- fed74b_: Prevent duplicate tally scores +- 8467ae_: Better threshold for allowable lost particles +- 493c6f_: Fix type of return argument for h5pget_driver_f + +.. _70daa7: https://github.com/mit-crpg/openmc/commit/70daa7 +.. _40b05f: https://github.com/mit-crpg/openmc/commit/40b05f +.. _9586ed: https://github.com/mit-crpg/openmc/commit/9586ed +.. _a855e8: https://github.com/mit-crpg/openmc/commit/a855e8 +.. _7294a1: https://github.com/mit-crpg/openmc/commit/7294a1 +.. _12f246: https://github.com/mit-crpg/openmc/commit/12f246 +.. _0227f4: https://github.com/mit-crpg/openmc/commit/0227f4 +.. _51deaa: https://github.com/mit-crpg/openmc/commit/51deaa +.. _fed74b: https://github.com/mit-crpg/openmc/commit/fed74b +.. _8467ae: https://github.com/mit-crpg/openmc/commit/8467ae +.. _493c6f: https://github.com/mit-crpg/openmc/commit/493c6f + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Will Boyd `_ +- `Derek Gaston `_ +- `Sterling Harper `_ +- `Colin Josey `_ +- `Jingang Liang `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Kelly Rowland `_ +- `Sam Shaner `_ diff --git a/docs/source/releasenotes/0.9.0.rst b/docs/source/releasenotes/0.9.0.rst new file mode 100644 index 0000000000..478c570962 --- /dev/null +++ b/docs/source/releasenotes/0.9.0.rst @@ -0,0 +1,150 @@ +=================== +What's New in 0.9.0 +=================== + +.. currentmodule:: openmc + +This release of OpenMC is the first release to use a new native HDF5 cross +section format rather than ACE format cross sections. Other significant new +features include a nuclear data interface in the Python API (:mod:`openmc.data`) +a stochastic volume calculation capability, a random sphere packing algorithm +that can handle packing fractions up to 60%, and a new XML parser with +significantly better performance than the parser used previously. + +.. caution:: With the new cross section format, the default energy units are now + **electronvolts (eV)** rather than megaelectronvolts (MeV)! If you + are specifying an energy filter for a tally, make sure you use + units of eV now. + +The Python API continues to improve over time; several backwards incompatible +changes were made in the API which users of previous versions should take note +of: + +- Each type of tally filter is now specified with a separate class. For example:: + + energy_filter = openmc.EnergyFilter([0.0, 0.625, 4.0, 1.0e6, 20.0e6]) + +- Several attributes of the :class:`Plot` class have changed (``color`` -> + ``color_by`` and ``col_spec`` > ``colors``). :attr:`Plot.colors` now accepts a + dictionary mapping :class:`Cell` or :class:`Material` instances to RGB + 3-tuples or string colors names, e.g.:: + + plot.colors = { + fuel: 'yellow', + water: 'blue' + } + +- ``make_hexagon_region`` is now :func:`get_hexagonal_prism` +- Several changes in :class:`Settings` attributes: + + - ``weight`` is now set as ``Settings.cutoff['weight']`` + - Shannon entropy is now specified by passing a :class:`openmc.Mesh` to + :attr:`Settings.entropy_mesh` + - Uniform fission site method is now specified by passing a + :class:`openmc.Mesh` to :attr:`Settings.ufs_mesh` + - All ``sourcepoint_*`` options are now specified in a + :attr:`Settings.sourcepoint` dictionary + - Resonance scattering method is now specified as a dictionary in + :attr:`Settings.resonance_scattering` + - Multipole is now turned on by setting ``Settings.temperature['multipole'] = + True`` + - The ``output_path`` attribute is now ``Settings.output['path']`` + +- All the ``openmc.mgxs.Nu*`` classes are gone. Instead, a ``nu`` argument was + added to the constructor of the corresponding classes. + +------------------- +System Requirements +------------------- + +There are no special requirements for running the OpenMC code. As of this +release, OpenMC has been tested on a variety of Linux distributions and Mac +OS X. Numerous users have reported working builds on Microsoft Windows, but your +mileage may vary. Memory requirements will vary depending on the size of the +problem at hand (mostly on the number of nuclides and tallies in the problem). + +------------ +New Features +------------ + +- Stochastic volume calculations +- Multi-delayed group cross section generation +- Ability to calculate multi-group cross sections over meshes +- Temperature interpolation on cross section data +- Nuclear data interface in Python API, :mod:`openmc.data` +- Allow cutoff energy via :attr:`Settings.cutoff` +- Ability to define fuel by enrichment (see :meth:`Material.add_element`) +- Random sphere packing for TRISO particle generation, + :func:`openmc.model.pack_trisos` +- Critical eigenvalue search, :func:`openmc.search_for_keff` +- Model container, :class:`openmc.model.Model` +- In-line plotting in Jupyter, :func:`openmc.plot_inline` +- Energy function tally filters, :class:`openmc.EnergyFunctionFilter` +- Replaced FoX XML parser with `pugixml `_ +- Cell/material instance counting, :meth:`Geometry.determine_paths` +- Differential tallies (see :class:`openmc.TallyDerivative`) +- Consistent multi-group scattering matrices +- Improved documentation and new Jupyter notebooks +- OpenMOC compatibility module, :mod:`openmc.openmoc_compatible` + +--------- +Bug Fixes +--------- + +- c5df6c_: Fix mesh filter max iterator check +- 1cfa39_: Reject external source only if 95% of sites are rejected +- 335359_: Fix bug in plotting meshlines +- 17c678_: Make sure system_clock uses high-resolution timer +- 23ec0b_: Fix use of S(a,b) with multipole data +- 7eefb7_: Fix several bugs in tally module +- 7880d4_: Allow plotting calculation with no boundary conditions +- ad2d9f_: Fix filter weight missing when scoring all nuclides +- 59fdca_: Fix use of source files for fixed source calculations +- 9eff5b_: Fix thermal scattering bugs +- 7848a9_: Fix combined k-eff estimator producing NaN +- f139ce_: Fix printing bug for tallies with AggregateNuclide +- b8ddfa_: Bugfix for short tracks near tally mesh edges +- ec3cfb_: Fix inconsistency in filter weights +- 5e9b06_: Fix XML representation for verbosity +- c39990_: Fix bug tallying reaction rates with multipole on +- c6b67e_: Fix fissionable source sampling bug +- 489540_: Check for void materials in tracklength tallies +- f0214f_: Fixes/improvements to the ARES algorithm + +.. _c5df6c: https://github.com/mit-crpg/openmc/commit/c5df6c +.. _1cfa39: https://github.com/mit-crpg/openmc/commit/1cfa39 +.. _335359: https://github.com/mit-crpg/openmc/commit/335359 +.. _17c678: https://github.com/mit-crpg/openmc/commit/17c678 +.. _23ec0b: https://github.com/mit-crpg/openmc/commit/23ec0b +.. _7eefb7: https://github.com/mit-crpg/openmc/commit/7eefb7 +.. _7880d4: https://github.com/mit-crpg/openmc/commit/7880d4 +.. _ad2d9f: https://github.com/mit-crpg/openmc/commit/ad2d9f +.. _59fdca: https://github.com/mit-crpg/openmc/commit/59fdca +.. _9eff5b: https://github.com/mit-crpg/openmc/commit/9eff5b +.. _7848a9: https://github.com/mit-crpg/openmc/commit/7848a9 +.. _f139ce: https://github.com/mit-crpg/openmc/commit/f139ce +.. _b8ddfa: https://github.com/mit-crpg/openmc/commit/b8ddfa +.. _ec3cfb: https://github.com/mit-crpg/openmc/commit/ec3cfb +.. _5e9b06: https://github.com/mit-crpg/openmc/commit/5e9b06 +.. _c39990: https://github.com/mit-crpg/openmc/commit/c39990 +.. _c6b67e: https://github.com/mit-crpg/openmc/commit/c6b67e +.. _489540: https://github.com/mit-crpg/openmc/commit/489540 +.. _f0214f: https://github.com/mit-crpg/openmc/commit/f0214f + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Will Boyd `_ +- `Sterling Harper `_ +- `Qingming He <906459647@qq.com>`_ +- `Colin Josey `_ +- `Travis Labossiere-Hickman `_ +- `Jingang Liang `_ +- `Amanda Lund `_ +- `Adam Nelson `_ +- `Paul Romano `_ +- `Sam Shaner `_ +- `Jon Walsh `_ diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst new file mode 100644 index 0000000000..320ea9ca05 --- /dev/null +++ b/docs/source/releasenotes/index.rst @@ -0,0 +1,28 @@ +.. _releasenotes: + +============= +Release Notes +============= + +.. toctree:: + :maxdepth: 1 + + 0.11.0 + 0.10.0 + 0.9.0 + 0.8.0 + 0.7.1 + 0.7.0 + 0.6.2 + 0.6.1 + 0.6.0 + 0.5.4 + 0.5.3 + 0.5.2 + 0.5.1 + 0.5.0 + 0.4.4 + 0.4.3 + 0.4.2 + 0.4.1 + 0.4.0 diff --git a/docs/source/usersguide/basics.rst b/docs/source/usersguide/basics.rst index 2dd91ee0fa..dbf63136cd 100644 --- a/docs/source/usersguide/basics.rst +++ b/docs/source/usersguide/basics.rst @@ -100,7 +100,7 @@ that roughly correspond to elements in the XML files. For example, the :class:`openmc.Geometry` for ``geometry.xml``, :class:`openmc.Materials` for ``materials.xml``, :class:`openmc.Settings` for ``settings.xml``, and so on. To create a model then, one creates instances of these classes and then uses the -``export_to_xml()`` method, e.g. :meth:`Geometry.export_to_xml`. Most scripts +``export_to_xml()`` method, e.g., :meth:`Geometry.export_to_xml`. Most scripts that generate a full model will look something like the following: .. code-block:: Python @@ -120,7 +120,7 @@ that generate a full model will look something like the following: ... settings.export_to_xml() -One a model has been created and exported to XML, a simulation can be run either +Once a model has been created and exported to XML, a simulation can be run either by calling :ref:`scripts_openmc` directly from a shell or by using the :func:`openmc.run()` function from Python. diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst new file mode 100644 index 0000000000..5dd98406e6 --- /dev/null +++ b/docs/source/usersguide/depletion.rst @@ -0,0 +1,53 @@ +.. _usersguide_depletion: + +========= +Depletion +========= + +OpenMC supports coupled depletion, or burnup, calculations through the +:mod:`openmc.deplete` Python module. OpenMC solves the transport equation to +obtain transmutation reaction rates, and then the reaction rates are used to +solve a set of transmutation equations that determine the evolution of nuclide +densities within a material. The nuclide densities predicted as some future time +are then used to determine updated reaction rates, and the process is repeated +for as many timesteps as are requested. + +The depletion module is designed such that the flux/reaction rate solution (the +transport "operator") is completely isolated from the solution of the +transmutation equations and the method used for advancing time. At present, the +:mod:`openmc.deplete` module offers a single transport operator, +:class:`openmc.deplete.Operator` (which uses the OpenMC transport solver), but +in principle additional operator classes based on other transport codes could be +implemented and no changes to the depletion solver itself would be needed. The +operator class requires a :class:`openmc.Geometry` instance and a +:class:`openmc.Settings` instance:: + + geom = openmc.Geometry() + settings = openmc.Settings() + ... + + op = openmc.deplete.Operator(geom, settings) + +:mod:`openmc.deplete` supports multiple time-integration methods for determining +material compositions over time. Each method appears as a different function. +For example, :func:`openmc.deplete.integrator.cecm` runs a depletion calculation +using the CE/CM algorithm (deplete over a timestep using the middle-of-step +reaction rates). An instance of :class:`openmc.deplete.Operator` is passed to +one of these functions along with the power level and timesteps:: + + power = 1200.0e6 + days = 24*60*60 + timesteps = [10.0*days, 10.0*days, 10.0*days] + openmc.deplete.cecm(op, power, timesteps) + +The coupled transport-depletion problem is executed, and once it is done a +``depletion_results.h5`` file is written. The results can be analyzed using the +:class:`openmc.deplete.ResultsList` class. This class has methods that allow for +easy retrieval of k-effective, nuclide concentrations, and reaction rates over +time:: + + results = openmc.deplete.ResultsList("depletion_results.h5") + time, keff = results.get_eigenvalue() + +Note that the coupling between the transport solver and the transmutation solver +happens in-memory rather than by reading/writing files on disk. diff --git a/docs/source/usersguide/index.rst b/docs/source/usersguide/index.rst index d22acba505..fab353c773 100644 --- a/docs/source/usersguide/index.rst +++ b/docs/source/usersguide/index.rst @@ -20,6 +20,7 @@ essential aspects of using OpenMC to perform simulations. settings tallies plots + depletion scripts processing parallel diff --git a/examples/jupyter/mdgxs-part-ii.ipynb b/examples/jupyter/mdgxs-part-ii.ipynb index 4c2cc40486..23ee4d2514 100644 --- a/examples/jupyter/mdgxs-part-ii.ipynb +++ b/examples/jupyter/mdgxs-part-ii.ipynb @@ -22,9 +22,7 @@ { "cell_type": "code", "execution_count": 1, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -82,9 +80,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create a materials collection and export to XML\n", @@ -102,9 +98,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create cylinders for the fuel and clad\n", @@ -167,9 +161,7 @@ { "cell_type": "code", "execution_count": 6, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create a Universe to encapsulate a control rod guide tube\n", @@ -204,9 +196,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create fuel assembly Lattice\n", @@ -225,9 +215,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create array indices for guide tube locations in lattice\n", @@ -254,9 +242,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create root Cell\n", @@ -280,9 +266,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and export to XML\n", @@ -300,9 +284,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", @@ -336,9 +318,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -383,9 +363,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a 20-group EnergyGroups object\n", @@ -407,14 +385,11 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Instantiate a tally mesh \n", - "mesh = openmc.Mesh(mesh_id=1)\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh(mesh_id=1)\n", "mesh.dimension = [17, 17, 1]\n", "mesh.lower_left = [-10.71, -10.71, -10000.]\n", "mesh.width = [1.26, 1.26, 20000.]\n", @@ -464,9 +439,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -635,9 +608,7 @@ { "cell_type": "code", "execution_count": 16, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Load the last statepoint file\n", @@ -654,9 +625,7 @@ { "cell_type": "code", "execution_count": 17, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Initialize MGXS Library with OpenMC statepoint data\n", @@ -687,9 +656,7 @@ { "cell_type": "code", "execution_count": 18, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", @@ -923,9 +890,7 @@ { "cell_type": "code", "execution_count": 19, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1110,9 +1075,7 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", @@ -1189,7 +1152,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python [default]", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1203,9 +1166,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.5.2" + "version": "3.6.7" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } diff --git a/examples/jupyter/mg-mode-part-i.ipynb b/examples/jupyter/mg-mode-part-i.ipynb index 8e52787879..23227b1ed3 100644 --- a/examples/jupyter/mg-mode-part-i.ipynb +++ b/examples/jupyter/mg-mode-part-i.ipynb @@ -86,9 +86,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# The scattering matrix is ordered with incoming groups as rows and outgoing groups as columns\n", @@ -158,9 +156,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# For every cross section data set in the library, assign an openmc.Macroscopic object to a material\n", @@ -207,9 +203,7 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create the surface used for each pin\n", @@ -249,9 +243,7 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "lattices = {}\n", @@ -352,9 +344,7 @@ { "cell_type": "code", "execution_count": 9, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "lattices['Core'] = openmc.RectLattice(name='3x3 core lattice')\n", @@ -396,9 +386,7 @@ { "cell_type": "code", "execution_count": 10, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -426,9 +414,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Create Geometry and set root Universe\n", @@ -456,8 +442,7 @@ "tallies_file = openmc.Tallies()\n", "\n", "# Instantiate a tally Mesh\n", - "mesh = openmc.Mesh()\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh()\n", "mesh.dimension = [17 * 2, 17 * 2]\n", "mesh.lower_left = [-32.13, -10.71]\n", "mesh.upper_right = [+10.71, +32.13]\n", @@ -489,9 +474,7 @@ { "cell_type": "code", "execution_count": 13, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# OpenMC simulation parameters\n", @@ -533,9 +516,7 @@ { "cell_type": "code", "execution_count": 14, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -649,9 +630,7 @@ { "cell_type": "code", "execution_count": 15, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -714,7 +693,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/jupyter/mg-mode-part-ii.ipynb b/examples/jupyter/mg-mode-part-ii.ipynb index e2df6fd3db..85a8d73675 100644 --- a/examples/jupyter/mg-mode-part-ii.ipynb +++ b/examples/jupyter/mg-mode-part-ii.ipynb @@ -564,8 +564,7 @@ ], "source": [ "# Instantiate a tally Mesh\n", - "mesh = openmc.Mesh()\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh()\n", "mesh.dimension = [17, 17]\n", "mesh.lower_left = [-10.71, -10.71]\n", "mesh.upper_right = [+10.71, +10.71]\n", @@ -1518,9 +1517,9 @@ ], "metadata": { "kernelspec": { - "display_name": "openmc", + "display_name": "Python 3", "language": "python", - "name": "openmc" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1532,7 +1531,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/jupyter/mg-mode-part-iii.ipynb b/examples/jupyter/mg-mode-part-iii.ipynb index 7487b842c6..2cf9981889 100644 --- a/examples/jupyter/mg-mode-part-iii.ipynb +++ b/examples/jupyter/mg-mode-part-iii.ipynb @@ -545,8 +545,7 @@ "outputs": [], "source": [ "# Instantiate a tally Mesh\n", - "mesh = openmc.Mesh()\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh()\n", "mesh.dimension = [10, 10]\n", "mesh.lower_left = [0., 0.]\n", "mesh.upper_right = [length, length]\n", @@ -1417,9 +1416,9 @@ ], "metadata": { "kernelspec": { - "display_name": "openmc", + "display_name": "Python 3", "language": "python", - "name": "openmc" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1431,7 +1430,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/jupyter/mgxs-part-iii.ipynb b/examples/jupyter/mgxs-part-iii.ipynb index d3e90d0946..5ffb66d3a0 100644 --- a/examples/jupyter/mgxs-part-iii.ipynb +++ b/examples/jupyter/mgxs-part-iii.ipynb @@ -586,8 +586,7 @@ "outputs": [], "source": [ "# Instantiate a tally Mesh\n", - "mesh = openmc.Mesh(mesh_id=1)\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh(mesh_id=1)\n", "mesh.dimension = [17, 17]\n", "mesh.lower_left = [-10.71, -10.71]\n", "mesh.upper_right = [+10.71, +10.71]\n", @@ -1527,9 +1526,9 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "openmc", + "display_name": "Python 3", "language": "python", - "name": "openmc" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1541,7 +1540,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/jupyter/pandas-dataframes.ipynb b/examples/jupyter/pandas-dataframes.ipynb index b8b10311c4..80a0b880f9 100644 --- a/examples/jupyter/pandas-dataframes.ipynb +++ b/examples/jupyter/pandas-dataframes.ipynb @@ -341,8 +341,7 @@ "outputs": [], "source": [ "# Instantiate a tally Mesh\n", - "mesh = openmc.Mesh(mesh_id=1)\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh(mesh_id=1)\n", "mesh.dimension = [17, 17]\n", "mesh.lower_left = [-10.71, -10.71]\n", "mesh.width = [1.26, 1.26]\n", @@ -1986,7 +1985,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.6" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/jupyter/post-processing.ipynb b/examples/jupyter/post-processing.ipynb index 1223f67f33..a6d134b54b 100644 --- a/examples/jupyter/post-processing.ipynb +++ b/examples/jupyter/post-processing.ipynb @@ -319,7 +319,7 @@ "outputs": [], "source": [ "# Create mesh which will be used for tally\n", - "mesh = openmc.Mesh()\n", + "mesh = openmc.RegularMesh()\n", "mesh.dimension = [100, 100]\n", "mesh.lower_left = [-0.63, -0.63]\n", "mesh.upper_right = [0.63, 0.63]\n", diff --git a/examples/jupyter/tally-arithmetic.ipynb b/examples/jupyter/tally-arithmetic.ipynb index 2bccf73f2a..35c096ba4d 100644 --- a/examples/jupyter/tally-arithmetic.ipynb +++ b/examples/jupyter/tally-arithmetic.ipynb @@ -284,9 +284,7 @@ { "cell_type": "code", "execution_count": 11, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Run openmc in plotting mode\n", @@ -296,9 +294,7 @@ { "cell_type": "code", "execution_count": 12, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -376,8 +372,7 @@ "tallies_file.append(tally)\n", "\n", "# Instantiate a tally mesh\n", - "mesh = openmc.Mesh(mesh_id=1)\n", - "mesh.type = 'regular'\n", + "mesh = openmc.RegularMesh(mesh_id=1)\n", "mesh.dimension = [1, 1, 1]\n", "mesh.lower_left = [-0.63, -0.63, -100.]\n", "mesh.width = [1.26, 1.26, 200.]\n", @@ -485,9 +480,7 @@ { "cell_type": "code", "execution_count": 20, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stderr", @@ -518,7 +511,6 @@ "cell_type": "code", "execution_count": 21, "metadata": { - "collapsed": false, "scrolled": true }, "outputs": [ @@ -674,9 +666,7 @@ { "cell_type": "code", "execution_count": 23, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -740,9 +730,7 @@ { "cell_type": "code", "execution_count": 24, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -807,9 +795,7 @@ { "cell_type": "code", "execution_count": 25, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -873,9 +859,7 @@ { "cell_type": "code", "execution_count": 26, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -939,9 +923,7 @@ { "cell_type": "code", "execution_count": 27, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1004,9 +986,7 @@ { "cell_type": "code", "execution_count": 28, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1066,9 +1046,7 @@ { "cell_type": "code", "execution_count": 29, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1128,9 +1106,7 @@ { "cell_type": "code", "execution_count": 30, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1210,9 +1186,7 @@ { "cell_type": "code", "execution_count": 32, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1358,9 +1332,7 @@ { "cell_type": "code", "execution_count": 33, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1390,9 +1362,7 @@ { "cell_type": "code", "execution_count": 34, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1414,9 +1384,7 @@ { "cell_type": "code", "execution_count": 35, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1445,9 +1413,7 @@ { "cell_type": "code", "execution_count": 36, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1539,9 +1505,7 @@ { "cell_type": "code", "execution_count": 37, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [ { "data": { @@ -1709,7 +1673,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.0" + "version": "3.6.7" } }, "nbformat": 4, diff --git a/examples/python/lattice/nested/build-xml.py b/examples/python/lattice/nested/build-xml.py index 37483a9397..06641f5ac5 100644 --- a/examples/python/lattice/nested/build-xml.py +++ b/examples/python/lattice/nested/build-xml.py @@ -150,8 +150,7 @@ plot_file.export_to_xml() ############################################################################### # Instantiate a tally mesh -mesh = openmc.Mesh(mesh_id=1) -mesh.type = 'regular' +mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [4, 4] mesh.lower_left = [-2, -2] mesh.width = [1, 1] @@ -164,6 +163,7 @@ tally = openmc.Tally(tally_id=1) tally.filters = [mesh_filter] tally.scores = ['total'] -# Instantiate a Tallies collection, register Tally/Mesh, and export to XML +# Instantiate a Tallies collection, register Tally/RegularMesh, and export to +# XML tallies_file = openmc.Tallies([tally]) tallies_file.export_to_xml() diff --git a/examples/python/lattice/simple/build-xml.py b/examples/python/lattice/simple/build-xml.py index 1cdfa24f3b..17544b87f5 100644 --- a/examples/python/lattice/simple/build-xml.py +++ b/examples/python/lattice/simple/build-xml.py @@ -143,8 +143,7 @@ plot_file.export_to_xml() ############################################################################### # Instantiate a tally mesh -mesh = openmc.Mesh(mesh_id=1) -mesh.type = 'regular' +mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [4, 4] mesh.lower_left = [-2, -2] mesh.width = [1, 1] diff --git a/examples/python/pincell/build-xml.py b/examples/python/pincell/build-xml.py index c728d5f36c..072cc911f0 100644 --- a/examples/python/pincell/build-xml.py +++ b/examples/python/pincell/build-xml.py @@ -106,7 +106,7 @@ 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 = openmc.RegularMesh() 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] @@ -119,8 +119,7 @@ settings_file.export_to_xml() ############################################################################### # Instantiate a tally mesh -mesh = openmc.Mesh() -mesh.type = 'regular' +mesh = openmc.RegularMesh() mesh.dimension = [100, 100, 1] mesh.lower_left = [-0.62992, -0.62992, -1.e50] mesh.upper_right = [0.62992, 0.62992, 1.e50] diff --git a/examples/python/pincell_depletion/restart_depletion.py b/examples/python/pincell_depletion/restart_depletion.py index f9324a8cb8..d5a0e2234a 100644 --- a/examples/python/pincell_depletion/restart_depletion.py +++ b/examples/python/pincell_depletion/restart_depletion.py @@ -47,7 +47,7 @@ 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 = openmc.RegularMesh() 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] diff --git a/examples/python/pincell_depletion/run_depletion.py b/examples/python/pincell_depletion/run_depletion.py index c1ad9d20aa..5a018cda10 100644 --- a/examples/python/pincell_depletion/run_depletion.py +++ b/examples/python/pincell_depletion/run_depletion.py @@ -119,7 +119,7 @@ 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 = openmc.RegularMesh() 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] diff --git a/examples/python/pincell_multigroup/build-xml.py b/examples/python/pincell_multigroup/build-xml.py index 995cd87db5..bb9fc78606 100644 --- a/examples/python/pincell_multigroup/build-xml.py +++ b/examples/python/pincell_multigroup/build-xml.py @@ -155,8 +155,7 @@ settings_file.export_to_xml() ############################################################################### # Instantiate a tally mesh -mesh = openmc.Mesh(mesh_id=1) -mesh.type = 'regular' +mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [100, 100, 1] mesh.lower_left = [-0.63, -0.63, -1.e50] mesh.upper_right = [0.63, 0.63, 1.e50] diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 1f6686df1a..9f05a56dcc 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -43,6 +43,7 @@ extern "C" { int openmc_global_tallies(double** ptr); int openmc_hard_reset(); int openmc_init(int argc, char* argv[], const void* intracomm); + bool openmc_is_statepoint_batch(); int openmc_legendre_filter_get_order(int32_t index, int* order); int openmc_legendre_filter_set_order(int32_t index, int order); int openmc_load_nuclide(const char* name); @@ -50,6 +51,7 @@ extern "C" { int openmc_material_get_densities(int32_t index, int** nuclides, double** densities, int* n); int openmc_material_get_id(int32_t index, int32_t* id); int openmc_material_get_fissionable(int32_t index, bool* fissionable); + int openmc_material_get_density(int32_t index, double* density); int openmc_material_get_volume(int32_t index, double* volume); int openmc_material_set_density(int32_t index, double density, const char* units); int openmc_material_set_densities(int32_t index, int n, const char** name, const double* density); diff --git a/include/openmc/cell.h b/include/openmc/cell.h index cc4429b44f..ee12305951 100644 --- a/include/openmc/cell.h +++ b/include/openmc/cell.h @@ -10,9 +10,7 @@ #include "hdf5.h" #include "pugixml.hpp" -#ifdef DAGMC -#include "DagMC.hpp" -#endif +#include "dagmc.h" #include "openmc/constants.h" #include "openmc/neighbor_list.h" diff --git a/include/openmc/constants.h b/include/openmc/constants.h index 49f980cc29..6b2e6f63b7 100644 --- a/include/openmc/constants.h +++ b/include/openmc/constants.h @@ -20,8 +20,9 @@ using double_4dvec = std::vector>>>; // OpenMC major, minor, and release numbers constexpr int VERSION_MAJOR {0}; -constexpr int VERSION_MINOR {10}; +constexpr int VERSION_MINOR {11}; constexpr int VERSION_RELEASE {0}; +constexpr bool VERSION_DEV {true}; constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; // HDF5 data format diff --git a/include/openmc/dagmc.h b/include/openmc/dagmc.h index 508b617d1e..d3012e3a0a 100644 --- a/include/openmc/dagmc.h +++ b/include/openmc/dagmc.h @@ -9,11 +9,19 @@ extern "C" const bool dagmc_enabled; #ifdef DAGMC #include "DagMC.hpp" -#include "openmc/cell.h" -#include "openmc/surface.h" +#include "openmc/xml_interface.h" +#include "openmc/position.h" namespace openmc { +namespace simulation { + +extern moab::DagMC::RayHistory history; //!< facet history for DagMC particles +extern Direction last_dir; //!< last direction passed to DagMC's ray_fire +#pragma omp threadprivate(history, last_dir) + +} + namespace model { extern moab::DagMC* DAG; } diff --git a/include/openmc/error.h b/include/openmc/error.h index 5f30f0252d..32ed36c8b8 100644 --- a/include/openmc/error.h +++ b/include/openmc/error.h @@ -44,7 +44,7 @@ void fatal_error(const std::stringstream& message) [[noreturn]] inline void fatal_error(const char* message) { - fatal_error({message, std::strlen(message)}); + fatal_error(std::string{message, std::strlen(message)}); } void warning(const std::string& message); diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 8e6e8a82ef..e386348c44 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -47,7 +47,7 @@ inline bool coincident(double d1, double d2) { //! Check for overlapping cells at a particle's position. //============================================================================== -bool check_cell_overlap(Particle* p); +bool check_cell_overlap(Particle* p, bool error=true); //============================================================================== //! Locate a particle in the geometry tree and set its geometry data fields. diff --git a/include/openmc/hdf5_interface.h b/include/openmc/hdf5_interface.h index 0be98718ce..d0474c8344 100644 --- a/include/openmc/hdf5_interface.h +++ b/include/openmc/hdf5_interface.h @@ -187,11 +187,12 @@ read_attribute(hid_t obj_id, const char* name, std::string& str) { // Create buffer to read data into auto n = attribute_typesize(obj_id, name); - char buffer[n]; + char* buffer = new char[n]; // Read attribute and set string read_attr_string(obj_id, name, n, buffer); str = std::string{buffer, n}; + delete[] buffer; } // overload for std::vector @@ -203,20 +204,21 @@ read_attribute(hid_t obj_id, const char* name, std::vector& vec) // Allocate a C char array to get strings auto n = attribute_typesize(obj_id, name); - char buffer[m][n]; + char* buffer = new char[m*n]; // Read char data in attribute - read_attr_string(obj_id, name, n, buffer[0]); + read_attr_string(obj_id, name, n, buffer); for (int i = 0; i < m; ++i) { // Determine proper length of string -- strlen doesn't work because // buffer[i] might not have any null characters std::size_t k = 0; - for (; k < n; ++k) if (buffer[i][k] == '\0') break; + for (; k < n; ++k) if (buffer[i*n + k] == '\0') break; // Create string based on (char*, size_t) constructor - vec.emplace_back(&buffer[i][0], k); + vec.emplace_back(&buffer[i*n], k); } + delete[] buffer; } //============================================================================== @@ -240,7 +242,7 @@ read_dataset(hid_t obj_id, const char* name, std::string& str, bool indep=false) { // Create buffer to read data into auto n = dataset_typesize(obj_id, name); - char buffer[n]; + char* buffer = new char[n]; // Read attribute and set string read_string(obj_id, name, n, buffer, indep); @@ -458,14 +460,17 @@ write_dataset(hid_t obj_id, const char* name, const std::vector& bu } // Copy data into contiguous buffer - char temp[n][m]; - std::fill(temp[0], temp[0] + n*m, '\0'); + char* temp = new char[n*m]; + std::fill(temp, temp + n*m, '\0'); for (int i = 0; i < n; ++i) { - std::copy(buffer[i].begin(), buffer[i].end(), temp[i]); + std::copy(buffer[i].begin(), buffer[i].end(), temp + i*m); } // Write 2D data - write_string(obj_id, 1, dims, m, name, temp[0], false); + write_string(obj_id, 1, dims, m, name, temp, false); + + // Free temp array + delete[] temp; } template inline void diff --git a/include/openmc/lattice.h b/include/openmc/lattice.h index 25ab4c0348..cd73b96408 100644 --- a/include/openmc/lattice.h +++ b/include/openmc/lattice.h @@ -27,6 +27,7 @@ enum class LatticeType { rect, hex }; + //============================================================================== // Global variables //============================================================================== @@ -265,8 +266,20 @@ public: void to_hdf5_inner(hid_t group_id) const; private: + enum class Orientation { + y, //!< Flat side of lattice parallel to y-axis + x //!< Flat side of lattice parallel to x-axis + }; + + //! Fill universes_ vector for 'y' orientation + void fill_lattice_y(const std::vector& univ_words); + + //! Fill universes_ vector for 'x' orientation + void fill_lattice_x(const std::vector& univ_words); + int n_rings_; //!< Number of radial tile positions int n_axial_; //!< Number of axial tile positions + Orientation orientation_; //!< Orientation of lattice Position center_; //!< Global center of lattice std::array pitch_; //!< Lattice tile width and height }; diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index f53bba224d..6d59826aaf 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -10,7 +10,7 @@ #include "hdf5.h" #include "pugixml.hpp" -#include "xtensor/xarray.hpp" +#include "xtensor/xtensor.hpp" #include "openmc/particle.h" #include "openmc/position.h" @@ -21,24 +21,21 @@ namespace openmc { // Global variables //============================================================================== -class RegularMesh; +class Mesh; namespace model { -extern std::vector> meshes; +extern std::vector> meshes; extern std::unordered_map mesh_map; } // namespace model -//============================================================================== -//! Tessellation of n-dimensional Euclidean space by congruent squares or cubes -//============================================================================== - -class RegularMesh { +class Mesh +{ public: // Constructors - RegularMesh() = default; - RegularMesh(pugi::xml_node node); + Mesh() = default; + Mesh(pugi::xml_node node); // Methods @@ -47,39 +44,108 @@ public: //! \param[in] p Particle to check //! \param[out] bins Bins that were crossed //! \param[out] lengths Fraction of tracklength in each bin - void bins_crossed(const Particle* p, std::vector& bins, - std::vector& lengths) const; + virtual void bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const = 0; //! Determine which surface bins were crossed by a particle // //! \param[in] p Particle to check //! \param[out] bins Surface bins that were crossed - void surface_bins_crossed(const Particle* p, std::vector& bins) const; + virtual void + surface_bins_crossed(const Particle* p, std::vector& bins) const = 0; //! Get bin at a given position in space // //! \param[in] r Position to get bin for //! \return Mesh bin - int get_bin(Position r) const; + virtual int get_bin(Position r) const = 0; //! Get bin given mesh indices // //! \param[in] Array of mesh indices //! \return Mesh bin - int get_bin_from_indices(const int* ijk) const; + virtual int get_bin_from_indices(const int* ijk) const = 0; //! Get mesh indices given a position // //! \param[in] r Position to get indices for //! \param[out] ijk Array of mesh indices //! \param[out] in_mesh Whether position is in mesh - void get_indices(Position r, int* ijk, bool* in_mesh) const; + virtual void get_indices(Position r, int* ijk, bool* in_mesh) const = 0; //! Get mesh indices corresponding to a mesh bin // //! \param[in] bin Mesh bin //! \param[out] ijk Mesh indices - void get_indices_from_bin(int bin, int* ijk) const; + virtual void get_indices_from_bin(int bin, int* ijk) const = 0; + + //! Get the number of mesh cells. + virtual int n_bins() const = 0; + + //! Get the number of mesh cell surfaces. + virtual int n_surface_bins() const = 0; + + //! Find the mesh lines that intersect an axis-aligned slice plot + // + //! \param[in] plot_ll The lower-left coordinates of the slice plot. + //! \param[in] plot_ur The upper-right coordinates of the slice plot. + //! \return A pair of vectors indicating where the mesh lines lie along each + //! of the plot's axes. For example an xy-slice plot will get back a vector + //! of x-coordinates and another of y-coordinates. These vectors may be + //! empty for low-dimensional meshes. + virtual std::pair, std::vector> + plot(Position plot_ll, Position plot_ur) const = 0; + + //! Write mesh data to an HDF5 group + // + //! \param[in] group HDF5 group + virtual void to_hdf5(hid_t group) const = 0; + + // Data members + + int id_ {-1}; //!< User-specified ID + int n_dimension_; //!< Number of dimensions + xt::xtensor lower_left_; //!< Lower-left coordinates of mesh + xt::xtensor upper_right_; //!< Upper-right coordinates of mesh +}; + +//============================================================================== +//! Tessellation of n-dimensional Euclidean space by congruent squares or cubes +//============================================================================== + +class RegularMesh : public Mesh +{ +public: + // Constructors + RegularMesh() = default; + RegularMesh(pugi::xml_node node); + + // Overriden methods + + void bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const override; + + void surface_bins_crossed(const Particle* p, std::vector& bins) + const override; + + int get_bin(Position r) const override; + + int get_bin_from_indices(const int* ijk) const override; + + void get_indices(Position r, int* ijk, bool* in_mesh) const override; + + void get_indices_from_bin(int bin, int* ijk) const override; + + int n_bins() const override; + + int n_surface_bins() const override; + + std::pair, std::vector> + plot(Position plot_ll, Position plot_ur) const override; + + void to_hdf5(hid_t group) const override; + + // New methods //! Check where a line segment intersects the mesh and if it intersects at all // @@ -89,26 +155,19 @@ public: //! \return Whether the line segment connecting r0 and r1 intersects mesh bool intersects(Position& r0, Position r1, int* ijk) const; - //! Write mesh data to an HDF5 group - // - //! \param[in] group HDF5 group - void to_hdf5(hid_t group) const; - //! Count number of bank sites in each mesh bin / energy bin // //! \param[in] bank Array of bank sites //! \param[out] Whether any bank sites are outside the mesh //! \return Array indicating number of sites in each mesh/energy bin - xt::xarray count_sites(const std::vector& bank, + xt::xtensor count_sites(const std::vector& bank, bool* outside) const; - int id_ {-1}; //!< User-specified ID - int n_dimension_; //!< Number of dimensions + // Data members + double volume_frac_; //!< Volume fraction of each mesh element - xt::xarray shape_; //!< Number of mesh elements in each dimension - xt::xarray lower_left_; //!< Lower-left coordinates of mesh - xt::xarray upper_right_; //!< Upper-right coordinates of mesh - xt::xarray width_; //!< Width of each mesh element + xt::xtensor shape_; //!< Number of mesh elements in each dimension + xt::xtensor width_; //!< Width of each mesh element private: bool intersects_1d(Position& r0, Position r1, int* ijk) const; @@ -116,6 +175,55 @@ private: bool intersects_3d(Position& r0, Position r1, int* ijk) const; }; +class RectilinearMesh : public Mesh +{ +public: + // Constructors + RectilinearMesh(pugi::xml_node node); + + // Overriden methods + + void bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const override; + + void surface_bins_crossed(const Particle* p, std::vector& bins) + const override; + + int get_bin(Position r) const override; + + int get_bin_from_indices(const int* ijk) const override; + + void get_indices(Position r, int* ijk, bool* in_mesh) const override; + + void get_indices_from_bin(int bin, int* ijk) const override; + + int n_bins() const override; + + int n_surface_bins() const override; + + std::pair, std::vector> + plot(Position plot_ll, Position plot_ur) const override; + + void to_hdf5(hid_t group) const override; + + // New methods + + //! Check where a line segment intersects the mesh and if it intersects at all + // + //! \param[in,out] r0 In: starting position, out: intersection point + //! \param[in] r1 Ending position + //! \param[out] ijk Indices of the mesh bin containing the intersection point + //! \return Whether the line segment connecting r0 and r1 intersects mesh + bool intersects(Position& r0, Position r1, int* ijk) const; + + // Data members + + xt::xtensor shape_; //!< Number of mesh elements in each dimension + +private: + std::vector> grid_; +}; + //============================================================================== // Non-member functions //============================================================================== diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 8e5aa02a6d..da83f42aa1 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -49,10 +49,19 @@ struct RGBColor { blue = v[2]; } + bool operator ==(const RGBColor& other) { + return red == other.red && green == other.green && blue == other.blue; + } + // Members uint8_t red, green, blue; }; +// some default colors +const RGBColor WHITE {255, 255, 255}; +const RGBColor RED {255, 0, 0}; + + typedef xt::xtensor ImageData; struct IdData { @@ -61,6 +70,7 @@ struct IdData { // Methods void set_value(size_t y, size_t x, const Particle& p, int level); + void set_overlap(size_t y, size_t x); // Members xt::xtensor data_; //!< 2D array of cell & material ids @@ -72,6 +82,7 @@ struct PropertyData { // Methods void set_value(size_t y, size_t x, const Particle& p, int level); + void set_overlap(size_t y, size_t x); // Members xt::xtensor data_; //!< 2D array of temperature & density data @@ -106,6 +117,7 @@ public: Position width_; //!< Plot width in geometry PlotBasis basis_; //!< Plot basis (XY/XZ/YZ) std::array pixels_; //!< Plot size in pixels + bool color_overlaps_; //!< Show overlapping cells? int level_; //!< Plot universe level }; @@ -173,6 +185,9 @@ T PlotBase::get_map() const { if (found_cell) { data.set_value(y, x, p, j); } + if (color_overlaps_ && check_cell_overlap(&p, false)) { + data.set_overlap(y, x); + } } // inner for } // outer for } // omp parallel @@ -200,6 +215,7 @@ private: void set_user_colors(pugi::xml_node plot_node); void set_meshlines(pugi::xml_node plot_node); void set_mask(pugi::xml_node plot_node); + void set_overlap_color(pugi::xml_node plot_node); // Members public: @@ -207,9 +223,10 @@ public: PlotType type_; //!< Plot type (Slice/Voxel) PlotColorBy color_by_; //!< Plot coloring (cell/material) int meshlines_width_; //!< Width of lines added to the plot - int index_meshlines_mesh_; //!< Index of the mesh to draw on the plot + int index_meshlines_mesh_ {-1}; //!< Index of the mesh to draw on the plot RGBColor meshlines_color_; //!< Color of meshlines on the plot - RGBColor not_found_; //!< Plot background color + RGBColor not_found_ {WHITE}; //!< Plot background color + RGBColor overlap_color_ {RED}; //!< Plot overlap color std::vector colors_; //!< Plot colors std::string path_plot_; //!< Plot output filename }; diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 4b76c3dc59..8f43767171 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -27,6 +27,7 @@ extern bool assume_separate; //!< assume tallies are spatially separate extern bool check_overlaps; //!< check overlaps in geometry? extern bool confidence_intervals; //!< use confidence intervals for results? extern bool create_fission_neutrons; //!< create fission neutrons (fixed source)? +extern "C" bool cmfd_run; //!< is a CMFD run? extern "C" bool dagmc; //!< indicator of DAGMC geometry extern "C" bool entropy_on; //!< calculate Shannon entropy? extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular? @@ -36,7 +37,7 @@ extern bool particle_restart_run; //!< particle restart run? extern "C" bool photon_transport; //!< photon transport turned on? extern "C" bool reduce_tallies; //!< reduce tallies at end of batch? extern bool res_scat_on; //!< use resonance upscattering method? -extern bool restart_run; //!< restart run? +extern "C" bool restart_run; //!< restart run? extern "C" bool run_CE; //!< run with continuous-energy data? extern bool source_latest; //!< write latest source at each batch? extern bool source_separate; //!< write source to separate file? @@ -57,10 +58,7 @@ extern std::string path_output; //!< directory where output files are extern std::string path_particle_restart; //!< path to a particle restart file extern std::string path_source; extern std::string path_sourcepoint; //!< path to a source file -extern std::string path_statepoint; //!< path to a statepoint file - -extern int32_t index_entropy_mesh; //!< Index of entropy mesh in global mesh array -extern int32_t index_ufs_mesh; //!< Index of UFS mesh in global mesh array +extern "C" std::string path_statepoint; //!< path to a statepoint file extern "C" int32_t n_batches; //!< number of (inactive+active) batches extern "C" int32_t n_inactive; //!< number of inactive batches diff --git a/include/openmc/simulation.h b/include/openmc/simulation.h index fef6ebbcd1..8efac4c7f8 100644 --- a/include/openmc/simulation.h +++ b/include/openmc/simulation.h @@ -4,6 +4,7 @@ #ifndef OPENMC_SIMULATION_H #define OPENMC_SIMULATION_H +#include "openmc/mesh.h" #include "openmc/particle.h" #include @@ -39,6 +40,9 @@ extern "C" int total_gen; //!< total number of generations simulated extern double total_weight; //!< Total source weight in a batch extern int64_t work_per_rank; //!< number of particles per MPI rank +extern const RegularMesh* entropy_mesh; +extern const RegularMesh* ufs_mesh; + extern std::vector k_generation; extern std::vector work_index; diff --git a/include/openmc/surface.h b/include/openmc/surface.h index 4db397a593..7b0ccefd1d 100644 --- a/include/openmc/surface.h +++ b/include/openmc/surface.h @@ -12,10 +12,7 @@ #include "openmc/constants.h" #include "openmc/position.h" - -#ifdef DAGMC -#include "DagMC.hpp" -#endif +#include "dagmc.h" namespace openmc { @@ -82,7 +79,7 @@ public: //! \param[in] r The point at which the ray is incident. //! \param[in] u Incident direction of the ray //! \return Outgoing direction of the ray - Direction reflect(Position r, Direction u) const; + virtual Direction reflect(Position r, Direction u) const; //! Evaluate the equation describing the surface. //! @@ -136,6 +133,7 @@ public: double evaluate(Position r) const; double distance(Position r, Direction u, bool coincident) const; Direction normal(Position r) const; + Direction reflect(Position r, Direction u) const; //! Get the bounding box of this surface. BoundingBox bounding_box() const; diff --git a/openmc/capi/core.py b/openmc/capi/core.py index ad4f77dfff..aae17e06ae 100644 --- a/openmc/capi/core.py +++ b/openmc/capi/core.py @@ -48,6 +48,7 @@ _init_linsolver_argtypes = [_array_1d_int, c_int, _array_1d_int, c_int, c_int, c_double, _array_1d_int, _array_1d_int] _dll.openmc_initialize_linsolver.argtypes = _init_linsolver_argtypes _dll.openmc_initialize_linsolver.restype = None +_dll.openmc_is_statepoint_batch.restype = c_bool _dll.openmc_master.restype = c_bool _dll.openmc_next_batch.argtypes = [POINTER(c_int)] _dll.openmc_next_batch.restype = c_int @@ -187,6 +188,18 @@ def init(args=None, intracomm=None): _dll.openmc_init(argc, argv, intracomm) +def is_statepoint_batch(): + """Return whether statepoint will be written in current batch or not. + + Returns + ------- + bool + Whether is statepoint batch or not + + """ + return _dll.openmc_is_statepoint_batch() + + def iter_batches(): """Iterator over batches. @@ -230,18 +243,9 @@ def keff(): Mean k-eigenvalue and standard deviation of the mean """ - n = openmc.capi.num_realizations() - if n > 3: - # Use the combined estimator if there are enough realizations - k = (c_double*2)() - _dll.openmc_get_keff(k) - 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 - return (mean, std_dev) + k = (c_double*2)() + _dll.openmc_get_keff(k) + return tuple(k) def master(): diff --git a/openmc/capi/filter.py b/openmc/capi/filter.py index 05ec85d010..c0716bcef6 100644 --- a/openmc/capi/filter.py +++ b/openmc/capi/filter.py @@ -11,7 +11,7 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler from .material import Material -from .mesh import Mesh +from .mesh import RegularMesh __all__ = ['Filter', 'AzimuthalFilter', 'CellFilter', @@ -255,7 +255,7 @@ class MeshFilter(Filter): def mesh(self): index_mesh = c_int32() _dll.openmc_mesh_filter_get_mesh(self._index, index_mesh) - return Mesh(index=index_mesh.value) + return RegularMesh(index=index_mesh.value) @mesh.setter def mesh(self, mesh): @@ -274,7 +274,7 @@ class MeshSurfaceFilter(Filter): def mesh(self): index_mesh = c_int32() _dll.openmc_meshsurface_filter_get_mesh(self._index, index_mesh) - return Mesh(index=index_mesh.value) + return RegularMesh(index=index_mesh.value) @mesh.setter def mesh(self, mesh): diff --git a/openmc/capi/material.py b/openmc/capi/material.py index 9811853380..43959e2446 100644 --- a/openmc/capi/material.py +++ b/openmc/capi/material.py @@ -32,6 +32,9 @@ _dll.openmc_material_get_densities.argtypes = [ POINTER(c_int)] _dll.openmc_material_get_densities.restype = c_int _dll.openmc_material_get_densities.errcheck = _error_handler +_dll.openmc_material_get_density.argtypes = [c_int32, POINTER(c_double)] +_dll.openmc_material_get_density.restype = c_int +_dll.openmc_material_get_density.errcheck = _error_handler _dll.openmc_material_get_volume.argtypes = [c_int32, POINTER(c_double)] _dll.openmc_material_get_volume.restype = c_int _dll.openmc_material_get_volume.errcheck = _error_handler @@ -139,6 +142,15 @@ class Material(_FortranObjectWithID): return self._get_densities()[0] return nuclides + @property + def density(self): + density = c_double() + try: + _dll.openmc_material_get_density(self._index, density) + except OpenMCError: + return None + return density.value + @property def densities(self): return self._get_densities()[1] diff --git a/openmc/capi/mesh.py b/openmc/capi/mesh.py index 70a4345b09..a1161f48f3 100644 --- a/openmc/capi/mesh.py +++ b/openmc/capi/mesh.py @@ -11,7 +11,7 @@ from .core import _FortranObjectWithID from .error import _error_handler from .material import Material -__all__ = ['Mesh', 'meshes'] +__all__ = ['RegularMesh', 'meshes'] # Mesh functions _dll.openmc_extend_meshes.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] @@ -45,8 +45,8 @@ _dll.n_meshes.argtypes = [] _dll.n_meshes.restype = c_int -class Mesh(_FortranObjectWithID): - """Mesh stored internally. +class RegularMesh(_FortranObjectWithID): + """RegularMesh 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 @@ -170,11 +170,11 @@ class _MeshMapping(Mapping): except (AllocationError, InvalidIDError) as e: # __contains__ expects a KeyError to work correctly raise KeyError(str(e)) - return Mesh(index=index.value) + return RegularMesh(index=index.value) def __iter__(self): for i in range(len(self)): - yield Mesh(index=i).id + yield RegularMesh(index=i).id def __len__(self): return _dll.n_meshes() diff --git a/openmc/capi/plot.py b/openmc/capi/plot.py index 9ff398f5c1..c4bff4cb3f 100644 --- a/openmc/capi/plot.py +++ b/openmc/capi/plot.py @@ -1,4 +1,5 @@ -from ctypes import c_int, c_size_t, c_int32, c_double, Structure, POINTER +from ctypes import (c_bool, c_int, c_size_t, c_int32, + c_double, Structure, POINTER) from . import _dll from .error import _error_handler @@ -84,10 +85,12 @@ class _PlotBase(Structure): ('width_', _Position), ('basis_', c_int), ('pixels_', 3*c_size_t), + ('color_overlaps_', c_bool), ('level_', c_int)] def __init__(self): self.level_ = -1 + self.color_overlaps_ = False @property def origin(self): @@ -112,32 +115,6 @@ class _PlotBase(Structure): raise ValueError("Plot basis {} is invalid".format(self.basis_)) - @property - def h_res(self): - return self.pixels_[0] - - @property - def v_res(self): - return self.pixels_[1] - - @property - def level(self): - return int(self.level_) - - @origin.setter - def origin(self, origin): - self.origin_.x = origin[0] - self.origin_.y = origin[1] - self.origin_.z = origin[2] - - @width.setter - def width(self, width): - self.width_.x = width - - @height.setter - def height(self, height): - self.width_.y = height - @basis.setter def basis(self, basis): if isinstance(basis, str): @@ -164,6 +141,40 @@ class _PlotBase(Structure): raise ValueError("{} of type {} is an" " invalid plot basis".format(basis, type(basis))) + @property + def h_res(self): + return self.pixels_[0] + + @property + def v_res(self): + return self.pixels_[1] + + @property + def level(self): + return int(self.level_) + + @property + def color_overlaps(self): + return self.color_overlaps_ + + @color_overlaps.setter + def color_overlaps(self, color_overlaps): + self.color_overlaps_ = color_overlaps + + @origin.setter + def origin(self, origin): + self.origin_.x = origin[0] + self.origin_.y = origin[1] + self.origin_.z = origin[2] + + @width.setter + def width(self, width): + self.width_.x = width + + @height.setter + def height(self, height): + self.width_.y = height + @h_res.setter def h_res(self, h_res): self.pixels_[0] = h_res @@ -176,6 +187,14 @@ class _PlotBase(Structure): def level(self, level): self.level_ = level + @property + def color_overlaps(self): + return self.color_overlaps_ + + @color_overlaps.setter + def color_overlaps(self, val): + self.color_overlaps_ = val + def __repr__(self): out_str = ["-----", "Plot:", @@ -186,6 +205,7 @@ class _PlotBase(Structure): "Basis: {}".format(self.basis), "HRes: {}".format(self.h_res), "VRes: {}".format(self.v_res), + "Color Overlaps: {}".format(self.color_overlaps), "Level: {}".format(self.level)] return '\n'.join(out_str) diff --git a/openmc/capi/settings.py b/openmc/capi/settings.py index 115962395d..78794d8563 100644 --- a/openmc/capi/settings.py +++ b/openmc/capi/settings.py @@ -18,10 +18,12 @@ _dll.openmc_get_seed.restype = c_int64 class _Settings(object): # Attributes that are accessed through a descriptor batches = _DLLGlobal(c_int32, 'n_batches') + cmfd_run = _DLLGlobal(c_bool, 'cmfd_run') entropy_on = _DLLGlobal(c_bool, 'entropy_on') generations_per_batch = _DLLGlobal(c_int32, 'gen_per_batch') inactive = _DLLGlobal(c_int32, 'n_inactive') particles = _DLLGlobal(c_int64, 'n_particles') + restart_run = _DLLGlobal(c_bool, 'restart_run') run_CE = _DLLGlobal(c_bool, 'run_CE') verbosity = _DLLGlobal(c_int, 'verbosity') @@ -43,6 +45,11 @@ class _Settings(object): else: raise ValueError('Invalid run mode: {}'.format(mode)) + @property + def path_statepoint(self): + path = c_char_p.in_dll(_dll, 'path_statepoint').value + return path.decode() + @property def seed(self): return _dll.openmc_get_seed() diff --git a/openmc/cmfd.py b/openmc/cmfd.py index d1ea77fa06..742cdeabfd 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -10,14 +10,17 @@ References """ +from contextlib import contextmanager from collections.abc import Iterable, Mapping from numbers import Real, Integral import sys import time -from ctypes import c_int, c_double +from ctypes import c_int +import warnings import numpy as np from scipy import sparse +import h5py import openmc.capi from openmc.checkvalue import (check_type, check_length, check_value, @@ -191,8 +194,12 @@ class CMFDRun(object): Attributes ---------- - begin : int - Batch number at which CMFD calculations should begin + tally_begin : int + Batch number at which CMFD tallies should begin accummulating + feedback_begin: int + Batch number at which CMFD feedback should be turned on + ref_d : list of floats + List of reference diffusion coefficients to fix CMFD parameters to dhat_reset : bool Indicate whether :math:`\widehat{D}` nonlinear CMFD parameters should be reset to zero before solving CMFD eigenproblem. @@ -251,6 +258,21 @@ class CMFDRun(object): * "math" - Create adjoint matrices mathematically as the transpose of loss and production CMFD matrices + window_type : {'expanding', 'rolling', 'none'} + Specifies type of tally window scheme to use to accumulate CMFD + tallies. Options are: + + * "expanding" - Have an expanding window that doubles in size + to give more weight to more recent tallies as more generations are + simulated + * "rolling" - Have a fixed window size that aggregates tallies from + the same number of previous generations tallied + * "none" - Don't use a windowing scheme so that all tallies from last + time they were reset are used for the CMFD algorithm. + + window_size : int + Size of window to use for tally window scheme. Only relevant when + window_type is set to "rolling" indices : numpy.ndarray Stores spatial and group dimensions as [nx, ny, nz, ng] cmfd_src : numpy.ndarray @@ -287,7 +309,9 @@ class CMFDRun(object): """ # Variables that users can modify - self._begin = 1 + self._tally_begin = 1 + self._feedback_begin = 1 + self._ref_d = [] self._dhat_reset = False self._display = {'balance': False, 'dominance': False, 'entropy': False, 'source': False} @@ -305,9 +329,12 @@ class CMFDRun(object): self._spectral = 0.0 self._gauss_seidel_tolerance = [1.e-10, 1.e-5] self._adjoint_type = 'physical' + self._window_type = 'none' + self._window_size = 10 self._intracomm = None # External variables used during runtime but users cannot control + self._set_reference_params = False self._indices = np.zeros(4, dtype=np.int32) self._egrid = None self._albedo = None @@ -323,6 +350,13 @@ class CMFDRun(object): self._adj_keff = None self._phi = None self._adj_phi = None + self._openmc_src_rate = None + self._flux_rate = None + self._total_rate = None + self._p1scatt_rate = None + self._scatt_rate = None + self._nfiss_rate = None + self._current_rate = None self._flux = None self._totalxs = None self._p1scattxs = None @@ -337,12 +371,13 @@ class CMFDRun(object): self._openmc_src = None self._sourcecounts = None self._weightfactors = None - self._entropy = None - self._balance = None - self._src_cmp = None - self._dom = None - self._k_cmfd = None + self._entropy = [] + self._balance = [] + self._src_cmp = [] + self._dom = [] + self._k_cmfd = [] self._resnb = None + self._reset_every = None self._time_cmfd = None self._time_cmfdbuild = None self._time_cmfdsolve = None @@ -379,8 +414,16 @@ class CMFDRun(object): self._prod_col = None @property - def begin(self): - return self._begin + def tally_begin(self): + return self._tally_begin + + @property + def feedback_begin(self): + return self._feedback_begin + + @property + def ref_d(self): + return self._ref_d @property def dhat_reset(self): @@ -414,6 +457,14 @@ class CMFDRun(object): def adjoint_type(self): return self._adjoint_type + @property + def window_type(self): + return self._window_type + + @property + def window_size(self): + return self._window_size + @property def power_monitor(self): return self._power_monitor @@ -474,11 +525,23 @@ class CMFDRun(object): def k_cmfd(self): return self._k_cmfd - @begin.setter - def begin(self, begin): - check_type('CMFD begin batch', begin, Integral) - check_greater_than('CMFD begin batch', begin, 0) - self._begin = begin + @tally_begin.setter + def tally_begin(self, begin): + check_type('CMFD tally begin batch', begin, Integral) + check_greater_than('CMFD tally begin batch', begin, 0) + self._tally_begin = begin + + @feedback_begin.setter + def feedback_begin(self, begin): + check_type('CMFD feedback begin batch', begin, Integral) + check_greater_than('CMFD feedback begin batch', begin, 0) + self._feedback_begin = begin + + @ref_d.setter + def ref_d(self, diff_params): + check_type('Reference diffusion params', diff_params, + Iterable, Real) + self._ref_d = np.array(diff_params) @dhat_reset.setter def dhat_reset(self, dhat_reset): @@ -569,6 +632,23 @@ class CMFDRun(object): ['math', 'physical']) self._adjoint_type = adjoint_type + @window_type.setter + def window_type(self, window_type): + check_type('CMFD window type', window_type, str) + check_value('CMFD window type', window_type, + ['none', 'rolling', 'expanding']) + self._window_type = window_type + + @window_size.setter + def window_size(self, window_size): + check_type('CMFD window size', window_size, Integral) + check_greater_than('CMFD window size', window_size, 0) + if self._window_type != 'rolling': + warn_msg = 'Window size will have no effect on CMFD simulation ' \ + 'unless window type is set to "rolling".' + warnings.warn(warn_msg, RuntimeWarning) + self._window_size = window_size + @power_monitor.setter def power_monitor(self, power_monitor): check_type('CMFD power monitor', power_monitor, bool) @@ -623,6 +703,31 @@ class CMFDRun(object): All keyword arguments are passed to :func:`openmc.capi.run_in_memory`. + """ + with self.run_in_memory(**kwargs): + for _ in self.iter_batches(): + pass + + @contextmanager + def run_in_memory(self, **kwargs): + """ Context manager for running CMFD functions with OpenMC shared + library functions. + + This function can be used with a 'with' statement to ensure the + CMFDRun class is properly initialized/finalized. For example:: + + from openmc import cmfd + cmfd_run = cmfd.CMFDRun() + with cmfd_run.run_in_memory(): + do_stuff_before_simulation_start() + for _ in cmfd_run.iter_batches(): + do_stuff_between_batches() + + Parameters + ---------- + **kwargs + All keyword arguments passed to :func:`openmc.capi.run_in_memory`. + """ # Store intracomm for part of CMFD routine where MPI reduce and # broadcast calls are made @@ -633,47 +738,171 @@ class CMFDRun(object): # Run and pass arguments to C API run_in_memory function with openmc.capi.run_in_memory(**kwargs): - # Configure CMFD parameters and tallies - self._configure_cmfd() + self.init() + yield + self.finalize() - # Set up CMFD coremap - self._set_coremap() + def iter_batches(self): + """ Iterator over batches. - # Compute and store array indices used to build cross section - # arrays - self._precompute_array_indices() + This function returns a generator-iterator that allows Python code to + be run between batches when running an OpenMC simulation with CMFD. + It should be used in conjunction with + :func`openmc.cmfd.CMFDRun.run_in_memory` to ensure proper + initialization/finalization of CMFDRun instance. - # Compute and store row and column indices used to build CMFD - # matrices - self._precompute_matrix_indices() + """ + status = 0 + while status == 0: + status = self.next_batch() + yield - # Initialize all variables used for linear solver in C++ - self._initialize_linsolver() + def init(self): + """ Initialize CMFDRun instance by setting up CMFD parameters and + calling :func:`openmc.capi.simulation_init` - # Initialize simulation - openmc.capi.simulation_init() + """ + # Configure CMFD parameters and tallies + self._configure_cmfd() - status = 0 - while status == 0: - # Initialize CMFD batch - self._cmfd_init_batch() + # Initialize all arrays used for CMFD solver + self._allocate_cmfd() - # Run next batch - status = openmc.capi.next_batch() + # Compute and store array indices used to build cross section + # arrays + self._precompute_array_indices() - # Perform CMFD calculation if on - if self._cmfd_on: - self._execute_cmfd() + # Compute and store row and column indices used to build CMFD + # matrices + self._precompute_matrix_indices() - # Write CMFD output if CMFD on for current batch - if openmc.capi.master(): - self._write_cmfd_output() + # Initialize all variables used for linear solver in C++ + self._initialize_linsolver() - # Finalize simuation - openmc.capi.simulation_finalize() + # Initialize simulation + openmc.capi.simulation_init() - # Print out CMFD timing statistics - self._write_cmfd_timing_stats() + # Set cmfd_run variable to True through C API + openmc.capi.settings.cmfd_run = True + + def next_batch(self): + """ Run next batch for CMFDRun. + + Returns + ------- + int + Status after running a batch (0=normal, 1=reached maximum number of + batches, 2=tally triggers reached) + + """ + # Initialize CMFD batch + self._cmfd_init_batch() + + # Run next batch + status = openmc.capi.next_batch() + + # Perform CMFD calculation if on + if self._cmfd_on: + self._execute_cmfd() + + # Write CMFD output if CMFD on for current batch + if openmc.capi.master(): + self._write_cmfd_output() + + # Write CMFD data to statepoint + if openmc.capi.is_statepoint_batch(): + self.statepoint_write() + return status + + def finalize(self): + """ Finalize simulation by calling + :func:`openmc.capi.simulation_finalize` and print out CMFD timing + information. + + """ + # Finalize simuation + openmc.capi.simulation_finalize() + + # Print out CMFD timing statistics + self._write_cmfd_timing_stats() + + def statepoint_write(self, filename=None): + """Write all simulation parameters to statepoint + + Parameters + ---------- + filename : str + Filename of statepoint + + """ + if filename is None: + batch_str_len = len(str(openmc.capi.settings.batches)) + batch_str = str(openmc.capi.current_batch()).zfill(batch_str_len) + filename = 'statepoint.{}.h5'.format(batch_str) + + # Call C API statepoint_write to save source distribution with CMFD + # feedback + openmc.capi.statepoint_write(filename=filename) + + # Append CMFD data to statepoint file using h5py + self._write_cmfd_statepoint(filename) + + def _write_cmfd_statepoint(self, filename): + """Append all CNFD simulation parameters to existing statepoint + + Parameters + ---------- + filename : str + Filename of statepoint + + """ + if openmc.capi.master(): + with h5py.File(filename, 'a') as f: + if 'cmfd' not in f: + if openmc.capi.settings.verbosity >= 5: + print(' Writing CMFD data to {}...'.format(filename)) + sys.stdout.flush() + cmfd_group = f.create_group("cmfd") + cmfd_group.attrs['cmfd_on'] = self._cmfd_on + cmfd_group.attrs['feedback'] = self._feedback + cmfd_group.attrs['feedback_begin'] = self._feedback_begin + cmfd_group.attrs['mesh_id'] = self._mesh_id + cmfd_group.attrs['tally_begin'] = self._tally_begin + cmfd_group.attrs['time_cmfd'] = self._time_cmfd + cmfd_group.attrs['time_cmfdbuild'] = self._time_cmfdbuild + cmfd_group.attrs['time_cmfdsolve'] = self._time_cmfdsolve + cmfd_group.attrs['window_size'] = self._window_size + cmfd_group.attrs['window_type'] = self._window_type + cmfd_group.create_dataset('k_cmfd', data=self._k_cmfd) + cmfd_group.create_dataset('dom', data=self._dom) + cmfd_group.create_dataset('src_cmp', data=self._src_cmp) + cmfd_group.create_dataset('balance', data=self._balance) + cmfd_group.create_dataset('entropy', data=self._entropy) + cmfd_group.create_dataset('reset', data=self._reset) + cmfd_group.create_dataset('albedo', data=self._albedo) + cmfd_group.create_dataset('coremap', data=self._coremap) + cmfd_group.create_dataset('egrid', data=self._egrid) + cmfd_group.create_dataset('indices', data=self._indices) + cmfd_group.create_dataset('tally_ids', + data=self._tally_ids) + cmfd_group.create_dataset('current_rate', + data=self._current_rate) + cmfd_group.create_dataset('flux_rate', + data=self._flux_rate) + cmfd_group.create_dataset('nfiss_rate', + data=self._nfiss_rate) + cmfd_group.create_dataset('openmc_src_rate', + data=self._openmc_src_rate) + cmfd_group.create_dataset('p1scatt_rate', + data=self._p1scatt_rate) + cmfd_group.create_dataset('scatt_rate', + data=self._scatt_rate) + cmfd_group.create_dataset('total_rate', + data=self._total_rate) + elif openmc.settings.verbosity >= 5: + print(' CMFD data not written to statepoint file' + 'as it already exists in {}'.format(filename)) + sys.stdout.flush() def _initialize_linsolver(self): # Determine number of rows in CMFD matrix @@ -731,16 +960,38 @@ class CMFDRun(object): def _configure_cmfd(self): """Initialize CMFD parameters and set CMFD input variables""" - # Read in cmfd input defined in Python - self._read_cmfd_input() + # Check if restarting simulation from statepoint file + if not openmc.capi.settings.restart_run: + # Read in cmfd input defined in Python + self._read_cmfd_input() - # Initialize timers - self._time_cmfd = 0.0 - self._time_cmfdbuild = 0.0 - self._time_cmfdsolve = 0.0 + # Set up CMFD coremap + self._set_coremap() - # Initialize all numpy arrays used for CMFD solver - self._allocate_cmfd() + # Extract spatial and energy indices + nx, ny, nz, ng = self._indices + + # Allocate parameters that need to stored for tally window + self._openmc_src_rate = np.zeros((nx, ny, nz, ng, 0)) + self._flux_rate = np.zeros((nx, ny, nz, ng, 0)) + self._total_rate = np.zeros((nx, ny, nz, ng, 0)) + self._p1scatt_rate = np.zeros((nx, ny, nz, ng, 0)) + self._scatt_rate = np.zeros((nx, ny, nz, ng, ng, 0)) + self._nfiss_rate = np.zeros((nx, ny, nz, ng, ng, 0)) + self._current_rate = np.zeros((nx, ny, nz, 12, ng, 0)) + + # Initialize timers + self._time_cmfd = 0.0 + self._time_cmfdbuild = 0.0 + self._time_cmfdsolve = 0.0 + + # Initialize parameters for CMFD tally windows + self._set_tally_window() + + else: + # Reset CMFD parameters from statepoint file + path_statepoint = openmc.capi.settings.path_statepoint + self._reset_cmfd(path_statepoint) def _read_cmfd_input(self): """Sets values of additional instance variables based on user input""" @@ -788,20 +1039,94 @@ class CMFDRun(object): self._coremap = np.ones((np.product(self._indices[0:3])), dtype=int) + # Check CMFD tallies accummulated before feedback turned on + if self._feedback and self._feedback_begin < self._tally_begin: + raise ValueError('Tally begin must be less than or equal to ' + 'feedback begin') + # Set number of batches where cmfd tallies should be reset - if self._reset is not None: - self._n_resets = len(self._reset) + self._n_resets = len(self._reset) # Create tally objects self._create_cmfd_tally() + def _reset_cmfd(self, filename): + """Reset all CMFD parameters from statepoint + + Parameters + ---------- + filename : str + Filename of statepoint to read from + + """ + with h5py.File(filename, 'r') as f: + if 'cmfd' not in f: + raise OpenMCError('Could not find CMFD parameters in ', + 'file {}'.format(filename)) + else: + # Overwrite CMFD values from statepoint + if (openmc.capi.master() and + openmc.capi.settings.verbosity >= 5): + print(' Loading CMFD data from {}...'.format(filename)) + sys.stdout.flush() + cmfd_group = f['cmfd'] + self._cmfd_on = cmfd_group.attrs['cmfd_on'] + self._feedback = cmfd_group.attrs['feedback'] + self._feedback_begin = cmfd_group.attrs['feedback_begin'] + self._tally_begin = cmfd_group.attrs['tally_begin'] + self._time_cmfd = cmfd_group.attrs['time_cmfd'] + self._time_cmfdbuild = cmfd_group.attrs['time_cmfdbuild'] + self._time_cmfdsolve = cmfd_group.attrs['time_cmfdsolve'] + self._window_size = cmfd_group.attrs['window_size'] + self._window_type = cmfd_group.attrs['window_type'] + self._k_cmfd = list(cmfd_group['k_cmfd']) + self._dom = list(cmfd_group['dom']) + self._src_cmp = list(cmfd_group['src_cmp']) + self._balance = list(cmfd_group['balance']) + self._entropy = list(cmfd_group['entropy']) + self._reset = list(cmfd_group['reset']) + self._albedo = cmfd_group['albedo'][()] + self._coremap = cmfd_group['coremap'][()] + self._egrid = cmfd_group['egrid'][()] + self._indices = cmfd_group['indices'][()] + self._current_rate = cmfd_group['current_rate'][()] + self._flux_rate = cmfd_group['flux_rate'][()] + self._nfiss_rate = cmfd_group['nfiss_rate'][()] + self._openmc_src_rate = cmfd_group['openmc_src_rate'][()] + self._p1scatt_rate = cmfd_group['p1scatt_rate'][()] + self._scatt_rate = cmfd_group['scatt_rate'][()] + self._total_rate = cmfd_group['total_rate'][()] + + # Overwrite CMFD mesh properties + cmfd_mesh_name = 'mesh ' + str(cmfd_group.attrs['mesh_id']) + cmfd_mesh = f['tallies']['meshes'][cmfd_mesh_name] + self._mesh.dimension = cmfd_mesh['dimension'][()] + self._mesh.lower_left = cmfd_mesh['lower_left'][()] + self._mesh.upper_right = cmfd_mesh['upper_right'][()] + self._mesh.width = cmfd_mesh['width'][()] + + # Store tally ids from statepoint run + sp_tally_ids = list(cmfd_group['tally_ids']) + + # Set CMFD variables not in statepoint file + default_egrid = np.array([_ENERGY_MIN_NEUTRON, _ENERGY_MAX_NEUTRON]) + self._energy_filters = not np.array_equal(self._egrid, default_egrid) + self._n_resets = len(self._reset) + self._mat_dim = np.max(self._coremap) + 1 + self._reset_every = (self._window_type == 'expanding' or + self._window_type == 'rolling') + + # Recreate CMFD tallies in memory + self._create_cmfd_tally() + def _allocate_cmfd(self): """Allocates all numpy arrays and lists used in CMFD algorithm""" # Extract spatial and energy indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] + nx, ny, nz, ng = self._indices + + # Allocate dimensions for each mesh cell + self._hxyz = np.zeros((nx, ny, nz, 3)) + self._hxyz[:] = openmc.capi.meshes[self._mesh_id].width # Allocate flux, cross sections and diffusion coefficient self._flux = np.zeros((nx, ny, nz, ng)) @@ -815,26 +1140,28 @@ class CMFDRun(object): self._dtilde = np.zeros((nx, ny, nz, ng, 6)) self._dhat = np.zeros((nx, ny, nz, ng, 6)) - # Allocate dimensions for each mesh cell - self._hxyz = np.zeros((nx, ny, nz, 3)) + # Set reference diffusion parameters + if self._ref_d: + self._set_reference_params = True + # Check length of reference diffusion parameters equal to number of + # energy groups + if len(self._ref_d) != self._indices[3]: + raise OpenMCError('Number of reference diffusion parameters ' + 'must equal number of CMFD energy groups') - # Allocate surface currents - self._current = np.zeros((nx, ny, nz, 12, ng)) - - # Allocate source distributions - self._cmfd_src = np.zeros((nx, ny, nz, ng)) - self._openmc_src = np.zeros((nx, ny, nz, ng)) - - # Allocate source weight modification variables - self._sourcecounts = np.zeros((nx*ny*nz, ng)) - self._weightfactors = np.ones((nx, ny, nz, ng)) - - # Allocate batchwise parameters - self._entropy = [] - self._balance = [] - self._src_cmp = [] - self._dom = [] - self._k_cmfd = [] + def _set_tally_window(self): + """Sets parameters to handle different tally window options""" + # Set parameters for expanding window + if self._window_type == 'expanding': + self._reset_every = True + self._window_size = 1 + # Set parameters for rolling window + elif self.window_type == 'rolling': + self._reset_every = True + # Set parameters for default case, with no window + else: + self._window_size = 1 + self._reset_every = False def _cmfd_init_batch(self): """Handles CMFD options at the beginning of each batch""" @@ -843,11 +1170,13 @@ class CMFDRun(object): current_batch = openmc.capi.current_batch() + 1 # Check to activate CMFD diffusion and possible feedback - if self._begin == current_batch: + # Check to activate CMFD tallies + if self._tally_begin == current_batch: self._cmfd_on = True # Check to reset tallies - if self._n_resets > 0 and current_batch in self._reset: + if ((self._n_resets > 0 and current_batch in self._reset) + or self._reset_every): self._cmfd_tally_reset() def _execute_cmfd(self): @@ -885,7 +1214,8 @@ class CMFDRun(object): def _cmfd_tally_reset(self): """Resets all CMFD tallies in memory""" # Print message - if openmc.capi.settings.verbosity >= 6 and openmc.capi.master(): + if (openmc.capi.settings.verbosity >= 6 and openmc.capi.master() and + not self._reset_every): print(' CMFD tallies reset') sys.stdout.flush() @@ -898,7 +1228,7 @@ class CMFDRun(object): """Configures CMFD object for a CMFD eigenvalue calculation """ - # Calculate all cross sections based on reaction rates from last batch + # Calculate all cross sections based on tally window averages self._compute_xs() # Compute effective downscatter cross section @@ -1042,15 +1372,9 @@ class CMFDRun(object): """ # Extract number of groups and number of accelerated regions - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] + nx, ny, nz, ng = self._indices n = self._mat_dim - # Reset CMFD source to 0 - self._cmfd_src.fill(0.) - # Compute cmfd_src in a vecotorized manner by phi to the spatial # indices of the actual problem so that cmfd_flux can be multiplied by # nfissxs @@ -1111,13 +1435,7 @@ class CMFDRun(object): if new_weights: # Get spatial dimensions and energy groups - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Set weight factors to default 1.0 - self._weightfactors.fill(1.0) + nx, ny, nz, ng = self._indices # Count bank site in mesh and reverse due to egrid structured outside = self._count_bank_sites() @@ -1147,12 +1465,13 @@ class CMFDRun(object): # Compute weight factors div_condition = np.logical_and(sourcecounts > 0, self._cmfd_src > 0) - with np.errstate(divide='ignore', invalid='ignore'): - self._weightfactors = (np.divide(self._cmfd_src * norm, - sourcecounts, where=div_condition, - out=np.ones_like(self._cmfd_src))) + self._weightfactors = (np.divide(self._cmfd_src * norm, + sourcecounts, where=div_condition, + out=np.ones_like(self._cmfd_src), + dtype=np.float32)) - if not self._feedback: + if (not self._feedback + or openmc.capi.current_batch() < self._feedback_begin): return # Broadcast weight factors to all procs @@ -1208,9 +1527,11 @@ class CMFDRun(object): bank = openmc.capi.source_bank() energy = self._egrid sites_outside = np.zeros(1, dtype=bool) + nxnynz = np.prod(self._indices[0:3]) ng = self._indices[3] outside = np.zeros(1, dtype=bool) + self._sourcecounts = np.zeros((nxnynz, ng)) count = np.zeros(self._sourcecounts.shape) # Get location and energy of each particle in source bank @@ -1572,32 +1893,36 @@ class CMFDRun(object): # Reshape coremap to three dimensional array # Indices of coremap in user input switched in x and z axes - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] + nx, ny, nz = self._indices[:3] self._coremap = self._coremap.reshape(nz, ny, nx) self._coremap = np.swapaxes(self._coremap, 0, 2) def _compute_xs(self): """Takes CMFD tallies from OpenMC and computes macroscopic cross - sections, flux, and diffusion coefficients for each mesh cell + sections, flux, and diffusion coefficients for each mesh cell using + a tally window scheme """ + # Update window size for expanding window if necessary + num_cmfd_batches = openmc.capi.current_batch() - self._tally_begin + 1 + if (self._window_type == 'expanding' and + num_cmfd_batches == self._window_size * 2): + self._window_size *= 2 + + # Discard tallies from oldest batch if window limit reached + tally_windows = self._flux_rate.shape[-1] + 1 + if tally_windows > self._window_size: + self._flux_rate = self._flux_rate[...,1:] + self._total_rate = self._total_rate[...,1:] + self._p1scatt_rate = self._p1scatt_rate[...,1:] + self._scatt_rate = self._scatt_rate[...,1:] + self._nfiss_rate = self._nfiss_rate[...,1:] + self._current_rate = self._current_rate[...,1:] + self._openmc_src_rate = self._openmc_src_rate[...,1:] + tally_windows -= 1 + # Extract spatial and energy indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] - ng = self._indices[3] - - # Set flux object and source distribution all to zeros - self._flux.fill(0.) - self._openmc_src.fill(0.) - - # Set mesh widths - self._hxyz[:,:,:,:] = openmc.capi.meshes[self._mesh_id].width - - # Reset keff_bal to zero - self._keff_bal = 0. + nx, ny, nz, ng = self._indices # Get tallies in-memory tallies = openmc.capi.tallies @@ -1632,81 +1957,115 @@ class CMFDRun(object): # Define target tally reshape dimensions. This defines how openmc # tallies are ordered by dimension - target_tally_shape = [nz, ny, nx, ng] + target_tally_shape = [nz, ny, nx, ng, 1] # Reshape flux array to target shape. Swap x and z axes so that - # flux shape is now [nx, ny, nz, ng] + # flux shape is now [nx, ny, nz, ng, 1] reshape_flux = np.swapaxes(flux.reshape(target_tally_shape), 0, 2) # Flip energy axis as tally results are given in reverse order of # energy group - self._flux = np.flip(reshape_flux, axis=3) + reshape_flux = np.flip(reshape_flux, axis=3) - # Get total rr and convert to total xs from CMFD tally 0 - tally_results = tallies[tally_id].results[:,1,1] - with np.errstate(divide='ignore', invalid='ignore'): - totalxs = np.divide(tally_results, flux, - where=flux > 0, - out=np.zeros_like(tally_results)) + # Bank flux to flux_rate + self._flux_rate = np.append(self._flux_rate, reshape_flux, axis=4) - # Reshape totalxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng] - reshape_totalxs = np.swapaxes(totalxs.reshape(target_tally_shape), + # Compute flux as aggregate of banked flux_rate over tally window + self._flux = np.sum(self._flux_rate, axis=4) + + # Get total rr from CMFD tally 0 + totalrr = tallies[tally_id].results[:,1,1] + + # Reshape totalrr array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, 1] + reshape_totalrr = np.swapaxes(totalrr.reshape(target_tally_shape), 0, 2) - # Total xs is flipped in energy axis as tally results are given in + # Total rr is flipped in energy axis as tally results are given in # reverse order of energy group - self._totalxs = np.flip(reshape_totalxs, axis=3) + reshape_totalrr = np.flip(reshape_totalrr, axis=3) - # Get scattering xs from CMFD tally 1 + # Bank total rr to total_rate + self._total_rate = np.append(self._total_rate, reshape_totalrr, + axis=4) + + # Compute total xs as aggregate of banked total_rate over tally window + # divided by flux + self._totalxs = np.divide(np.sum(self._total_rate, axis=4), + self._flux, where=self._flux > 0, + out=np.zeros_like(self._totalxs)) + + # Get scattering rr from CMFD tally 1 # flux is repeated to account for extra dimensionality of scattering xs tally_id = self._tally_ids[1] - tally_results = tallies[tally_id].results[:,0,1] - with np.errstate(divide='ignore', invalid='ignore'): - scattxs = np.divide(tally_results, np.repeat(flux, ng), - where=np.repeat(flux > 0, ng), - out=np.zeros_like(tally_results)) + scattrr = tallies[tally_id].results[:,0,1] # Define target tally reshape dimensions for xs with incoming # and outgoing energies - target_tally_shape = [nz, ny, nx, ng, ng] + target_tally_shape = [nz, ny, nx, ng, ng, 1] - # Reshape scattxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, ng] - reshape_scattxs = np.swapaxes(scattxs.reshape(target_tally_shape), + # Reshape scattrr array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng, 1] + reshape_scattrr = np.swapaxes(scattrr.reshape(target_tally_shape), 0, 2) - # Scattering xs is flipped in both incoming and outgoing energy axes + # Scattering rr is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group - self._scattxs = np.flip(reshape_scattxs, axis=3) - self._scattxs = np.flip(self._scattxs, axis=4) + reshape_scattrr = np.flip(reshape_scattrr, axis=3) + reshape_scattrr = np.flip(reshape_scattrr, axis=4) - # Get nu-fission xs from CMFD tally 1 - # flux is repeated to account for extra dimensionality of nu-fission xs - tally_results = tallies[tally_id].results[:,1,1] + # Bank scattering rr to scatt_rate + self._scatt_rate = np.append(self._scatt_rate, reshape_scattrr, + axis=5) + + # Compute scattering xs as aggregate of banked scatt_rate over tally + # window divided by flux. Flux dimensionality increased to account for + # extra dimensionality of scattering xs + extended_flux = self._flux[:,:,:,:,np.newaxis] + self._scattxs = np.divide(np.sum(self._scatt_rate, axis=5), + extended_flux, where=extended_flux > 0, + out=np.zeros_like(self._scattxs)) + + # Get nu-fission rr from CMFD tally 1 + nfissrr = tallies[tally_id].results[:,1,1] num_realizations = tallies[tally_id].num_realizations - with np.errstate(divide='ignore', invalid='ignore'): - nfissxs = np.divide(tally_results, np.repeat(flux, ng), - where=np.repeat(flux > 0, ng), - out=np.zeros_like(tally_results)) - # Reshape nfissxs array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, ng] - reshape_nfissxs = np.swapaxes(nfissxs.reshape(target_tally_shape), + # Reshape nfissrr array to target shape. Swap x and z axes so that + # shape is now [nx, ny, nz, ng, ng, 1] + reshape_nfissrr = np.swapaxes(nfissrr.reshape(target_tally_shape), 0, 2) - # Nu-fission xs is flipped in both incoming and outgoing energy axes + # Nu-fission rr is flipped in both incoming and outgoing energy axes # as tally results are given in reverse order of energy group - self._nfissxs = np.flip(reshape_nfissxs, axis=3) - self._nfissxs = np.flip(self._nfissxs, axis=4) + reshape_nfissrr = np.flip(reshape_nfissrr, axis=3) + reshape_nfissrr = np.flip(reshape_nfissrr, axis=4) + + # Bank nu-fission rr to nfiss_rate + self._nfiss_rate = np.append(self._nfiss_rate, reshape_nfissrr, + axis=5) + + # Compute nu-fission xs as aggregate of banked nfiss_rate over tally + # window divided by flux. Flux dimensionality increased to account for + # extra dimensionality of nu-fission xs + self._nfissxs = np.divide(np.sum(self._nfiss_rate, axis=5), + extended_flux, where=extended_flux > 0, + out=np.zeros_like(self._nfissxs)) # Openmc source distribution is sum of nu-fission rr in incoming # energies - self._openmc_src = np.sum(self._nfissxs*self._flux[:,:,:,:,np.newaxis], - axis=3) + openmc_src = np.sum(reshape_nfissrr, axis=3) + + # Bank OpenMC source distribution from current batch to + # openmc_src_rate + self._openmc_src_rate = np.append(self._openmc_src_rate, openmc_src, + axis=4) + + # Compute source distribution over entire tally window + self._openmc_src = np.sum(self._openmc_src_rate, axis=4) # Compute k_eff from source distribution - self._keff_bal = np.sum(self._openmc_src) / num_realizations + self._keff_bal = (np.sum(self._openmc_src) / num_realizations / + tally_windows) # Normalize openmc source distribution self._openmc_src /= np.sum(self._openmc_src) * self._norm @@ -1716,44 +2075,62 @@ class CMFDRun(object): tally_results = tallies[tally_id].results[:,0,1] # Filter tally results to include only accelerated regions - current = np.where(np.repeat(flux > 0, 12), tally_results, 0.) + current = np.where(np.repeat(is_cmfd_accel, 12), tally_results, 0.) # Define target tally reshape dimensions for current - target_tally_shape = [nz, ny, nx, 12, ng] + target_tally_shape = [nz, ny, nx, 12, ng, 1] # Reshape current array to target shape. Swap x and z axes so that - # shape is now [nx, ny, nz, ng, 12] + # shape is now [nx, ny, nz, 12, ng, 1] reshape_current = np.swapaxes(current.reshape(target_tally_shape), 0, 2) # Current is flipped in energy axis as tally results are given in # reverse order of energy group - self._current = np.flip(reshape_current, axis=4) + reshape_current = np.flip(reshape_current, axis=4) - # Get p1 scatter xs from CMFD tally 3 + # Bank current to current_rate + self._current_rate = np.append(self._current_rate, reshape_current, + axis=5) + + # Compute current as aggregate of banked current_rate over tally window + self._current = np.sum(self._current_rate, axis=5) + + # Get p1 scatter rr from CMFD tally 3 tally_id = self._tally_ids[3] - tally_results = tallies[tally_id].results[:,0,1] + p1scattrr = tallies[tally_id].results[:,0,1] # Define target tally reshape dimensions for p1 scatter tally - target_tally_shape = [nz, ny, nx, 2, ng] + target_tally_shape = [nz, ny, nx, 2, ng, 1] # Reshape and extract only p1 data from tally results as there is # no need for p0 data - p1scattrr = np.swapaxes(tally_results.reshape(target_tally_shape), - 0, 2)[:,:,:,1,:] + reshape_p1scattrr = np.swapaxes(p1scattrr.reshape(target_tally_shape), + 0, 2)[:,:,:,1,:,:] - # Store p1 scatter xs - # p1 scatter xs is flipped in energy axis as tally results are given in + # p1-scatter rr is flipped in energy axis as tally results are given in # reverse order of energy group - with np.errstate(divide='ignore', invalid='ignore'): - self._p1scattxs = np.divide(np.flip(p1scattrr, axis=3), self._flux, - where=self._flux > 0, - out=np.zeros_like(p1scattrr)) + reshape_p1scattrr = np.flip(reshape_p1scattrr, axis=3) - # Calculate and store diffusion coefficient - with np.errstate(divide='ignore', invalid='ignore'): - self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * - (self._totalxs - self._p1scattxs)), 0.) + # Bank p1-scatter rr to p1scatt_rate + self._p1scatt_rate = np.append(self._p1scatt_rate, reshape_p1scattrr, + axis=4) + + # Compute p1-scatter xs as aggregate of banked p1scatt_rate over tally + # window divided by flux + self._p1scattxs = np.divide(np.sum(self._p1scatt_rate, axis=4), + self._flux, where=self._flux > 0, + out=np.zeros_like(self._p1scattxs)) + + if self._set_reference_params: + # Set diffusion coefficients based on reference value + self._diffcof = np.where(self._flux > 0, + self._ref_d[None, None, None, :], 0.0) + else: + # Calculate and store diffusion coefficient + with np.errstate(divide='ignore', invalid='ignore'): + self._diffcof = np.where(self._flux > 0, 1.0 / (3.0 * + (self._totalxs-self._p1scattxs)), 0.) def _compute_effective_downscatter(self): """Changes downscatter rate for zero upscatter""" @@ -1781,10 +2158,9 @@ class CMFDRun(object): siga2 = sigt2 - sigs22 - sigs21 # Compute effective downscatter XS - with np.errstate(divide='ignore', invalid='ignore'): - sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, - where=flux1 > 0, - out=np.zeros_like(flux2)) + sigs12_eff = sigs12 - sigs21 * np.divide(flux2, flux1, + where=flux1 > 0, + out=np.zeros_like(flux2)) # Recompute total cross sections and record self._totalxs[:,:,:,0] = siga1 + sigs11 + sigs12_eff @@ -1851,9 +2227,7 @@ class CMFDRun(object): """ # Extract spatial indices - nx = self._indices[0] - ny = self._indices[1] - nz = self._indices[2] + nx, ny, nz = self._indices[:3] # Logical for determining whether region of interest is accelerated # region @@ -1993,7 +2367,7 @@ class CMFDRun(object): def _precompute_matrix_indices(self): """Computes the indices and row/column data used to populate CMFD CSR matrices. These indices are used in _build_loss_matrix and - _build_prod_matrix + _build_prod_matrix. """ # Extract energy group indices @@ -2226,10 +2600,9 @@ class CMFDRun(object): # a cell borders a reflector region on the left current_in_left = self._current[:,:,:,_CURRENTS['in_left'],:] current_out_left = self._current[:,:,:,_CURRENTS['out_left'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_left, current_out_left, - where=current_out_left > 1.0e-10, - out=np.ones_like(current_out_left)) + ref_albedo = np.divide(current_in_left, current_out_left, + where=current_out_left > 1.0e-10, + out=np.ones_like(current_out_left)) # Diffusion coefficient of neighbor to left neig_dc = np.roll(self._diffcof, 1, axis=0) @@ -2256,10 +2629,9 @@ class CMFDRun(object): # a cell borders a reflector region on the right current_in_right = self._current[:,:,:,_CURRENTS['in_right'],:] current_out_right = self._current[:,:,:,_CURRENTS['out_right'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_right, current_out_right, - where=current_out_right > 1.0e-10, - out=np.ones_like(current_out_right)) + ref_albedo = np.divide(current_in_right, current_out_right, + where=current_out_right > 1.0e-10, + out=np.ones_like(current_out_right)) # Diffusion coefficient of neighbor to right neig_dc = np.roll(self._diffcof, -1, axis=0) @@ -2286,10 +2658,9 @@ class CMFDRun(object): # a cell borders a reflector region on the back current_in_back = self._current[:,:,:,_CURRENTS['in_back'],:] current_out_back = self._current[:,:,:,_CURRENTS['out_back'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_back, current_out_back, - where=current_out_back > 1.0e-10, - out=np.ones_like(current_out_back)) + ref_albedo = np.divide(current_in_back, current_out_back, + where=current_out_back > 1.0e-10, + out=np.ones_like(current_out_back)) # Diffusion coefficient of neighbor to back neig_dc = np.roll(self._diffcof, 1, axis=1) @@ -2316,10 +2687,9 @@ class CMFDRun(object): # a cell borders a reflector region in the front current_in_front = self._current[:,:,:,_CURRENTS['in_front'],:] current_out_front = self._current[:,:,:,_CURRENTS['out_front'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_front, current_out_front, - where=current_out_front > 1.0e-10, - out=np.ones_like(current_out_front)) + ref_albedo = np.divide(current_in_front, current_out_front, + where=current_out_front > 1.0e-10, + out=np.ones_like(current_out_front)) # Diffusion coefficient of neighbor to front neig_dc = np.roll(self._diffcof, -1, axis=1) @@ -2346,10 +2716,9 @@ class CMFDRun(object): # a cell borders a reflector region on the bottom current_in_bottom = self._current[:,:,:,_CURRENTS['in_bottom'],:] current_out_bottom = self._current[:,:,:,_CURRENTS['out_bottom'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_bottom, current_out_bottom, - where=current_out_bottom > 1.0e-10, - out=np.ones_like(current_out_bottom)) + ref_albedo = np.divide(current_in_bottom, current_out_bottom, + where=current_out_bottom > 1.0e-10, + out=np.ones_like(current_out_bottom)) # Diffusion coefficient of neighbor to bottom neig_dc = np.roll(self._diffcof, 1, axis=2) @@ -2376,10 +2745,9 @@ class CMFDRun(object): # a cell borders a reflector region on the top current_in_top = self._current[:,:,:,_CURRENTS['in_top'],:] current_out_top = self._current[:,:,:,_CURRENTS['out_top'],:] - with np.errstate(divide='ignore', invalid='ignore'): - ref_albedo = np.divide(current_in_top, current_out_top, - where=current_out_top > 1.0e-10, - out=np.ones_like(current_out_top)) + ref_albedo = np.divide(current_in_top, current_out_top, + where=current_out_top > 1.0e-10, + out=np.ones_like(current_out_top)) # Diffusion coefficient of neighbor to top neig_dc = np.roll(self._diffcof, -1, axis=2) @@ -2605,16 +2973,16 @@ class CMFDRun(object): def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = openmc.capi.Mesh() - # Store id of Mesh object + cmfd_mesh = openmc.capi.RegularMesh() + # Store id of mesh object self._mesh_id = cmfd_mesh.id - # Set dimension and parameters of Mesh object + # Set dimension and parameters of mesh object cmfd_mesh.dimension = self._mesh.dimension cmfd_mesh.set_parameters(lower_left=self._mesh.lower_left, upper_right=self._mesh.upper_right, width=self._mesh.width) - # Create Mesh Filter object, stored internally + # Create mesh Filter object, stored internally mesh_filter = openmc.capi.MeshFilter() # Set mesh for Mesh Filter mesh_filter.mesh = cmfd_mesh diff --git a/openmc/data/library.py b/openmc/data/library.py index 905dbbe4a7..d5f18aa170 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -1,5 +1,6 @@ import os import xml.etree.ElementTree as ET +import pathlib import h5py @@ -48,19 +49,30 @@ class DataLibrary(EqualityMixin): Parameters ---------- - filename : str + filename : str or Path Path to the file to be registered. + If an ``xml`` file, treat as the depletion chain file without + materials. """ - # Support pathlib - # TODO: Remove when support is Python 3.6+ only - filename = str(filename) + if not isinstance(filename, pathlib.Path): + path = pathlib.Path(filename) + else: + path = filename - with h5py.File(filename, 'r') as h5file: - filetype = h5file.attrs['filetype'].decode()[5:] - materials = list(h5file) + if path.suffix == '.xml': + filetype = 'depletion_chain' + materials = [] + elif path.suffix == '.h5': + with h5py.File(path, 'r') as h5file: + filetype = h5file.attrs['filetype'].decode()[5:] + materials = list(h5file) + else: + raise ValueError( + "File type {} not supported by {}" + .format(path.name, self.__class__.__name__)) - library = {'path': filename, 'type': filetype, 'materials': materials} + library = {'path': str(path), 'type': filetype, 'materials': materials} self.libraries.append(library) def export_to_xml(self, path='cross_sections.xml'): @@ -85,8 +97,11 @@ class DataLibrary(EqualityMixin): dir_element.text = os.path.realpath(common_dir) for library in self.libraries: - lib_element = ET.SubElement(root, "library") - lib_element.set('materials', ' '.join(library['materials'])) + if library['type'] == "depletion_chain": + lib_element = ET.SubElement(root, "depletion_chain") + else: + lib_element = ET.SubElement(root, "library") + lib_element.set('materials', ' '.join(library['materials'])) lib_element.set('path', os.path.relpath(library['path'], common_dir)) lib_element.set('type', library['type']) @@ -106,7 +121,7 @@ class DataLibrary(EqualityMixin): ---------- path : str, optional Path to XML file to read. If not provided, the - `OPENMC_CROSS_SECTIONS` environment variable will be used. + :envvar:`OPENMC_CROSS_SECTIONS` environment variable will be used. Returns ------- @@ -146,4 +161,13 @@ class DataLibrary(EqualityMixin): 'materials': materials} data.libraries.append(library) + # get depletion chain data + + dep_node = root.find("depletion_chain") + if dep_node is not None: + filename = os.path.join(directory, dep_node.attrib['path']) + library = {'path': filename, 'type': 'depletion_chain', + 'materials': []} + data.libraries.append(library) + return data diff --git a/openmc/data/photon.py b/openmc/data/photon.py index da576b5c8f..0d0e527533 100644 --- a/openmc/data/photon.py +++ b/openmc/data/photon.py @@ -929,13 +929,12 @@ class IncidentPhoton(EqualityMixin): The point-wise heating cross section is calculated as: .. math:: - \sigma_{Hx}(E) &= (E - \overline{E}_x(E)) \cdot \sigma_x(E), x \in \left\{I, PP, PE \right\} - - \overline{E}_I(E) &= \frac {\int E' \sigma_I (E,E',\mu) d\mu} {\int \sigma_I (E,E',\mu) d\mu} - - \overline{E}_{PP} &= 2 m_e c^2 = 1.022 \times 10^6 eV - + \begin{aligned} + \sigma_{Hx}(E) &= (E - \overline{E}_x(E)) \cdot \sigma_x(E), x \in \left\{I, PP, PE \right\} \\ + \overline{E}_I(E) &= \frac {\int E' \sigma_I (E,E',\mu) d\mu} {\int \sigma_I (E,E',\mu) d\mu} \\ + \overline{E}_{PP} &= 2 m_e c^2 = 1.022 \times 10^6 eV \\ \overline{E}_{PE} &= E(\text{fluorescent photons}) + \end{aligned} The differential cross section representation for incoherent scattering can be found in the theory manual. diff --git a/openmc/deplete/abc.py b/openmc/deplete/abc.py index 8502909a21..2e598fb74a 100644 --- a/openmc/deplete/abc.py +++ b/openmc/deplete/abc.py @@ -8,7 +8,10 @@ from collections import namedtuple import os from pathlib import Path from abc import ABCMeta, abstractmethod +from xml.etree import ElementTree as ET +from warnings import warn +from openmc.data import DataLibrary from .chain import Chain OperatorResult = namedtuple('OperatorResult', ['k', 'rates']) @@ -43,8 +46,9 @@ class TransportOperator(metaclass=ABCMeta): Parameters ---------- chain_file : str, optional - Path to the depletion chain XML file. Defaults to the - :envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists. + Path to the depletion chain XML file. Defaults to the file + listed under ``depletion_chain`` in + :envvar:`OPENMC_CROSS_SECTIONS` environment variable. Attributes ---------- @@ -62,8 +66,21 @@ class TransportOperator(metaclass=ABCMeta): if chain_file is None: chain_file = os.environ.get("OPENMC_DEPLETE_CHAIN", None) if chain_file is None: - raise IOError("No chain specified, either manually or in " - "environment variable OPENMC_DEPLETE_CHAIN.") + data = DataLibrary.from_xml() + # search for depletion_chain path from end of list + for lib in reversed(data.libraries): + if lib['type'] == 'depletion_chain': + break + else: + raise IOError( + "No chain specified, either manually or " + "under depletion_chain in environment variable " + "OPENMC_CROSS_SECTIONS.") + chain_file = lib['path'] + else: + warn("Use of OPENMC_DEPLETE_CHAIN is deprecated in favor " + "of adding depletion_chain to OPENMC_CROSS_SECTIONS", + FutureWarning) self.chain = Chain.from_xml(chain_file) @abstractmethod diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 4635d99d15..b8b6a3644a 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -107,7 +107,8 @@ class Chain(object): yield sublibrary files. The depletion chain used during a depletion simulation is indicated by either an argument to :class:`openmc.deplete.Operator` or through the - :envvar:`OPENMC_DEPLETE_CHAIN` environment variable. + ``depletion_chain`` item in the :envvar:`OPENMC_CROSS_SECTIONS` + environment variable. Attributes ---------- diff --git a/openmc/deplete/integrator/cecm.py b/openmc/deplete/integrator/cecm.py index 42024863a1..b1bcec448d 100644 --- a/openmc/deplete/integrator/cecm.py +++ b/openmc/deplete/integrator/cecm.py @@ -17,15 +17,13 @@ def cecm(operator, timesteps, power=None, power_density=None, print_out=True): midpoint on corrector. This algorithm is mathematically defined as: .. math:: - y' &= A(y, t) y(t) - - A_p &= A(y_n, t_n) - - y_m &= \text{expm}(A_p h/2) y_n - - A_c &= A(y_m, t_n + h/2) - + \begin{aligned} + y' &= A(y, t) y(t) \\ + A_p &= A(y_n, t_n) \\ + y_m &= \text{expm}(A_p h/2) y_n \\ + A_c &= A(y_m, t_n + h/2) \\ y_{n+1} &= \text{expm}(A_c h) y_n + \end{aligned} Parameters ---------- diff --git a/openmc/deplete/integrator/celi.py b/openmc/deplete/integrator/celi.py index 0ba86d5850..e7b8014990 100644 --- a/openmc/deplete/integrator/celi.py +++ b/openmc/deplete/integrator/celi.py @@ -12,10 +12,12 @@ def _celi_f1(chain, rates): return 5/12 * chain.form_matrix(rates[0]) + \ 1/12 * chain.form_matrix(rates[1]) + def _celi_f2(chain, rates): return 1/12 * chain.form_matrix(rates[0]) + \ 5/12 * chain.form_matrix(rates[1]) + def celi(operator, timesteps, power=None, power_density=None, print_out=True): r"""Deplete using the CE/LI CFQ4 algorithm. @@ -27,16 +29,14 @@ def celi(operator, timesteps, power=None, power_density=None, interpolation on corrector. This algorithm is mathematically defined as: .. math:: - y' &= A(y, t) y(t) - - A_0 &= A(y_n, t_n) - - y_p &= \text{expm}(h A_0) y_n - - A_1 &= A(y_p, t_n + h) - + \begin{aligned} + y' &= A(y, t) y(t) \\ + A_0 &= A(y_n, t_n) \\ + y_p &= \text{expm}(h A_0) y_n \\ + A_1 &= A(y_p, t_n + h) \\ y_{n+1} &= \text{expm}(\frac{h}{12} A_0 + \frac{5h}{12} A1) \text{expm}(\frac{5h}{12} A_0 + \frac{h}{12} A1) y_n + \end{aligned} Parameters ---------- @@ -90,6 +90,7 @@ def celi(operator, timesteps, power=None, power_density=None, # Create results, write to disk Results.save(operator, x, op_results, [t, t], p, i_res + len(timesteps)) + def celi_inner(operator, vec, p, i, i_res, t, dt, print_out): """ The inner loop of CE/LI CFQ4. diff --git a/openmc/deplete/integrator/cf4.py b/openmc/deplete/integrator/cf4.py index e93b22d5cf..fecb917517 100644 --- a/openmc/deplete/integrator/cf4.py +++ b/openmc/deplete/integrator/cf4.py @@ -11,22 +11,26 @@ from ..results import Results def _cf4_f1(chain, rates): return 1/2 * chain.form_matrix(rates) + def _cf4_f2(chain, rates): return -1/2 * chain.form_matrix(rates[0]) + \ chain.form_matrix(rates[1]) + def _cf4_f3(chain, rates): return 1/4 * chain.form_matrix(rates[0]) + \ 1/6 * chain.form_matrix(rates[1]) + \ 1/6 * chain.form_matrix(rates[2]) + \ -1/12 * chain.form_matrix(rates[3]) + def _cf4_f4(chain, rates): return -1/12 * chain.form_matrix(rates[0]) + \ 1/6 * chain.form_matrix(rates[1]) + \ 1/6 * chain.form_matrix(rates[2]) + \ 1/4 * chain.form_matrix(rates[3]) + def cf4(operator, timesteps, power=None, power_density=None, print_out=True): r"""Deplete using the CF4 algorithm. @@ -35,22 +39,17 @@ def cf4(operator, timesteps, power=None, power_density=None, print_out=True): This algorithm is mathematically defined as: .. math:: - F_1 &= h A(y_0) - - y_1 &= \text{expm}(1/2 F_1) y_0 - - F_2 &= h A(y_1) - - y_2 &= \text{expm}(1/2 F_2) y_0 - - F_3 &= h A(y_2) - - y_3 &= \text{expm}(-1/2 F_1 + F_3) y_1 - - F_4 &= h A(y_3) - + \begin{aligned} + F_1 &= h A(y_0) \\ + y_1 &= \text{expm}(1/2 F_1) y_0 \\ + F_2 &= h A(y_1) \\ + y_2 &= \text{expm}(1/2 F_2) y_0 \\ + F_3 &= h A(y_2) \\ + y_3 &= \text{expm}(-1/2 F_1 + F_3) y_1 \\ + F_4 &= h A(y_3) \\ y_4 &= \text{expm}( 1/4 F_1 + 1/6 F_2 + 1/6 F_3 - 1/12 F_4) \text{expm}(-1/12 F_1 + 1/6 F_2 + 1/6 F_3 + 1/4 F_4) y_0 + \end{aligned} Parameters ---------- diff --git a/openmc/deplete/integrator/epc_rk4.py b/openmc/deplete/integrator/epc_rk4.py index 3789f36985..58c4d08f7e 100644 --- a/openmc/deplete/integrator/epc_rk4.py +++ b/openmc/deplete/integrator/epc_rk4.py @@ -25,21 +25,16 @@ def epc_rk4(operator, timesteps, power=None, power_density=None, print_out=True) This algorithm is mathematically defined as: .. math:: - F_1 &= h A(y_0) - - y_1 &= \text{expm}(1/2 F_1) y_0 - - F_2 &= h A(y_1) - - y_2 &= \text{expm}(1/2 F_2) y_0 - - F_3 &= h A(y_2) - - y_3 &= \text{expm}(F_3) y_0 - - F_4 &= h A(y_3) - + \begin{aligned} + F_1 &= h A(y_0) \\ + y_1 &= \text{expm}(1/2 F_1) y_0 \\ + F_2 &= h A(y_1) \\ + y_2 &= \text{expm}(1/2 F_2) y_0 \\ + F_3 &= h A(y_2) \\ + y_3 &= \text{expm}(F_3) y_0 \\ + F_4 &= h A(y_3) \\ y_4 &= \text{expm}(1/6 F_1 + 1/3 F_2 + 1/3 F_3 + 1/6 F_4) y_0 + \end{aligned} Parameters ---------- diff --git a/openmc/deplete/integrator/leqi.py b/openmc/deplete/integrator/leqi.py index 9d221e877c..66b0c253ad 100644 --- a/openmc/deplete/integrator/leqi.py +++ b/openmc/deplete/integrator/leqi.py @@ -16,12 +16,14 @@ def _leqi_f1(chain, inputs): dt_l, dt = inputs[2], inputs[3] return -dt / (12 * dt_l) * f1 + (dt + 6 * dt_l) / (12 * dt_l) * f2 + def _leqi_f2(chain, inputs): f1 = chain.form_matrix(inputs[0]) f2 = chain.form_matrix(inputs[1]) dt_l, dt = inputs[2], inputs[3] return -5 * dt / (12 * dt_l) * f1 + (5 * dt + 6 * dt_l) / (12 * dt_l) * f2 + def _leqi_f3(chain, inputs): f1 = chain.form_matrix(inputs[0]) f2 = chain.form_matrix(inputs[1]) @@ -31,6 +33,7 @@ def _leqi_f3(chain, inputs): (dt**2 + 6*dt*dt_l + 5*dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + \ dt_l / (12 * (dt + dt_l)) * f3 + def _leqi_f4(chain, inputs): f1 = chain.form_matrix(inputs[0]) f2 = chain.form_matrix(inputs[1]) @@ -40,6 +43,7 @@ def _leqi_f4(chain, inputs): (dt**2 + 2*dt*dt_l + dt_l**2) / (12 * dt_l * (dt + dt_l)) * f2 + \ (4 * dt * dt_l + 5 * dt_l**2) / (12 * dt_l * (dt + dt_l)) * f3 + def leqi(operator, timesteps, power=None, power_density=None, print_out=True): r"""Deplete using the LE/QI CFQ4 algorithm. @@ -50,29 +54,22 @@ def leqi(operator, timesteps, power=None, power_density=None, print_out=True): interpolation on corrector. This algorithm is mathematically defined as: .. math:: - y' &= A(y, t) y(t) - - A_{last} &= A(y_{n-1}, t_n - h_1) - - A_0 &= A(y_n, t_n) - - F_1 &= \frac{-h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+h_2)}{12h_1} A_0 - - F_2 &= \frac{-5h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+5h_2)}{12h_1} A_0 - - y_p &= \text{expm}(F_2) \text{expm}(F_1) y_n - - A_1 &= A(y_p, t_n + h_2) - + \begin{aligned} + y' &= A(y, t) y(t) \\ + A_{last} &= A(y_{n-1}, t_n - h_1) \\ + A_0 &= A(y_n, t_n) \\ + F_1 &= \frac{-h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+h_2)}{12h_1} A_0 \\ + F_2 &= \frac{-5h_2^2}{12h_1} A_{last} + \frac{h_2(6h_1+5h_2)}{12h_1} A_0 \\ + y_p &= \text{expm}(F_2) \text{expm}(F_1) y_n \\ + A_1 &= A(y_p, t_n + h_2) \\ F_3 &= \frac{-h_2^3}{12 h_1 (h_1 + h_2)} A_{last} + \frac{h_2 (5 h_1^2 + 6 h_2 h_1 + h_2^2)}{12 h_1 (h_1 + h_2)} A_0 + - \frac{h_2 h_1)}{12 (h_1 + h_2)} A_1 - + \frac{h_2 h_1)}{12 (h_1 + h_2)} A_1 \\ F_4 &= \frac{-h_2^3}{12 h_1 (h_1 + h_2)} A_{last} + \frac{h_2 (h_1^2 + 2 h_2 h_1 + h_2^2)}{12 h_1 (h_1 + h_2)} A_0 + - \frac{h_2 (5 h_1^2 + 4 h_2 h_1)}{12 h_1 (h_1 + h_2)} A_1 - + \frac{h_2 (5 h_1^2 + 4 h_2 h_1)}{12 h_1 (h_1 + h_2)} A_1 \\ y_{n+1} &= \text{expm}(F_4) \text{expm}(F_3) y_n + \end{aligned} It is initialized using the CE/LI algorithm. diff --git a/openmc/deplete/integrator/predictor.py b/openmc/deplete/integrator/predictor.py index f670a125ee..6872ae6ef6 100644 --- a/openmc/deplete/integrator/predictor.py +++ b/openmc/deplete/integrator/predictor.py @@ -15,11 +15,11 @@ def predictor(operator, timesteps, power=None, power_density=None, mathematically defined as: .. math:: - y' &= A(y, t) y(t) - - A_p &= A(y_n, t_n) - + \begin{aligned} + y' &= A(y, t) y(t) \\ + A_p &= A(y_n, t_n) \\ y_{n+1} &= \text{expm}(A_p h) y_n + \end{aligned} Parameters ---------- diff --git a/openmc/deplete/operator.py b/openmc/deplete/operator.py index 66cac392d9..d284d98567 100644 --- a/openmc/deplete/operator.py +++ b/openmc/deplete/operator.py @@ -64,8 +64,9 @@ class Operator(TransportOperator): settings : openmc.Settings OpenMC Settings object chain_file : str, optional - Path to the depletion chain XML file. Defaults to the - :envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists. + Path to the depletion chain XML file. Defaults to the file + listed under ``depletion_chain`` in + :envvar:`OPENMC_CROSS_SECTIONS` environment variable. prev_results : ResultsList, optional Results from a previous depletion calculation. If this argument is specified, the depletion calculation will start from the latest state diff --git a/openmc/filter.py b/openmc/filter.py index 49d3ff5de1..dbdaf2e19e 100644 --- a/openmc/filter.py +++ b/openmc/filter.py @@ -161,8 +161,8 @@ class Filter(IDManagerMixin, metaclass=FilterMeta): Keyword arguments ----------------- meshes : dict - Dictionary mapping integer IDs to openmc.Mesh objects. Only used - for openmc.MeshFilter objects. + Dictionary mapping integer IDs to openmc.MeshBase objects. Only + used for openmc.MeshFilter objects. """ @@ -587,15 +587,15 @@ class MeshFilter(Filter): Parameters ---------- - mesh : openmc.Mesh - The Mesh object that events will be tallied onto + mesh : openmc.MeshBase + The mesh object that events will be tallied onto filter_id : int Unique identifier for the filter Attributes ---------- - mesh : openmc.Mesh - The Mesh object that events will be tallied onto + mesh : openmc.MeshBase + The mesh object that events will be tallied onto id : int Unique identifier for the filter bins : list of tuple @@ -610,6 +610,11 @@ class MeshFilter(Filter): self.mesh = mesh self.id = filter_id + def __hash__(self): + string = type(self).__name__ + '\n' + string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) + return hash(string) + def __repr__(self): string = type(self).__name__ + '\n' string += '{: <16}=\t{}\n'.format('\tMesh ID', self.mesh.id) @@ -641,7 +646,7 @@ class MeshFilter(Filter): @mesh.setter def mesh(self, mesh): - cv.check_type('filter mesh', mesh, openmc.Mesh) + cv.check_type('filter mesh', mesh, openmc.MeshBase) self._mesh = mesh self.bins = list(mesh.indices) @@ -683,7 +688,7 @@ class MeshFilter(Filter): # Initialize dictionary to build Pandas Multi-index column filter_dict = {} - # Append Mesh ID as outermost index of multi-index + # Append mesh ID as outermost index of multi-index mesh_key = 'mesh {}'.format(self.mesh.id) # Find mesh dimensions - use 3D indices for simplicity @@ -745,17 +750,17 @@ class MeshSurfaceFilter(MeshFilter): Parameters ---------- - mesh : openmc.Mesh - The Mesh object that events will be tallied onto + mesh : openmc.MeshBase + The mesh object that events will be tallied onto filter_id : int Unique identifier for the filter Attributes ---------- bins : Integral - The Mesh ID - mesh : openmc.Mesh - The Mesh object that events will be tallied onto + The mesh ID + mesh : openmc.MeshBase + The mesh object that events will be tallied onto id : int Unique identifier for the filter bins : list of tuple @@ -770,11 +775,11 @@ class MeshSurfaceFilter(MeshFilter): @MeshFilter.mesh.setter def mesh(self, mesh): - cv.check_type('filter mesh', mesh, openmc.Mesh) + cv.check_type('filter mesh', mesh, openmc.MeshBase) self._mesh = mesh # Take the product of mesh indices and current names - n_dim = len(mesh.dimension) + n_dim = mesh.n_dimension self.bins = [mesh_tuple + (surf,) for mesh_tuple, surf in product(mesh.indices, _CURRENT_NAMES[:4*n_dim])] @@ -812,7 +817,7 @@ class MeshSurfaceFilter(MeshFilter): # Initialize dictionary to build Pandas Multi-index column filter_dict = {} - # Append Mesh ID as outermost index of multi-index + # Append mesh ID as outermost index of multi-index mesh_key = 'mesh {}'.format(self.mesh.id) # Find mesh dimensions - use 3D indices for simplicity diff --git a/openmc/lattice.py b/openmc/lattice.py index fb0a7a1bd8..4776222da2 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -36,7 +36,7 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): outer : openmc.Universe A universe to fill all space outside the lattice universes : Iterable of Iterable of openmc.Universe - A two- or three-dimensional list/array of universes filling each element + A two-or three-dimensional list/array of universes filling each element of the lattice """ @@ -141,6 +141,10 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): center = group['center'][()] pitch = group['pitch'][()] outer = group['outer'][()] + if 'orientation' in group: + orientation = group['orientation'][()].decode() + else: + orientation = "y" universe_ids = group['universes'][()] @@ -148,67 +152,124 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): lattice = openmc.HexLattice(lattice_id, name) lattice.center = center lattice.pitch = pitch - + lattice.orientation = orientation # If the Universe specified outer the Lattice is not void if outer >= 0: lattice.outer = universes[outer] + if orientation == "y": + # Build array of Universe pointers for the Lattice. Note that + # we need to convert between the HDF5's square array of + # (x, alpha, z) to the Python API's format of a ragged nested + # list of (z, ring, theta). + uarray = [] + for z in range(n_axial): + # Add a list for this axial level. + uarray.append([]) + x = n_rings - 1 + a = 2*n_rings - 2 + for r in range(n_rings - 1, 0, -1): + # Add a list for this ring. + uarray[-1].append([]) - # Build array of Universe pointers for the Lattice. Note that - # we need to convert between the HDF5's square array of - # (x, alpha, z) to the Python API's format of a ragged nested - # list of (z, ring, theta). - uarray = [] - for z in range(n_axial): - # Add a list for this axial level. - uarray.append([]) - x = n_rings - 1 - a = 2*n_rings - 2 - for r in range(n_rings - 1, 0, -1): - # Add a list for this ring. - uarray[-1].append([]) + # Climb down the top-right. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + x += 1 + a -= 1 - # Climb down the top-right. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) - x += 1 + # Climb down the right. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + a -= 1 + + # Climb down the bottom-right. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + x -= 1 + + # Climb up the bottom-left. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + x -= 1 + a += 1 + + # Climb up the left. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + a += 1 + + # Climb up the top-left. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, a, x]) + x += 1 + + # Move down to the next ring. a -= 1 - # Climb down the right. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) + # Convert the ids into Universe objects. + uarray[-1][-1] = [universes[u_id] + for u_id in uarray[-1][-1]] + + # Handle the degenerate center ring separately. + u_id = universe_ids[z, a, x] + uarray[-1].append([universes[u_id]]) + else: + # Build array of Universe pointers for the Lattice. Note that + # we need to convert between the HDF5's square array of + # (alpha, y, z) to the Python API's format of a ragged nested + # list of (z, ring, theta). + uarray = [] + for z in range(n_axial): + # Add a list for this axial level. + uarray.append([]) + a = 2*n_rings - 2 + y = n_rings - 1 + for r in range(n_rings - 1, 0, -1): + # Add a list for this ring. + uarray[-1].append([]) + + # Climb down the bottom-right. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + y -= 1 + + # Climb across the bottom. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + a -= 1 + + # Climb up the bottom-left. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + a -= 1 + y +=1 + + # Climb up the top-left. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + y += 1 + + # Climb across the top. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + a += 1 + + # Climb down the top-right. + for i in range(r): + uarray[-1][-1].append(universe_ids[z, y, a]) + a += 1 + y -= 1 + + # Move down to the next ring. a -= 1 - # Climb down the bottom-right. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) - x -= 1 + # Convert the ids into Universe objects. + uarray[-1][-1] = [universes[u_id] + for u_id in uarray[-1][-1]] - # Climb up the bottom-left. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) - x -= 1 - a += 1 - - # Climb up the left. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) - a += 1 - - # Climb up the top-left. - for i in range(r): - uarray[-1][-1].append(universe_ids[z, a, x]) - x += 1 - - # Move down to the next ring. - a -= 1 - - # Convert the ids into Universe objects. - uarray[-1][-1] = [universes[u_id] - for u_id in uarray[-1][-1]] - - # Handle the degenerate center ring separately. - u_id = universe_ids[z, a, x] - uarray[-1].append([universes[u_id]]) + # Handle the degenerate center ring separately. + u_id = universe_ids[z, y, a] + uarray[-1].append([universes[u_id]]) # Add the universes to the lattice. if len(pitch) == 2: @@ -346,7 +407,9 @@ class Lattice(IDManagerMixin, metaclass=ABCMeta): Lattice element indices. For a rectangular lattice, the indices are given in the :math:`(x,y)` or :math:`(x,y,z)` coordinate system. For hexagonal lattices, they are given in the :math:`x,\alpha` or - :math:`x,\alpha,z` coordinate systems. + :math:`x,\alpha,z` coordinate systems for "y" orientations and + :math:`\alpha,y` or :math:`\alpha,y,z` coordinate systems for "x" + orientations. Returns ------- @@ -646,7 +709,8 @@ class RectLattice(Lattice): return (x, y, z) def get_universe_index(self, idx): - """Return index in the universes array corresponding to a lattice element index + """Return index in the universes array corresponding + to a lattice element index Parameters ---------- @@ -790,7 +854,8 @@ class RectLattice(Lattice): lat_id = int(get_text(elem, 'id')) name = get_text(elem, 'name') lat = cls(lat_id, name) - lat.lower_left = [float(i) for i in get_text(elem, 'lower_left').split()] + lat.lower_left = [float(i) + for i in get_text(elem, 'lower_left').split()] lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] outer = get_text(elem, 'outer') if outer is not None: @@ -800,7 +865,7 @@ class RectLattice(Lattice): dimension = get_text(elem, 'dimension').split() shape = np.array(dimension, dtype=int)[::-1] uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + get_text(elem, 'universes').split()]) uarray.shape = shape lat.universes = uarray return lat @@ -815,10 +880,15 @@ class HexLattice(Lattice): Most methods for this class use a natural indexing scheme wherein elements are assigned an index corresponding to their position relative to skewed - :math:`(x,\alpha,z)` axes as described fully in - :ref:`hexagonal_indexing`. However, note that when universes are assigned to - lattice elements using the :attr:`HexLattice.universes` property, the array - indices do not correspond to natural indices. + :math:`(x,\alpha,z)` or :math:`(\alpha,y,z)` bases, depending on the lattice + orientation, as described fully in :ref:`hexagonal_indexing`. However, note + that when universes are assigned to lattice elements using the + :attr:`HexLattice.universes` property, the array indices do not correspond + to natural indices. + + .. versionchanged:: 0.11 + The orientation of the lattice can now be changed with the + :attr:`orientation` attribute. Parameters ---------- @@ -855,6 +925,9 @@ class HexLattice(Lattice): possible, where z is the axial index, r is in the ring index (starting from the outermost ring), and i is the index with a ring starting from the top and proceeding clockwise. + orientation : {'x', 'y'} + str by default 'y' orientation of main lattice diagonal another option + - 'x' num_rings : int Number of radial ring positions in the xy-plane num_axial : int @@ -869,11 +942,14 @@ class HexLattice(Lattice): self._num_rings = None self._num_axial = None self._center = None + self._orientation = 'y' def __repr__(self): string = 'HexLattice\n' string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) + string += '{0: <16}{1}{2}\n'.format('\tOrientation', '=\t', + self._orientation) string += '{0: <16}{1}{2}\n'.format('\t# Rings', '=\t', self._num_rings) string += '{0: <16}{1}{2}\n'.format('\t# Axial', '=\t', self._num_axial) string += '{0: <16}{1}{2}\n'.format('\tCenter', '=\t', @@ -902,6 +978,10 @@ class HexLattice(Lattice): def num_rings(self): return self._num_rings + @property + def orientation(self): + return self._orientation + @property def num_axial(self): return self._num_axial @@ -955,6 +1035,11 @@ class HexLattice(Lattice): cv.check_length('lattice center', center, 2, 3) self._center = center + @orientation.setter + def orientation(self, orientation): + cv.check_value('orientation', orientation.lower(), ('x', 'y')) + self._orientation = orientation.lower() + @Lattice.pitch.setter def pitch(self, pitch): cv.check_type('lattice pitch', pitch, Iterable, Real) @@ -1000,7 +1085,7 @@ class HexLattice(Lattice): # Check the center ring. if len(axial_slice[-1]) != 1: msg = 'HexLattice ID={0:d} has the wrong number of ' \ - 'elements in the innermost ring. Only 1 element is ' \ + 'elements in the innermost ring. Only 1 element is ' \ 'allowed in the innermost ring.'.format(self._id) raise ValueError(msg) @@ -1009,7 +1094,7 @@ class HexLattice(Lattice): if len(axial_slice[r]) != 6*(self._num_rings - 1 - r): msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in ring number {1:d} (counting from the '\ - 'outermost ring). This ring should have {2:d} ' \ + 'outermost ring). This ring should have {2:d} ' \ 'elements.'.format(self._id, r, 6*(self._num_rings - 1 - r)) raise ValueError(msg) @@ -1045,7 +1130,7 @@ class HexLattice(Lattice): ------- 3-tuple of int Indices of corresponding lattice element in :math:`(x,\alpha,z)` - bases + or :math:`(\alpha,y,z)` bases numpy.ndarray Carestian coordinates of the point in the corresponding lattice element coordinate system @@ -1059,15 +1144,21 @@ class HexLattice(Lattice): else: z = point[2] - self.center[2] iz = floor(z/self.pitch[1] + 0.5*self.num_axial) - alpha = y - x/sqrt(3.) - ix = floor(x/(sqrt(0.75) * self.pitch[0])) - ia = floor(alpha/self.pitch[0]) - + if self._orientation == 'x': + alpha = y - x*sqrt(3.) + i1 = floor(-alpha/(sqrt(3.0) * self.pitch[0])) + i2 = floor(y/(sqrt(0.75) * self.pitch[0])) + else: + alpha = y - x/sqrt(3.) + i1 = floor(x/(sqrt(0.75) * self.pitch[0])) + i2 = floor(alpha/self.pitch[0]) # Check four lattice elements to see which one is closest based on local # coordinates + indices = [(i1, i2, iz), (i1 + 1, i2, iz), (i1, i2 + 1, iz), + (i1 + 1, i2 + 1, iz)] d_min = np.inf - for idx in [(ix, ia, iz), (ix + 1, ia, iz), (ix, ia + 1, iz), - (ix + 1, ia + 1, iz)]: + + for idx in indices: p = self.get_local_coordinates(point, idx) d = p[0]**2 + p[1]**2 if d < d_min: @@ -1085,7 +1176,8 @@ class HexLattice(Lattice): point : Iterable of float Cartesian coordinates of point idx : Iterable of int - Indices of lattice element in :math:`(x,\alpha,z)` bases + Indices of lattice element in :math:`(x,\alpha,z)` + or :math:`(\alpha,y,z)` bases Returns ------- @@ -1094,8 +1186,17 @@ class HexLattice(Lattice): system """ - x = point[0] - (self.center[0] + sqrt(0.75)*self.pitch[0]*idx[0]) - y = point[1] - (self.center[1] + (0.5*idx[0] + idx[1])*self.pitch[0]) + if self._orientation == 'x': + x = point[0] - (self.center[0] + self.pitch[0]*idx[0] + + 0.5*self.pitch[0]*idx[1]) + y = point[1] - (self.center[1] + + sqrt(0.75)*self.pitch[0]*idx[1]) + else: + x = point[0] - (self.center[0] + + sqrt(0.75)*self.pitch[0]*idx[0]) + y = point[1] - (self.center[1] + + (0.5*idx[0] + idx[1])*self.pitch[0]) + if self._num_axial is None: z = point[2] else: @@ -1104,18 +1205,21 @@ class HexLattice(Lattice): return (x, y, z) def get_universe_index(self, idx): - r"""Return index in the universes array corresponding to a lattice element index + r"""Return index in the universes array corresponding + to a lattice element index Parameters ---------- idx : Iterable of int Lattice element indices in the :math:`(x,\alpha,z)` coordinate - system + system in 'y' orientation case, or indices in the + :math:`(\alpha,y,z)` coordinate system in 'x' one Returns ------- - 2- or 3-tuple of int - Indices used when setting the :attr:`HexLattice.universes` property + 2- or 3-tuple of int + Indices used when setting the :attr:`HexLattice.universes` + property """ @@ -1138,6 +1242,9 @@ class HexLattice(Lattice): else: i_within = 5*g - z + if self._orientation == 'x' and g > 0: + i_within = (i_within + 5*g) % (6*g) + if self.num_axial is None: return (i_ring, i_within) else: @@ -1149,8 +1256,8 @@ class HexLattice(Lattice): Parameters ---------- idx : Iterable of int - Lattice element indices in the :math:`(x,\alpha,z)` coordinate - system + Lattice element indices in the both :math:`(x,\alpha,z)` + and :math:`(\alpha,y,z)` coordinate system Returns ------- @@ -1193,6 +1300,9 @@ class HexLattice(Lattice): self._outer.create_xml_subelement(xml_element) lattice_subelement.set("n_rings", str(self._num_rings)) + # If orientation is "x" export it to XML + if self._orientation == 'x': + lattice_subelement.set("orientation", "x") if self._num_axial is not None: lattice_subelement.set("n_axial", str(self._num_axial)) @@ -1267,6 +1377,7 @@ class HexLattice(Lattice): lat = cls(lat_id, name) lat.center = [float(i) for i in get_text(elem, 'center').split()] lat.pitch = [float(i) for i in get_text(elem, 'pitch').split()] + lat.orientation = get_text(elem, 'orientation', 'y') outer = get_text(elem, 'outer') if outer is not None: lat.outer = get_universe(int(outer)) @@ -1277,13 +1388,13 @@ class HexLattice(Lattice): # Create empty nested lists for one axial level univs = [[None for _ in range(max(6*(n_rings - 1 - r), 1))] - for r in range(n_rings)] + for r in range(n_rings)] if n_axial > 1: univs = [deepcopy(univs) for i in range(n_axial)] - # Get flat array of universes numbers + # Get flat array of universes uarray = np.array([get_universe(int(i)) for i in - get_text(elem, 'universes').split()]) + get_text(elem, 'universes').split()]) # Fill nested lists j = 0 @@ -1291,34 +1402,174 @@ class HexLattice(Lattice): # Get list for a single axial level axial_level = univs[z] if n_axial > 1 else univs - # Start iterating from top - x, alpha = 0, n_rings - 1 - while True: - # Set entry in list based on (x,alpha,z) coordinates - _, i_ring, i_within = lat.get_universe_index((x, alpha, z)) - axial_level[i_ring][i_within] = uarray[j] + if lat.orientation == 'y': + # Start iterating from top + x, alpha = 0, n_rings - 1 + while True: + # Set entry in list based on (x,alpha,z) coordinates + _, i_ring, i_within = lat.get_universe_index((x, alpha, z)) + axial_level[i_ring][i_within] = uarray[j] - # Move to the right - x += 2 - alpha -= 1 - if not lat.is_valid_index((x, alpha, z)): - # Move down in y direction - alpha += x - 1 - x = 1 - x + # Move to the right + x += 2 + alpha -= 1 if not lat.is_valid_index((x, alpha, z)): - # Move to the right - x += 2 - alpha -= 1 + # Move down in y direction + alpha += x - 1 + x = 1 - x if not lat.is_valid_index((x, alpha, z)): - # Reached the bottom + # Move to the right + x += 2 + alpha -= 1 + if not lat.is_valid_index((x, alpha, z)): + # Reached the bottom + break + j += 1 + else: + # Start iterating from top + alpha, y = 1 - n_rings, n_rings - 1 + while True: + # Set entry in list based on (alpha,y,z) coordinates + _, i_ring, i_within = lat.get_universe_index((alpha, y, z)) + axial_level[i_ring][i_within] = uarray[j] + + # Move to the right + alpha += 1 + if not lat.is_valid_index((alpha, y, z)): + # Move down to next row + alpha = 1 - n_rings + y -= 1 + + # Check if we've reached the bottom + if y == -n_rings: break - j += 1 + + while not lat.is_valid_index((alpha, y, z)): + # Move to the right + alpha += 1 + j += 1 + lat.universes = univs return lat def _repr_axial_slice(self, universes): """Return string representation for the given 2D group of universes. + The 'universes' argument should be a list of lists of universes where + each sub-list represents a single ring. The first list should be the + outer ring. + """ + if self._orientation == 'x': + return self._repr_axial_slice_x(universes) + else: + return self._repr_axial_slice_y(universes) + + def _repr_axial_slice_x(self, universes): + """Return string representation for the given 2D group of universes + in 'x' orientation case. + + The 'universes' argument should be a list of lists of universes where + each sub-list represents a single ring. The first list should be the + outer ring. + """ + + # Find the largest universe ID and count the number of digits so we can + # properly pad the output string later. + largest_id = max([max([univ._id for univ in ring]) + for ring in universes]) + n_digits = len(str(largest_id)) + pad = ' '*n_digits + id_form = '{: ^' + str(n_digits) + 'd}' + + # Initialize the list for each row. + rows = [[] for i in range(2*self._num_rings - 1)] + middle = self._num_rings - 1 + + # Start with the degenerate first ring. + universe = universes[-1][0] + rows[middle] = [id_form.format(universe._id)] + + # Add universes one ring at a time. + for r in range(1, self._num_rings): + # r_prime increments down while r increments up. + r_prime = self._num_rings - 1 - r + theta = 0 + y = middle + + # Climb down the bottom-right + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].append(id_form.format(universe._id)) + + # Translate the indices. + y += 1 + theta += 1 + + # Climb left across the bottom + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].insert(0, id_form.format(universe._id)) + + # Translate the indices. + theta += 1 + + # Climb up the bottom-left + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].insert(0, id_form.format(universe._id)) + + # Translate the indices. + y -= 1 + theta += 1 + + # Climb up the top-left + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].insert(0, id_form.format(universe._id)) + + # Translate the indices. + y -= 1 + theta += 1 + + # Climb right across the top + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].append(id_form.format(universe._id)) + + # Translate the indices. + theta += 1 + + # Climb down the top-right + for i in range(r): + # Add the universe. + universe = universes[r_prime][theta] + rows[y].append(id_form.format(universe._id)) + + # Translate the indices. + y += 1 + theta += 1 + + # Flip the rows and join each row into a single string. + rows = [pad.join(x) for x in rows] + + # Pad the beginning of the rows so they line up properly. + for y in range(self._num_rings - 1): + rows[y] = (self._num_rings - 1 - y)*pad + rows[y] + rows[-1 - y] = (self._num_rings - 1 - y)*pad + rows[-1 - y] + + # Join the rows together and return the string. + universe_ids = '\n'.join(rows) + return universe_ids + + def _repr_axial_slice_y(self, universes): + """Return string representation for the given 2D group of universes in + 'y' orientation case.. + The 'universes' argument should be a list of lists of universes where each sub-list represents a single ring. The first list should be the outer ring. @@ -1425,7 +1676,7 @@ class HexLattice(Lattice): return universe_ids @staticmethod - def show_indices(num_rings): + def _show_indices_y(num_rings): """Return a diagram of the hexagonal lattice layout with indices. This method can be used to show the proper indices to be used when @@ -1527,3 +1778,124 @@ class HexLattice(Lattice): # Join the rows together and return the string. return '\n'.join(rows) + + @staticmethod + def _show_indices_x(num_rings): + """Return a diagram of the hexagonal lattice with x orientation + layout with indices. + + This method can be used to show the proper indices to be used when + setting the :attr:`HexLattice.universes` property. For example,running + this method with num_rings=3 will return the similar diagram:: + + (0, 8) (0, 9) (0,10) + + (0, 7) (1, 4) (1, 5) (0,11) + + (0, 6) (1, 3) (2, 0) (1, 0) (0, 0) + + (0, 5) (1, 2) (1, 1) (0, 1) + + (0, 4) (0, 3) (0, 2) + + Parameters + ---------- + num_rings : int + Number of rings in the hexagonal lattice + + Returns + ------- + str + Diagram of the hexagonal lattice showing indices in OX orientation + + """ + + # Find the largest string and count the number of digits so we can + # properly pad the output string later + largest_index = 6*(num_rings - 1) + n_digits_index = len(str(largest_index)) + n_digits_ring = len(str(num_rings - 1)) + str_form = '({{:{}}},{{:{}}})'.format(n_digits_ring, n_digits_index) + pad = ' '*(n_digits_index + n_digits_ring + 3) + + # Initialize the list for each row. + rows = [[] for i in range(2*num_rings - 1)] + middle = num_rings - 1 + + # Start with the degenerate first ring. + rows[middle] = [str_form.format(num_rings - 1, 0)] + + # Add universes one ring at a time. + for r in range(1, num_rings): + # r_prime increments down while r increments up. + r_prime = num_rings - 1 - r + theta = 0 + y = middle + + for i in range(r): + # Climb down the bottom-right + rows[y].append(str_form.format(r_prime, theta)) + y += 1 + theta += 1 + + for i in range(r): + # Climb left across the bottom + rows[y].insert(0, str_form.format(r_prime, theta)) + theta += 1 + + for i in range(r): + # Climb up the bottom-left + rows[y].insert(0, str_form.format(r_prime, theta)) + y -= 1 + theta += 1 + + for i in range(r): + # Climb up the top-left + rows[y].insert(0, str_form.format(r_prime, theta)) + y -= 1 + theta += 1 + + for i in range(r): + # Climb right across the top + rows[y].append(str_form.format(r_prime, theta)) + theta += 1 + + for i in range(r): + # Climb down the top-right + rows[y].append(str_form.format(r_prime, theta)) + y += 1 + theta += 1 + + # Flip the rows and join each row into a single string. + rows = [pad.join(x) for x in rows] + + # Pad the beginning of the rows so they line up properly. + for y in range(num_rings - 1): + rows[y] = (num_rings - 1 - y)*pad + rows[y] + rows[-1 - y] = (num_rings - 1 - y)*pad + rows[-1 - y] + + # Join the rows together and return the string. + return '\n\n'.join(rows) + + @staticmethod + def show_indices(num_rings, orientation="y"): + """Return a diagram of the hexagonal lattice layout with indices. + + Parameters + ---------- + num_rings : int + Number of rings in the hexagonal lattice + orientation : {"x", "y"} + Orientation of the hexagonal lattice + + Returns + ------- + str + Diagram of the hexagonal lattice showing indices + + """ + + if orientation == 'x': + return HexLattice._show_indices_x(num_rings) + else: + return HexLattice._show_indices_y(num_rings) diff --git a/openmc/mesh.py b/openmc/mesh.py index ba07ec178b..4186b56a88 100644 --- a/openmc/mesh.py +++ b/openmc/mesh.py @@ -1,17 +1,20 @@ +from abc import ABCMeta from collections.abc import Iterable from numbers import Real, Integral from xml.etree import ElementTree as ET import sys +import warnings import numpy as np import openmc.checkvalue as cv import openmc +from openmc._xml import get_text from openmc.mixin import EqualityMixin, IDManagerMixin -class Mesh(IDManagerMixin): - """A structured Cartesian mesh in one, two, or three dimensions +class MeshBase(IDManagerMixin, metaclass=ABCMeta): + """A mesh that partitions geometry for tallying purposes. Parameters ---------- @@ -26,21 +29,6 @@ class Mesh(IDManagerMixin): Unique identifier for the mesh name : str Name of the mesh - type : str - Type of the mesh - dimension : Iterable of int - The number of mesh cells in each direction. - lower_left : Iterable of float - 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 : Iterable of float - 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 : 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), ...] """ @@ -51,24 +39,95 @@ class Mesh(IDManagerMixin): # Initialize Mesh class attributes self.id = mesh_id self.name = name - self._type = 'regular' + + @property + def name(self): + return self._name + + @name.setter + def name(self, name): + if name is not None: + cv.check_type('name for mesh ID="{0}"'.format(self._id), + name, str) + self._name = name + else: + self._name = '' + + @classmethod + def from_hdf5(cls, group): + """Create mesh from HDF5 group + + Parameters + ---------- + group : h5py.Group + Group in HDF5 file + + Returns + ------- + openmc.MeshBase + Instance of a MeshBase subclass + + """ + + mesh_type = group['type'][()].decode() + if mesh_type == 'regular': + return RegularMesh.from_hdf5(group) + elif mesh_type == 'rectilinear': + return RectilinearMesh.from_hdf5(group) + else: + raise ValueError('Unrecognized mesh type: "' + mesh_type + '"') + + +class RegularMesh(MeshBase): + """A regular Cartesian mesh in one, two, or three dimensions + + Parameters + ---------- + mesh_id : int + Unique identifier for the mesh + name : str + Name of the mesh + + Attributes + ---------- + id : int + Unique identifier for the mesh + name : str + Name of the mesh + dimension : Iterable of int + The number of mesh cells in each direction. + n_dimension : int + Number of mesh dimensions. + lower_left : Iterable of float + 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 : Iterable of float + 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 : Iterable of float + The width of mesh cells in each direction. + indices : Iterable of tuple + An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), + (2, 1, 1), ...] + + """ + + def __init__(self, mesh_id=None, name=''): + super().__init__(mesh_id, name) + self._dimension = None self._lower_left = None self._upper_right = None self._width = None - @property - def name(self): - return self._name - - @property - def type(self): - return self._type - @property def dimension(self): return self._dimension + @property + def n_dimension(self): + return len(self._dimension) + @property def lower_left(self): return self._lower_left @@ -103,23 +162,6 @@ class Mesh(IDManagerMixin): nx, = self.dimension return ((x,) for x in range(1, nx + 1)) - @name.setter - def name(self, name): - if name is not None: - cv.check_type('name for mesh ID="{0}"'.format(self._id), - name, str) - self._name = name - else: - self._name = '' - - @type.setter - def type(self, meshtype): - cv.check_type('type for mesh ID="{0}"'.format(self._id), - meshtype, str) - cv.check_value('type for mesh ID="{0}"'.format(self._id), - meshtype, ['regular']) - self._type = meshtype - @dimension.setter def dimension(self, dimension): cv.check_type('mesh dimension', dimension, Iterable, Integral) @@ -145,7 +187,7 @@ class Mesh(IDManagerMixin): self._width = width def __repr__(self): - string = 'Mesh\n' + string = 'RegularMesh\n' string += '{0: <16}{1}{2}\n'.format('\tID', '=\t', self._id) string += '{0: <16}{1}{2}\n'.format('\tName', '=\t', self._name) string += '{0: <16}{1}{2}\n'.format('\tType', '=\t', self._type) @@ -157,24 +199,10 @@ class Mesh(IDManagerMixin): @classmethod def from_hdf5(cls, group): - """Create mesh from HDF5 group - - Parameters - ---------- - group : h5py.Group - Group in HDF5 file - - Returns - ------- - openmc.Mesh - Mesh instance - - """ mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) # Read and assign mesh properties mesh = cls(mesh_id) - mesh.type = group['type'][()].decode() mesh.dimension = group['dimension'][()] mesh.lower_left = group['lower_left'][()] mesh.upper_right = group['upper_right'][()] @@ -200,8 +228,8 @@ class Mesh(IDManagerMixin): Returns ------- - openmc.Mesh - Mesh instance + openmc.RegularMesh + RegularMesh instance """ cv.check_type('rectangular lattice', lattice, openmc.RectLattice) @@ -228,10 +256,10 @@ class Mesh(IDManagerMixin): element = ET.Element("mesh") element.set("id", str(self._id)) - element.set("type", self._type) - subelement = ET.SubElement(element, "dimension") - subelement.text = ' '.join(map(str, self._dimension)) + if self._dimension is not None: + subelement = ET.SubElement(element, "dimension") + subelement.text = ' '.join(map(str, self._dimension)) subelement = ET.SubElement(element, "lower_left") subelement.text = ' '.join(map(str, self._lower_left)) @@ -246,6 +274,46 @@ class Mesh(IDManagerMixin): return element + @classmethod + def from_xml_element(cls, elem): + """Generate mesh from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Mesh + Mesh generated from XML element + + """ + mesh_id = int(get_text(elem, 'id')) + mesh = cls(mesh_id) + + mesh_type = get_text(elem, 'type') + if mesh_type is not None: + mesh.type = mesh_type + + dimension = get_text(elem, 'dimension') + if dimension is not None: + mesh.dimension = [int(x) for x in dimension.split()] + + lower_left = get_text(elem, 'lower_left') + if lower_left is not None: + mesh.lower_left = [float(x) for x in lower_left.split()] + + upper_right = get_text(elem, 'upper_right') + if upper_right is not None: + mesh.upper_right = [float(x) for x in upper_right.split()] + + width = get_text(elem, 'width') + if width is not None: + mesh.width = [float(x) for x in width.split()] + + return mesh + def build_cells(self, bc=['reflective'] * 6): """Generates a lattice of universes with the same dimensionality as the mesh object. The individual cells/universes produced @@ -374,3 +442,125 @@ class Mesh(IDManagerMixin): root_cell.fill = lattice return root_cell, cells + + +def Mesh(*args, **kwargs): + warnings.warn("Mesh has been renamed RegularMesh. Future versions of " + "OpenMC will not accept the name Mesh.") + return RegularMesh(*args, **kwargs) + + +class RectilinearMesh(MeshBase): + """A 3D rectilinear Cartesian mesh + + Parameters + ---------- + mesh_id : int + Unique identifier for the mesh + name : str + Name of the mesh + + Attributes + ---------- + id : int + Unique identifier for the mesh + name : str + Name of the mesh + n_dimension : int + Number of mesh dimensions (always 3 for a RectilinearMesh). + x_grid : Iterable of float + Mesh boundary points along the x-axis. + y_grid : Iterable of float + Mesh boundary points along the y-axis. + z_grid : Iterable of float + Mesh boundary points along the z-axis. + indices : Iterable of tuple + An iterable of mesh indices for each mesh element, e.g. [(1, 1, 1), + (2, 1, 1), ...] + + """ + + def __init__(self, mesh_id=None, name=''): + super().__init__(mesh_id, name) + + self._x_grid = None + self._y_grid = None + self._z_grid = None + + @property + def n_dimension(self): + return 3 + + @property + def x_grid(self): + return self._x_grid + + @property + def y_grid(self): + return self._y_grid + + @property + def z_grid(self): + return self._z_grid + + @property + def indices(self): + nx = len(self.x_grid) - 1 + ny = len(self.y_grid) - 1 + nz = len(self.z_grid) - 1 + 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)) + + @x_grid.setter + def x_grid(self, grid): + cv.check_type('mesh x_grid', grid, Iterable, Real) + self._x_grid = grid + + @y_grid.setter + def y_grid(self, grid): + cv.check_type('mesh y_grid', grid, Iterable, Real) + self._y_grid = grid + + @z_grid.setter + def z_grid(self, grid): + cv.check_type('mesh z_grid', grid, Iterable, Real) + self._z_grid = grid + + @classmethod + def from_hdf5(cls, group): + mesh_id = int(group.name.split('/')[-1].lstrip('mesh ')) + + # Read and assign mesh properties + mesh = cls(mesh_id) + mesh.x_grid = group['x_grid'][()] + mesh.y_grid = group['y_grid'][()] + mesh.z_grid = group['z_grid'][()] + + return mesh + + def to_xml_element(self): + """Return XML representation of the mesh + + Returns + ------- + element : xml.etree.ElementTree.Element + XML element containing mesh data + + """ + + element = ET.Element("mesh") + element.set("id", str(self._id)) + element.set("type", "rectilinear") + + subelement = ET.SubElement(element, "x_grid") + subelement.text = ' '.join(map(str, self.x_grid)) + + subelement = ET.SubElement(element, "y_grid") + subelement.text = ' '.join(map(str, self.y_grid)) + + subelement = ET.SubElement(element, "z_grid") + subelement.text = ' '.join(map(str, self.z_grid)) + + return element diff --git a/openmc/mgxs/library.py b/openmc/mgxs/library.py index 7b75d90fff..fbf08f85f7 100644 --- a/openmc/mgxs/library.py +++ b/openmc/mgxs/library.py @@ -51,7 +51,7 @@ class Library(object): The types of cross sections in the library (e.g., ['total', 'scatter']) domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization - domains : Iterable of openmc.Material, openmc.Cell, openmc.Universe or openmc.Mesh + domains : Iterable of openmc.Material, openmc.Cell, openmc.Universe or openmc.RegularMesh The spatial domain(s) for which MGXS in the Library are computed correction : {'P0', None} Apply the P0 correction to scattering matrices if set to 'P0' @@ -324,7 +324,7 @@ class Library(object): cv.check_type('domain', domains, Iterable, openmc.Universe) all_domains = self.geometry.get_all_universes().values() elif self.domain_type == 'mesh': - cv.check_type('domain', domains, Iterable, openmc.Mesh) + cv.check_type('domain', domains, Iterable, openmc.RegularMesh) # The mesh and geometry are independent, so set all_domains # to the input domains @@ -606,7 +606,7 @@ class Library(object): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh or Integral + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh or Integral The material, cell, or universe object of interest (or its ID) mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix', 'delayed-nu-fission', 'delayed-nu-fission matrix', 'chi-delayed', 'beta'} The type of multi-group cross section object to return @@ -631,7 +631,7 @@ class Library(object): elif self.domain_type == 'universe': cv.check_type('domain', domain, (openmc.Universe, Integral)) elif self.domain_type == 'mesh': - cv.check_type('domain', domain, (openmc.Mesh, Integral)) + cv.check_type('domain', domain, (openmc.RegularMesh, Integral)) # Check that requested domain is included in library if isinstance(domain, Integral): @@ -916,7 +916,7 @@ class Library(object): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization xsdata_name : str Name to apply to the "xsdata" entry produced by this method @@ -930,7 +930,7 @@ class Library(object): subdomain : iterable of int This parameter is not used unless using a mesh domain. In that case, the subdomain is an [i,j,k] index (1-based indexing) of the - mesh cell of interest in the openmc.Mesh object. Note: + mesh cell of interest in the openmc.RegularMesh object. Note: this parameter currently only supports subdomains within a mesh, and not the subdomains of a distribcell. @@ -952,7 +952,7 @@ class Library(object): """ cv.check_type('domain', domain, (openmc.Material, openmc.Cell, - openmc.Universe, openmc.Mesh)) + openmc.Universe, openmc.RegularMesh)) cv.check_type('xsdata_name', xsdata_name, str) cv.check_type('nuclide', nuclide, str) cv.check_value('xs_type', xs_type, ['macro', 'micro']) diff --git a/openmc/mgxs/mdgxs.py b/openmc/mgxs/mdgxs.py index 2d278d62d7..2d1e75e81c 100644 --- a/openmc/mgxs/mdgxs.py +++ b/openmc/mgxs/mdgxs.py @@ -40,7 +40,7 @@ class MDGXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -68,7 +68,7 @@ class MDGXS(MGXS): Reaction type (e.g., 'chi-delayed', 'beta', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -238,7 +238,7 @@ class MDGXS(MGXS): mdgxs_type : {'delayed-nu-fission', 'chi-delayed', 'beta', 'decay-rate', 'delayed-nu-fission matrix'} The type of multi-delayed-group cross section object to return domain : openmc.Material or openmc.Cell or openmc.Universe or - openmc.Mesh + openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -906,6 +906,7 @@ class ChiDelayed(MDGXS): .. math:: + \begin{aligned} \langle \nu^d \sigma_{f,g' \rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_0^\infty dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E) \nu^d \sigma_f (r, E') \psi(r, E', \Omega')\\ @@ -914,10 +915,11 @@ class ChiDelayed(MDGXS): E') \psi(r, E', \Omega') \\ \chi_g^d &= \frac{\langle \nu^d \sigma_{f,g' \rightarrow g} \phi \rangle} {\langle \nu^d \sigma_f \phi \rangle} + \end{aligned} Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -945,7 +947,7 @@ class ChiDelayed(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1429,7 +1431,7 @@ class DelayedNuFissionXS(MDGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -1457,7 +1459,7 @@ class DelayedNuFissionXS(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1547,6 +1549,7 @@ class Beta(MDGXS): .. math:: + \begin{aligned} \langle \nu^d \sigma_f \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_0^\infty dE' \int_0^\infty dE \; \chi(E) \nu^d \sigma_f (r, E') \psi(r, E', \Omega') \\ @@ -1555,6 +1558,7 @@ class Beta(MDGXS): \sigma_f (r, E') \psi(r, E', \Omega') \\ \beta_{d,g} &= \frac{\langle \nu^d \sigma_f \phi \rangle} {\langle \nu \sigma_f \phi \rangle} + \end{aligned} NOTE: The Beta MGXS is the delayed neutron fraction computed directly from the nuclear data. Often the delayed neutron fraction is @@ -1563,7 +1567,7 @@ class Beta(MDGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -1591,7 +1595,7 @@ class Beta(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1735,6 +1739,7 @@ class DecayRate(MDGXS): .. math:: + \begin{aligned} \langle \lambda_d \nu^d \sigma_f \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_0^\infty dE' \int_0^\infty dE \; \lambda_d \nu^d \sigma_f (r, E') \psi(r, E', \Omega') \\ @@ -1743,10 +1748,11 @@ class DecayRate(MDGXS): \sigma_f (r, E') \psi(r, E', \Omega') \\ \lambda_d &= \frac{\langle \lambda_d \nu^d \sigma_f \phi \rangle} {\langle \nu^d \sigma_f \phi \rangle} + \end{aligned} Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -1774,7 +1780,7 @@ class DecayRate(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -1921,7 +1927,7 @@ class MatrixMDGXS(MDGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -1949,7 +1955,7 @@ class MatrixMDGXS(MDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2501,6 +2507,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): .. math:: + \begin{aligned} \langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E) \nu\sigma_f^d (r, E') \psi(r, E', \Omega')\\ @@ -2508,11 +2515,12 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ \nu\sigma_{f,g'\rightarrow g} &= \frac{\langle \nu\sigma_{f,g'\rightarrow g}^d \phi \rangle}{\langle \phi \rangle} + \end{aligned} Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2540,7 +2548,7 @@ class DelayedNuFissionMatrixXS(MatrixMDGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 01a03f6506..1c8f00f6e4 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -56,7 +56,7 @@ _DOMAIN_TO_FILTER = {'cell': openmc.CellFilter, _DOMAINS = (openmc.Cell, openmc.Universe, openmc.Material, - openmc.Mesh) + openmc.RegularMesh) # Supported ScatterMatrixXS angular distribution types MU_TREATMENTS = ('legendre', 'histogram') @@ -124,7 +124,7 @@ class MGXS(metaclass=ABCMeta): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -150,7 +150,7 @@ class MGXS(metaclass=ABCMeta): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -606,7 +606,7 @@ class MGXS(metaclass=ABCMeta): self._domain_type = 'cell' elif isinstance(domain, openmc.Universe): self._domain_type = 'universe' - elif isinstance(domain, openmc.Mesh): + elif isinstance(domain, openmc.RegularMesh): self._domain_type = 'mesh' @domain_type.setter @@ -675,7 +675,7 @@ class MGXS(metaclass=ABCMeta): ---------- mgxs_type : {'total', 'transport', 'nu-transport', 'absorption', 'capture', 'fission', 'nu-fission', 'kappa-fission', 'scatter', 'nu-scatter', 'scatter matrix', 'nu-scatter matrix', 'multiplicity matrix', 'nu-fission matrix', 'chi', 'chi-prompt', 'inverse-velocity', 'prompt-nu-fission', 'prompt-nu-fission matrix'} The type of multi-group cross section object to return - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -1985,7 +1985,7 @@ class MatrixMGXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2011,7 +2011,7 @@ class MatrixMGXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2478,7 +2478,7 @@ class TotalXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2504,7 +2504,7 @@ class TotalXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2591,6 +2591,7 @@ class TransportXS(MGXS): .. math:: + \begin{aligned} \langle \sigma_t \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \sigma_t (r, E) \psi (r, E, \Omega) \\ @@ -2603,13 +2604,14 @@ class TransportXS(MGXS): \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ \sigma_{tr} &= \frac{\langle \sigma_t \phi \rangle - \langle \sigma_{s1} \phi \rangle}{\langle \phi \rangle} + \end{aligned} To incorporate the effect of scattering multiplication in the above relation, the `nu` parameter can be set to `True`. Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2640,7 +2642,7 @@ class TransportXS(MGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2834,7 +2836,7 @@ class AbsorptionXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2860,7 +2862,7 @@ class AbsorptionXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -2961,7 +2963,7 @@ class CaptureXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -2987,7 +2989,7 @@ class CaptureXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3103,7 +3105,7 @@ class FissionXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -3140,7 +3142,7 @@ class FissionXS(MGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3282,7 +3284,7 @@ class KappaFissionXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -3308,7 +3310,7 @@ class KappaFissionXS(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3408,7 +3410,7 @@ class ScatterXS(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -3439,7 +3441,7 @@ class ScatterXS(MGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -3549,7 +3551,8 @@ class ScatterMatrixXS(MatrixMGXS): .. math:: - \langle \sigma_{s,\ell,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr + \begin{aligned} + \langle \sigma}_{s,\ell,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; P_\ell (\Omega \cdot \Omega') \sigma_s (r, E' \rightarrow E, \Omega' \cdot \Omega) \psi(r, E', \Omega')\\ @@ -3557,6 +3560,7 @@ class ScatterMatrixXS(MatrixMGXS): \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ \sigma_{s,\ell,g'\rightarrow g} &= \frac{\langle \sigma_{s,\ell,g'\rightarrow g} \phi \rangle}{\langle \phi \rangle} + \end{aligned} If the order is zero and a :math:`P_0` transport-correction is applied (default), the scattering matrix elements are: @@ -3602,7 +3606,7 @@ class ScatterMatrixXS(MatrixMGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -3656,7 +3660,7 @@ class ScatterMatrixXS(MatrixMGXS): If True, the cross section data will include neutron multiplication by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -4699,6 +4703,7 @@ class MultiplicityMatrixXS(MatrixMGXS): .. math:: + \begin{aligned} \langle \upsilon \sigma_{s,g'\rightarrow g} \phi \rangle &= \int_{r \in D} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \sum_i \upsilon_i \sigma_i (r, E' \rightarrow @@ -4710,12 +4715,13 @@ class MultiplicityMatrixXS(MatrixMGXS): \upsilon_{g'\rightarrow g} &= \frac{\langle \upsilon \sigma_{s,g'\rightarrow g} \rangle}{\langle \sigma_{s,g'\rightarrow g} \rangle} + \end{aligned} where :math:`\upsilon_i` is the multiplicity for the :math:`i`-th reaction. Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -4741,7 +4747,7 @@ class MultiplicityMatrixXS(MatrixMGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -4866,6 +4872,7 @@ class ScatterProbabilityMatrix(MatrixMGXS): .. math:: + \begin{aligned} \langle \sigma_{s,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{4\pi} d\Omega \int_{E_g}^{E_{g-1}} dE \; \sigma_{s} (r, E' \rightarrow E, \Omega' @@ -4877,10 +4884,11 @@ class ScatterProbabilityMatrix(MatrixMGXS): P_{s,g'\rightarrow g} &= \frac{\langle \sigma_{s,g'\rightarrow g} \phi \rangle}{\langle \sigma_{s,g'} \phi \rangle} + \end{aligned} Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -4906,7 +4914,7 @@ class ScatterProbabilityMatrix(MatrixMGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5032,6 +5040,7 @@ class NuFissionMatrixXS(MatrixMGXS): .. math:: + \begin{aligned} \langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_{E_{g'}}^{E_{g'-1}} dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E) \nu\sigma_f (r, E') \psi(r, E', \Omega')\\ @@ -5039,10 +5048,11 @@ class NuFissionMatrixXS(MatrixMGXS): \int_{E_g}^{E_{g-1}} dE \; \psi (r, E, \Omega) \\ \nu\sigma_{f,g'\rightarrow g} &= \frac{\langle \nu\sigma_{f,g'\rightarrow g} \phi \rangle}{\langle \phi \rangle} + \end{aligned} Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -5073,7 +5083,7 @@ class NuFissionMatrixXS(MatrixMGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5183,6 +5193,7 @@ class Chi(MGXS): .. math:: + \begin{aligned} \langle \nu\sigma_{f,g' \rightarrow g} \phi \rangle &= \int_{r \in V} dr \int_{4\pi} d\Omega' \int_0^\infty dE' \int_{E_g}^{E_{g-1}} dE \; \chi(E) \nu\sigma_f (r, E') \psi(r, E', \Omega')\\ @@ -5191,6 +5202,7 @@ class Chi(MGXS): E') \psi(r, E', \Omega') \\ \chi_g &= \frac{\langle \nu\sigma_{f,g' \rightarrow g} \phi \rangle} {\langle \nu\sigma_f \phi \rangle} + \end{aligned} This class can also be used to gather a prompt-chi (which only includes the outgoing energy spectrum of prompt neutrons). This is accomplished by @@ -5198,7 +5210,7 @@ class Chi(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -5229,7 +5241,7 @@ class Chi(MGXS): If true, computes cross sections which only includes prompt neutrons by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization @@ -5780,7 +5792,7 @@ class InverseVelocity(MGXS): Parameters ---------- - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh The domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} The domain type for spatial homogenization @@ -5806,7 +5818,7 @@ class InverseVelocity(MGXS): Reaction type (e.g., 'total', 'nu-fission', etc.) by_nuclide : bool If true, computes cross sections for each nuclide in domain - domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.Mesh + domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh Domain for spatial homogenization domain_type : {'material', 'cell', 'distribcell', 'universe', 'mesh'} Domain type for spatial homogenization diff --git a/openmc/model/model.py b/openmc/model/model.py index cf6603638e..f66bd8f16d 100644 --- a/openmc/model/model.py +++ b/openmc/model/model.py @@ -133,8 +133,9 @@ class Model(object): Array of timesteps in units of [s]. Note that values are not cumulative. chain_file : str, optional - Path to the depletion chain XML file. Defaults to the - :envvar:`OPENMC_DEPLETE_CHAIN` environment variable if it exists. + Path to the depletion chain XML file. Defaults to the chain + found under the ``depletion_chain`` in the + :envvar:`OPENMC_CROSS_SECTIONS` environment variable if it exists. method : str Integration method used for depletion (e.g., 'cecm', 'predictor') **kwargs diff --git a/openmc/plots.py b/openmc/plots.py index 00d96fd7ee..b4712c9026 100644 --- a/openmc/plots.py +++ b/openmc/plots.py @@ -208,14 +208,18 @@ class Plot(IDManagerMixin): The cells or materials to plot mask_background : Iterable of int or str Color to apply to all cells/materials not listed in mask_components + show_overlaps : bool + Inidicate whether or not overlapping regions are shown + overlap_color : Iterable of int or str + Color to apply to overlapping regions colors : dict Dictionary indicating that certain cells/materials (keys) should be displayed with a particular color. level : int Universe depth to plot at meshlines : dict - Dictionary defining type, id, linewidth and color of a regular mesh - to be plotted on top of a plot + Dictionary defining type, id, linewidth and color of a mesh to be + plotted on top of a plot """ @@ -236,6 +240,8 @@ class Plot(IDManagerMixin): self._background = None self._mask_components = None self._mask_background = None + self._show_overlaps = False + self._overlap_color = None self._colors = {} self._level = None self._meshlines = None @@ -284,6 +290,14 @@ class Plot(IDManagerMixin): def mask_background(self): return self._mask_background + @property + def show_overlaps(self): + return self._show_overlaps + + @property + def overlap_color(self): + return self._overlap_color + @property def colors(self): return self._colors @@ -343,15 +357,7 @@ class Plot(IDManagerMixin): @background.setter def background(self, background): - cv.check_type('plot background', background, Iterable) - if isinstance(background, str): - if background.lower() not in _SVG_COLORS: - raise ValueError("'{}' is not a valid color.".format(background)) - else: - cv.check_length('plot background', background, 3) - for rgb in background: - cv.check_greater_than('plot background', rgb, 0, True) - cv.check_less_than('plot background', rgb, 256) + self._check_color('plot background', background) self._background = background @colors.setter @@ -359,17 +365,7 @@ class Plot(IDManagerMixin): cv.check_type('plot colors', colors, Mapping) for key, value in colors.items(): cv.check_type('plot color key', key, (openmc.Cell, openmc.Material)) - cv.check_type('plot color value', value, Iterable) - if isinstance(value, str): - if value.lower() not in _SVG_COLORS: - raise ValueError("'{}' is not a valid color.".format(value)) - else: - cv.check_length('plot color (RGB)', value, 3) - for component in value: - cv.check_type('RGB component', component, Real) - cv.check_greater_than('RGB component', component, 0, True) - cv.check_less_than('RGB component', component, 255, True) - + self._check_color('plot color value', value) self._colors = colors @mask_components.setter @@ -380,17 +376,20 @@ class Plot(IDManagerMixin): @mask_background.setter def mask_background(self, mask_background): - cv.check_type('plot mask background', mask_background, Iterable) - if isinstance(mask_background, str): - if mask_background.lower() not in _SVG_COLORS: - raise ValueError("'{}' is not a valid color.".format(mask_background)) - else: - cv.check_length('plot mask_background', mask_background, 3) - for rgb in mask_background: - cv.check_greater_than('plot mask background', rgb, 0, True) - cv.check_less_than('plot mask background', rgb, 256) + self._check_color('plot mask background', mask_background) self._mask_background = mask_background + @show_overlaps.setter + def show_overlaps(self, show_overlaps): + cv.check_type('Show overlaps flag for Plot ID="{}"'.format(self.id), + show_overlaps, bool) + self._show_overlaps = show_overlaps + + @overlap_color.setter + def overlap_color(self, overlap_color): + self._check_color('plot overlap color', overlap_color) + self._overlap_color = overlap_color + @level.setter def level(self, plot_level): cv.check_type('plot level', plot_level, Integral) @@ -421,15 +420,23 @@ class Plot(IDManagerMixin): 0, equality=True) if 'color' in meshlines: - cv.check_type('plot meshlines color', meshlines['color'], Iterable, - Integral) - cv.check_length('plot meshlines color', meshlines['color'], 3) - for rgb in meshlines['color']: - cv.check_greater_than('plot meshlines color', rgb, 0, True) - cv.check_less_than('plot meshlines color', rgb, 256) + self._check_color('plot meshlines color', meshlines['color']) self._meshlines = meshlines + @staticmethod + def _check_color(err_string, color): + cv.check_type(err_string, color, Iterable) + if isinstance(color, str): + if color.lower() not in _SVG_COLORS: + raise ValueError("'{}' is not a valid color.".format(color)) + else: + cv.check_length(err_string, color, 3) + for rgb in color: + cv.check_type(err_string, rgb, Real) + cv.check_greater_than('RGB component', rgb, 0, True) + cv.check_less_than('RGB component', rgb, 256) + def __repr__(self): string = 'Plot\n' string += '{: <16}=\t{}\n'.format('\tID', self._id) @@ -446,6 +453,8 @@ class Plot(IDManagerMixin): self._mask_components) string += '{: <16}=\t{}\n'.format('\tMask background', self._mask_background) + string += '{: <16}=\t{}\n'.format('\Overlap Color', + self._overlap_color) string += '{: <16}=\t{}\n'.format('\tColors', self._colors) string += '{: <16}=\t{}\n'.format('\tLevel', self._level) string += '{: <16}=\t{}\n'.format('\tMeshlines', self._meshlines) @@ -635,6 +644,18 @@ class Plot(IDManagerMixin): subelement.set("background", ' '.join( str(x) for x in color)) + if self._show_overlaps: + subelement = ET.SubElement(element, "show_overlaps") + subelement.text = "true" + + if self._overlap_color is not None: + color = self._overlap_color + if isinstance(color, str): + color = _SVG_COLORS[color.lower()] + subelement = ET.SubElement(element, "overlap_color") + subelement.text = ' '.join(str(x) for x in color) + + if self._level is not None: subelement = ET.SubElement(element, "level") subelement.text = str(self._level) diff --git a/openmc/settings.py b/openmc/settings.py index f348bc6cb0..784c2ac6e6 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -7,9 +7,9 @@ import sys import numpy as np -from openmc._xml import clean_indentation +from openmc._xml import clean_indentation, get_text import openmc.checkvalue as cv -from openmc import VolumeCalculation, Source, Mesh +from openmc import VolumeCalculation, Source, RegularMesh _RUN_MODES = ['eigenvalue', 'fixed source', 'plot', 'volume', 'particle restart'] _RES_SCAT_METHODS = ['dbrc', 'rvs'] @@ -45,7 +45,7 @@ class Settings(object): secondary bremsstrahlung photons ('ttb'). energy_mode : {'continuous-energy', 'multi-group'} Set whether the calculation should be continuous-energy or multi-group. - entropy_mesh : openmc.Mesh + entropy_mesh : openmc.RegularMesh Mesh to be used to calculate Shannon entropy. If the mesh dimensions are not specified. OpenMC assigns a mesh such that 20 source sites per mesh cell are to be expected on average. @@ -145,7 +145,7 @@ class Settings(object): Maximum number of batches simulated. If this is set, the number of batches specified via ``batches`` is interpreted as the minimum number of batches - ufs_mesh : openmc.Mesh + ufs_mesh : openmc.RegularMesh Mesh to be used for redistributing source sites via the uniform fision site (UFS) method. verbosity : int @@ -174,7 +174,6 @@ class Settings(object): self._source = cv.CheckedList(Source, 'source distributions') self._confidence_intervals = None - self._cross_sections = None self._electron_treatment = None self._photon_transport = None self._ptables = None @@ -551,8 +550,9 @@ class Settings(object): @entropy_mesh.setter def entropy_mesh(self, entropy): - cv.check_type('entropy mesh', entropy, Mesh) - cv.check_length('entropy mesh dimension', entropy.dimension, 3) + cv.check_type('entropy mesh', entropy, RegularMesh) + if entropy.dimension: + cv.check_length('entropy mesh dimension', entropy.dimension, 3) cv.check_length('entropy mesh lower-left corner', entropy.lower_left, 3) cv.check_length('entropy mesh upper-right corner', entropy.upper_right, 3) self._entropy_mesh = entropy @@ -640,7 +640,7 @@ class Settings(object): @ufs_mesh.setter def ufs_mesh(self, ufs_mesh): - cv.check_type('UFS mesh', ufs_mesh, Mesh) + cv.check_type('UFS mesh', ufs_mesh, RegularMesh) cv.check_length('UFS mesh dimension', ufs_mesh.dimension, 3) cv.check_length('UFS mesh lower-left corner', ufs_mesh.lower_left, 3) cv.check_length('UFS mesh upper-right corner', ufs_mesh.upper_right, 3) @@ -693,29 +693,29 @@ class Settings(object): elem = ET.SubElement(root, "run_mode") elem.text = self._run_mode - def _create_batches_subelement(self, run_mode_element): + def _create_batches_subelement(self, root): if self._batches is not None: - element = ET.SubElement(run_mode_element, "batches") + element = ET.SubElement(root, "batches") element.text = str(self._batches) - def _create_generations_per_batch_subelement(self, run_mode_element): + def _create_generations_per_batch_subelement(self, root): if self._generations_per_batch is not None: - element = ET.SubElement(run_mode_element, "generations_per_batch") + element = ET.SubElement(root, "generations_per_batch") element.text = str(self._generations_per_batch) - def _create_inactive_subelement(self, run_mode_element): + def _create_inactive_subelement(self, root): if self._inactive is not None: - element = ET.SubElement(run_mode_element, "inactive") + element = ET.SubElement(root, "inactive") element.text = str(self._inactive) - def _create_particles_subelement(self, run_mode_element): + def _create_particles_subelement(self, root): if self._particles is not None: - element = ET.SubElement(run_mode_element, "particles") + element = ET.SubElement(root, "particles") element.text = str(self._particles) - def _create_keff_trigger_subelement(self, run_mode_element): + def _create_keff_trigger_subelement(self, root): if self._keff_trigger is not None: - element = ET.SubElement(run_mode_element, "keff_trigger") + element = ET.SubElement(root, "keff_trigger") for key in self._keff_trigger: subelement = ET.SubElement(element, key) @@ -927,6 +927,237 @@ class Settings(object): elem = ET.SubElement(root, "dagmc") elem.text = str(self._dagmc).lower() + def _eigenvalue_from_xml_element(self, root): + elem = root.find('eigenvalue') + if elem is not None: + self._run_mode_from_xml_element(elem) + self._particles_from_xml_element(elem) + self._batches_from_xml_element(elem) + self._inactive_from_xml_element(elem) + self._generations_per_batch_from_xml_element(elem) + + def _run_mode_from_xml_element(self, root): + text = get_text(root, 'run_mode') + if text is not None: + self.run_mode = text + + def _particles_from_xml_element(self, root): + text = get_text(root, 'particles') + if text is not None: + self.particles = int(text) + + def _batches_from_xml_element(self, root): + text = get_text(root, 'batches') + if text is not None: + self.batches = int(text) + + def _inactive_from_xml_element(self, root): + text = get_text(root, 'inactive') + if text is not None: + self.inactive = int(text) + + def _generations_per_batch_from_xml_element(self, root): + text = get_text(root, 'generations_per_batch') + if text is not None: + self.generations_per_batch = int(text) + + def _keff_trigger_from_xml_element(self, root): + elem = root.find('keff_trigger') + if elem is not None: + trigger = get_text(elem, 'type') + threshold = float(get_text(elem, 'threshold')) + self.keff_trigger = {'type': trigger, 'threshold': threshold} + + def _source_from_xml_element(self, root): + for elem in root.findall('source'): + self.source.append(Source.from_xml_element(elem)) + + def _output_from_xml_element(self, root): + elem = root.find('output') + if elem is not None: + self.output = {} + for key in ('summary', 'tallies', 'path'): + value = get_text(elem, key) + if value is not None: + if key in ('summary', 'tallies'): + value = value in ('true', '1') + self.output[key] = value + + def _statepoint_from_xml_element(self, root): + elem = root.find('state_point') + if elem is not None: + text = get_text(elem, 'batches') + if text is not None: + self.statepoint['batches'] = [int(x) for x in text.split()] + + def _sourcepoint_from_xml_element(self, root): + elem = root.find('source_point') + if elem is not None: + for key in ('separate', 'write', 'overwrite_latest', 'batches'): + value = get_text(elem, key) + if value is not None: + if key in ('separate', 'write'): + value = value in ('true', '1') + elif key == 'overwrite_latest': + value = value in ('true', '1') + key = 'overwrite' + else: + value = [int(x) for x in value.split()] + self.sourcepoint[key] = value + + def _confidence_intervals_from_xml_element(self, root): + text = get_text(root, 'confidence_intervals') + if text is not None: + self.confidence_intervals = text in ('true', '1') + + def _electron_treatment_from_xml_element(self, root): + text = get_text(root, 'electron_treatment') + if text is not None: + self.electron_treatment = text + + def _energy_mode_from_xml_element(self, root): + text = get_text(root, 'energy_mode') + if text is not None: + self.energy_mode = text + + def _max_order_from_xml_element(self, root): + text = get_text(root, 'max_order') + if text is not None: + self.max_order = int(text) + + def _photon_transport_from_xml_element(self, root): + text = get_text(root, 'photon_transport') + if text is not None: + self.photon_transport = text in ('true', '1') + + def _ptables_from_xml_element(self, root): + text = get_text(root, 'ptables') + if text is not None: + self.ptables = text in ('true', '1') + + def _seed_from_xml_element(self, root): + text = get_text(root, 'seed') + if text is not None: + self.seed = int(text) + + def _survival_biasing_from_xml_element(self, root): + text = get_text(root, 'survival_biasing') + if text is not None: + self.survival_biasing = text in ('true', '1') + + def _cutoff_from_xml_element(self, root): + elem = root.find('cutoff') + if elem is not None: + self.cutoff = {} + for key in ('energy_neutron', 'energy_photon', 'energy_electron', + 'energy_positron', 'weight', 'weight_avg'): + value = get_text(elem, key) + if value is not None: + self.cutoff[key] = float(value) + + def _entropy_mesh_from_xml_element(self, root): + text = get_text(root, 'entropy_mesh') + if text is not None: + path = "./mesh[@id='{}']".format(int(text)) + elem = root.find(path) + if elem is not None: + self.entropy_mesh = RegularMesh.from_xml_element(elem) + + def _trigger_from_xml_element(self, root): + elem = root.find('trigger') + if elem is not None: + self.trigger_active = get_text(elem, 'active') in ('true', '1') + text = get_text(elem, 'max_batches') + if text is not None: + self.trigger_max_batches = int(text) + text = get_text(elem, 'batch_interval') + if text is not None: + self.trigger_batch_interval = int(text) + + def _no_reduce_from_xml_element(self, root): + text = get_text(root, 'no_reduce') + if text is not None: + self.no_reduce = text in ('true', '1') + + def _verbosity_from_xml_element(self, root): + text = get_text(root, 'verbosity') + if text is not None: + self.verbosity = int(text) + + def _tabular_legendre_from_xml_element(self, root): + elem = root.find('tabular_legendre') + if elem is not None: + text = get_text(elem, 'enable') + self.tabular_legendre['enable'] = text in ('true', '1') + text = get_text(elem, 'num_points') + if text is not None: + self.tabular_legendre['num_points'] = int(text) + + def _temperature_from_xml_element(self, root): + text = get_text(root, 'temperature_default') + if text is not None: + self.temperature['default'] = float(text) + text = get_text(root, 'temperature_tolerance') + if text is not None: + self.temperature['tolerance'] = float(text) + text = get_text(root, 'temperature_method') + if text is not None: + self.temperature['method'] = text + text = get_text(root, 'temperature_range') + if text is not None: + self.temperature['range'] = [float(x) for x in text.split()] + text = get_text(root, 'temperature_multipole') + if text is not None: + self.temperature['multipole'] = text in ('true', '1') + + def _trace_from_xml_element(self, root): + text = get_text(root, 'trace') + if text is not None: + self.trace = [int(x) for x in text.split()] + + def _track_from_xml_element(self, root): + text = get_text(root, 'track') + if text is not None: + self.track = [int(x) for x in text.split()] + + def _ufs_mesh_from_xml_element(self, root): + text = get_text(root, 'ufs_mesh') + if text is not None: + path = "./mesh[@id='{}']".format(int(text)) + elem = root.find(path) + if elem is not None: + self.ufs_mesh = RegularMesh.from_xml_element(elem) + + def _resonance_scattering_from_xml_element(self, root): + elem = root.find('resonance_scattering') + if elem is not None: + keys = ('enable', 'method', 'energy_min', 'energy_max', 'nuclides') + for key in keys: + value = get_text(elem, key) + if value is not None: + if key == 'enable': + value = value in ('true', '1') + elif key in ('energy_min', 'energy_max'): + value = float(value) + elif key == 'nuclides': + value = value.split() + self.resonance_scattering[key] = value + + def _create_fission_neutrons_from_xml_element(self, root): + text = get_text(root, 'create_fission_neutrons') + if text is not None: + self.create_fission_neutrons = text in ('true', '1') + + def _log_grid_bins_from_xml_element(self, root): + text = get_text(root, 'log_grid_bins') + if text is not None: + self.log_grid_bins = int(text) + + def _dagmc_from_xml_element(self, root): + text = get_text(root, 'dagmc') + if text is not None: + self.dagmc = text in ('true', '1') + def export_to_xml(self, path='settings.xml'): """Export simulation settings to an XML file. @@ -985,3 +1216,60 @@ class Settings(object): # Write the XML Tree to the settings.xml file tree = ET.ElementTree(root_element) tree.write(str(p), xml_declaration=True, encoding='utf-8') + + @classmethod + def from_xml(cls, path='settings.xml'): + """Generate settings from XML file + + Parameters + ---------- + path : str, optional + Path to settings XML file + + Returns + ------- + openmc.Settings + Settings object + + """ + tree = ET.parse(path) + root = tree.getroot() + + settings = cls() + settings._eigenvalue_from_xml_element(root) + settings._run_mode_from_xml_element(root) + settings._particles_from_xml_element(root) + settings._batches_from_xml_element(root) + settings._inactive_from_xml_element(root) + settings._generations_per_batch_from_xml_element(root) + settings._keff_trigger_from_xml_element(root) + settings._source_from_xml_element(root) + settings._output_from_xml_element(root) + settings._statepoint_from_xml_element(root) + settings._sourcepoint_from_xml_element(root) + settings._confidence_intervals_from_xml_element(root) + settings._electron_treatment_from_xml_element(root) + settings._energy_mode_from_xml_element(root) + settings._max_order_from_xml_element(root) + settings._photon_transport_from_xml_element(root) + settings._ptables_from_xml_element(root) + settings._seed_from_xml_element(root) + settings._survival_biasing_from_xml_element(root) + settings._cutoff_from_xml_element(root) + settings._entropy_mesh_from_xml_element(root) + settings._trigger_from_xml_element(root) + settings._no_reduce_from_xml_element(root) + settings._verbosity_from_xml_element(root) + settings._tabular_legendre_from_xml_element(root) + settings._temperature_from_xml_element(root) + settings._trace_from_xml_element(root) + settings._track_from_xml_element(root) + settings._ufs_mesh_from_xml_element(root) + settings._resonance_scattering_from_xml_element(root) + settings._create_fission_neutrons_from_xml_element(root) + settings._log_grid_bins_from_xml_element(root) + settings._dagmc_from_xml_element(root) + + # TODO: Get volume calculations + + return settings diff --git a/openmc/source.py b/openmc/source.py index 6ec882ca6a..88c2f86119 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -2,6 +2,7 @@ from numbers import Real import sys from xml.etree import ElementTree as ET +from openmc._xml import get_text from openmc.stats.univariate import Univariate from openmc.stats.multivariate import UnitSphere, Spatial import openmc.checkvalue as cv @@ -137,3 +138,46 @@ class Source(object): if self.energy is not None: element.append(self.energy.to_xml_element('energy')) return element + + @classmethod + def from_xml_element(cls, elem): + """Generate source from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.Source + Source generated from XML element + + """ + source = cls() + + strength = get_text(elem, 'strength') + if strength is not None: + source.strength = float(strength) + + particle = get_text(elem, 'particle') + if particle is not None: + source.particle = particle + + filename = get_text(elem, 'file') + if filename is not None: + source.file = filename + + space = elem.find('space') + if space is not None: + source.space = Spatial.from_xml_element(space) + + angle = elem.find('angle') + if angle is not None: + source.angle = UnitSphere.from_xml_element(angle) + + energy = elem.find('energy') + if energy is not None: + source.energy = Univariate.from_xml_element(energy) + + return source diff --git a/openmc/statepoint.py b/openmc/statepoint.py index 3dc6535841..c9a8f9f9a8 100644 --- a/openmc/statepoint.py +++ b/openmc/statepoint.py @@ -72,7 +72,7 @@ class StatePoint(object): k_generation : numpy.ndarray Estimate of k-effective for each batch/generation meshes : dict - Dictionary whose keys are mesh IDs and whose values are Mesh objects + Dictionary whose keys are mesh IDs and whose values are MeshBase objects n_batches : int Number of batches n_inactive : int @@ -292,9 +292,9 @@ class StatePoint(object): if not self._meshes_read: mesh_group = self._f['tallies/meshes'] - # Iterate over all Meshes + # Iterate over all meshes for group in mesh_group.values(): - mesh = openmc.Mesh.from_hdf5(group) + mesh = openmc.MeshBase.from_hdf5(group) self._meshes[mesh.id] = mesh self._meshes_read = True diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index ac788c3443..35afc21dd1 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -8,6 +8,7 @@ from xml.etree import ElementTree as ET import numpy as np import openmc.checkvalue as cv +from openmc._xml import get_text from openmc.stats.univariate import Univariate, Uniform @@ -47,6 +48,17 @@ class UnitSphere(metaclass=ABCMeta): def to_xml_element(self): return '' + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + distribution = get_text(elem, 'type') + if distribution == 'mu-phi': + return PolarAzimuthal.from_xml_element(elem) + elif distribution == 'isotropic': + return Isotropic.from_xml_element(elem) + elif distribution == 'monodirectional': + return Monodirectional.from_xml_element(elem) + class PolarAzimuthal(UnitSphere): """Angular distribution represented by polar and azimuthal angles @@ -121,6 +133,29 @@ class PolarAzimuthal(UnitSphere): element.append(self.phi.to_xml_element('phi')) return element + @classmethod + def from_xml_element(cls, elem): + """Generate angular distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.PolarAzimuthal + Angular distribution generated from XML element + + """ + mu_phi = cls() + params = get_text(elem, 'parameters') + if params is not None: + mu_phi.reference_uvw = [float(x) for x in params.split()] + mu_phi.mu = Univariate.from_xml_element(elem.find('mu')) + mu_phi.phi = Univariate.from_xml_element(elem.find('phi')) + return mu_phi + class Isotropic(UnitSphere): """Isotropic angular distribution. @@ -143,6 +178,23 @@ class Isotropic(UnitSphere): element.set("type", "isotropic") return element + @classmethod + def from_xml_element(cls, elem): + """Generate isotropic distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Isotropic + Isotropic distribution generated from XML element + + """ + return cls() + class Monodirectional(UnitSphere): """Monodirectional angular distribution. @@ -178,6 +230,27 @@ class Monodirectional(UnitSphere): element.set("reference_uvw", ' '.join(map(str, self.reference_uvw))) return element + @classmethod + def from_xml_element(cls, elem): + """Generate monodirectional distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Monodirectional + Monodirectional distribution generated from XML element + + """ + monodirectional = cls() + params = get_text(elem, 'parameters') + if params is not None: + monodirectional.reference_uvw = [float(x) for x in params.split()] + return monodirectional + class Spatial(metaclass=ABCMeta): """Distribution of locations in three-dimensional Euclidean space. @@ -193,6 +266,17 @@ class Spatial(metaclass=ABCMeta): def to_xml_element(self): return '' + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + distribution = get_text(elem, 'type') + if distribution == 'cartesian': + return CartesianIndependent.from_xml_element(elem) + elif distribution == 'box' or distribution == 'fission': + return Box.from_xml_element(elem) + elif distribution == 'point': + return Point.from_xml_element(elem) + class CartesianIndependent(Spatial): """Spatial distribution with independent x, y, and z distributions. @@ -270,6 +354,26 @@ class CartesianIndependent(Spatial): element.append(self.z.to_xml_element('z')) return element + @classmethod + def from_xml_element(cls, elem): + """Generate spatial distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.CartesianIndependent + Spatial distribution generated from XML element + + """ + x = Univariate.from_xml_element(elem.find('x')) + y = Univariate.from_xml_element(elem.find('y')) + z = Univariate.from_xml_element(elem.find('z')) + return cls(x, y, z) + class Box(Spatial): """Uniform distribution of coordinates in a rectangular cuboid. @@ -351,6 +455,27 @@ class Box(Spatial): ' '.join(map(str, self.upper_right)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate box distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Box + Box distribution generated from XML element + + """ + only_fissionable = get_text(elem, 'type') == 'fission' + params = [float(x) for x in get_text(elem, 'parameters').split()] + lower_left = params[:len(params)//2] + upper_right = params[len(params)//2:] + return cls(lower_left, upper_right, only_fissionable) + class Point(Spatial): """Delta function in three dimensions. @@ -398,3 +523,21 @@ class Point(Spatial): params = ET.SubElement(element, "parameters") params.text = ' '.join(map(str, self.xyz)) return element + + @classmethod + def from_xml_element(cls, elem): + """Generate point distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Point + Point distribution generated from XML element + + """ + xyz = [float(x) for x in get_text(elem, 'parameters').split()] + return cls(xyz) diff --git a/openmc/stats/univariate.py b/openmc/stats/univariate.py index e61f216ef0..363dd2ee35 100644 --- a/openmc/stats/univariate.py +++ b/openmc/stats/univariate.py @@ -7,6 +7,7 @@ from xml.etree import ElementTree as ET import numpy as np import openmc.checkvalue as cv +from openmc._xml import get_text from openmc.mixin import EqualityMixin @@ -32,6 +33,29 @@ class Univariate(EqualityMixin, metaclass=ABCMeta): def __len__(self): return 0 + @classmethod + @abstractmethod + def from_xml_element(cls, elem): + distribution = get_text(elem, 'type') + if distribution == 'discrete': + return Discrete.from_xml_element(elem) + elif distribution == 'uniform': + return Uniform.from_xml_element(elem) + elif distribution == 'maxwell': + return Maxwell.from_xml_element(elem) + elif distribution == 'watt': + return Watt.from_xml_element(elem) + elif distribution == 'normal': + return Normal.from_xml_element(elem) + elif distribution == 'muir': + return Muir.from_xml_element(elem) + elif distribution == 'tabular': + return Tabular.from_xml_element(elem) + elif distribution == 'legendre': + return Legendre.from_xml_element(elem) + elif distribution == 'mixture': + return Mixture.from_xml_element(elem) + class Discrete(Univariate): """Distribution characterized by a probability mass function. @@ -110,6 +134,26 @@ class Discrete(Univariate): return element + @classmethod + def from_xml_element(cls, elem): + """Generate discrete distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Discrete + Discrete distribution generated from XML element + + """ + params = [float(x) for x in get_text(elem, 'parameters').split()] + x = params[:len(params)//2] + p = params[len(params)//2:] + return cls(x, p) + class Uniform(Univariate): """Distribution with constant probability over a finite interval [a,b] @@ -181,6 +225,24 @@ class Uniform(Univariate): element.set("parameters", '{} {}'.format(self.a, self.b)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate uniform distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Uniform + Uniform distribution generated from XML element + + """ + params = get_text(elem, 'parameters').split() + return cls(*map(float, params)) + class Maxwell(Univariate): """Maxwellian distribution in energy. @@ -237,6 +299,24 @@ class Maxwell(Univariate): element.set("parameters", str(self.theta)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Maxwellian distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Maxwell + Maxwellian distribution generated from XML element + + """ + theta = float(get_text(elem, 'parameters')) + return cls(theta) + class Watt(Univariate): r"""Watt fission energy spectrum. @@ -308,6 +388,25 @@ class Watt(Univariate): element.set("parameters", '{} {}'.format(self.a, self.b)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Watt distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Watt + Watt distribution generated from XML element + + """ + params = get_text(elem, 'parameters').split() + return cls(*map(float, params)) + + class Normal(Univariate): r"""Normally distributed sampling. @@ -377,6 +476,25 @@ class Normal(Univariate): element.set("parameters", '{} {}'.format(self.mean_value, self.std_dev)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Normal distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Normal + Normal distribution generated from XML element + + """ + params = get_text(elem, 'parameters').split() + return cls(*map(float, params)) + + class Muir(Univariate): """Muir energy spectrum. @@ -465,6 +583,24 @@ class Muir(Univariate): element.set("parameters", '{} {} {}'.format(self._e0, self._m_rat, self._kt)) return element + @classmethod + def from_xml_element(cls, elem): + """Generate Muir distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Muir + Muir distribution generated from XML element + + """ + params = get_text(elem, 'parameters').split() + return cls(*map(float, params)) + class Tabular(Univariate): """Piecewise continuous probability distribution. @@ -561,6 +697,27 @@ class Tabular(Univariate): return element + @classmethod + def from_xml_element(cls, elem): + """Generate tabular distribution from an XML element + + Parameters + ---------- + elem : xml.etree.ElementTree.Element + XML element + + Returns + ------- + openmc.stats.Tabular + Tabular distribution generated from XML element + + """ + interpolation = get_text(elem, 'interpolation') + params = [float(x) for x in get_text(elem, 'parameters').split()] + x = params[:len(params)//2] + p = params[len(params)//2:] + return cls(x, p, interpolation) + class Legendre(Univariate): r"""Probability density given by a Legendre polynomial expansion @@ -607,6 +764,10 @@ class Legendre(Univariate): def to_xml_element(self, element_name): raise NotImplementedError + @classmethod + def from_xml_element(cls, elem): + raise NotImplementedError + class Mixture(Univariate): """Probability distribution characterized by a mixture of random variables. @@ -660,3 +821,7 @@ class Mixture(Univariate): def to_xml_element(self, element_name): raise NotImplementedError + + @classmethod + def from_xml_element(cls, elem): + raise NotImplementedError diff --git a/openmc/trigger.py b/openmc/trigger.py index 71f9c0b92b..98557aab58 100644 --- a/openmc/trigger.py +++ b/openmc/trigger.py @@ -31,7 +31,6 @@ class Trigger(object): """ def __init__(self, trigger_type, threshold): - # Initialize Mesh class attributes self.trigger_type = trigger_type self.threshold = threshold self._scores = [] diff --git a/setup.py b/setup.py index 38ee57f716..712244efb4 100755 --- a/setup.py +++ b/setup.py @@ -39,7 +39,13 @@ kwargs = { 'author': 'The OpenMC Development Team', 'author_email': 'openmc-dev@googlegroups.com', 'description': 'OpenMC', - 'url': 'https://github.com/openmc-dev/openmc', + 'url': 'https://openmc.org', + 'download_url': 'https://github.com/openmc-dev/openmc/releases', + 'project_urls': { + 'Issue Tracker': 'https://github.com/openmc-dev/openmc/issues', + 'Documentation': 'https://openmc.readthedocs.io', + 'Source Code': 'https://github.com/openmc-dev/openmc', + }, 'classifiers': [ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', @@ -48,6 +54,7 @@ kwargs = { 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Topic :: Scientific/Engineering' + 'Programming Language :: C++', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', @@ -55,13 +62,12 @@ kwargs = { 'Programming Language :: Python :: 3.7', ], - # Required dependencies + # Dependencies + 'python_requires': '>=3.4', 'install_requires': [ 'numpy>=1.9', 'h5py', 'scipy', 'ipython', 'matplotlib', 'pandas', 'lxml', 'uncertainties' ], - - # Optional dependencies 'extras_require': { 'test': ['pytest', 'pytest-cov'], 'vtk': ['vtk'], diff --git a/src/cell.cpp b/src/cell.cpp index 650a18a8a0..bf67036315 100644 --- a/src/cell.cpp +++ b/src/cell.cpp @@ -1,12 +1,15 @@ + #include "openmc/cell.h" #include #include #include #include +#include #include "openmc/capi.h" #include "openmc/constants.h" +#include "openmc/dagmc.h" #include "openmc/error.h" #include "openmc/geometry.h" #include "openmc/hdf5_interface.h" @@ -560,7 +563,7 @@ CSGCell::contains_complex(Position r, Direction u, int32_t on_surface) const { // Make a stack of booleans. We don't know how big it needs to be, but we do // know that rpn.size() is an upper-bound. - bool stack[rpn_.size()]; + std::vector stack(rpn_.size()); int i_stack = -1; for (int32_t token : rpn_) { @@ -613,19 +616,28 @@ DAGCell::DAGCell() : Cell{} {}; std::pair DAGCell::distance(Position r, Direction u, int32_t on_surface) const { + // if we've changed direction or we're not on a surface, + // reset the history and update last direction + if (u != simulation::last_dir || on_surface == 0) { + simulation::history.reset(); + simulation::last_dir = u; + } + moab::ErrorCode rval; moab::EntityHandle vol = dagmc_ptr_->entity_by_index(3, dag_index_); moab::EntityHandle hit_surf; double dist; double pnt[3] = {r.x, r.y, r.z}; double dir[3] = {u.x, u.y, u.z}; - rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist); + rval = dagmc_ptr_->ray_fire(vol, pnt, dir, hit_surf, dist, &simulation::history); MB_CHK_ERR_CONT(rval); int surf_idx; if (hit_surf != 0) { surf_idx = dagmc_ptr_->index_by_handle(hit_surf); - } else { // indicate that particle is lost + } else { + // indicate that particle is lost surf_idx = -1; + dist = INFINITY; } return {dist, surf_idx}; diff --git a/src/cross_sections.cpp b/src/cross_sections.cpp index 5d8d18c219..8ea59c1f08 100644 --- a/src/cross_sections.cpp +++ b/src/cross_sections.cpp @@ -402,6 +402,10 @@ void read_ce_cross_sections_xml() // If no directory is listed in cross_sections.xml, by default select the // directory in which the cross_sections.xml file resides auto pos = filename.rfind("/"); + if (pos == std::string::npos) { + // no '/' found, probably a Windows directory + pos = filename.rfind("\\"); + } directory = filename.substr(0, pos); } diff --git a/src/dagmc.cpp b/src/dagmc.cpp index 8add1f4f37..78d8bc4fb6 100644 --- a/src/dagmc.cpp +++ b/src/dagmc.cpp @@ -9,6 +9,7 @@ #include "openmc/material.h" #include "openmc/string_utils.h" #include "openmc/settings.h" +#include "openmc/surface.h" #ifdef DAGMC @@ -38,6 +39,14 @@ const std::string DAGMC_FILENAME = "dagmc.h5m"; namespace openmc { + +namespace simulation { + +moab::DagMC::RayHistory history; +Direction last_dir; + +} + namespace model { moab::DagMC* DAG; diff --git a/src/eigenvalue.cpp b/src/eigenvalue.cpp index 08a4d3f91c..a0b7f82c5f 100644 --- a/src/eigenvalue.cpp +++ b/src/eigenvalue.cpp @@ -29,6 +29,7 @@ #include // for sqrt, abs, pow #include // for back_inserter #include +#include //for infinity namespace openmc { @@ -388,10 +389,15 @@ int openmc_get_keff(double* k_combined) k_combined[0] = 0.0; k_combined[1] = 0.0; - // Make sure we have at least four realizations. Notice that at the end, + // Special case for n <=3. Notice that at the end, // there is a N-3 term in a denominator. if (simulation::n_realizations <= 3) { - return -1; + k_combined[0] = simulation::keff; + k_combined[1] = simulation::keff_std; + if (simulation::n_realizations <=1) { + k_combined[1] = std::numeric_limits::infinity(); + } + return 0; } // Initialize variables @@ -532,13 +538,10 @@ int openmc_get_keff(double* k_combined) void shannon_entropy() { - // Get pointer to entropy mesh - auto& m = model::meshes[settings::index_entropy_mesh]; - // Get source weight in each mesh bin bool sites_outside; - xt::xtensor p = m->count_sites(simulation::fission_bank, - &sites_outside); + xt::xtensor p = simulation::entropy_mesh->count_sites( + simulation::fission_bank, &sites_outside); // display warning message if there were sites outside entropy box if (sites_outside) { @@ -564,21 +567,19 @@ void shannon_entropy() void ufs_count_sites() { - auto &m = model::meshes[settings::index_ufs_mesh]; - if (simulation::current_batch == 1 && simulation::current_gen == 1) { // On the first generation, just assume that the source is already evenly // distributed so that effectively the production of fission sites is not // biased auto s = xt::view(simulation::source_frac, xt::all()); - s = m->volume_frac_; + s = simulation::ufs_mesh->volume_frac_; } else { // count number of source sites in each ufs mesh cell bool sites_outside; - simulation::source_frac = m->count_sites(simulation::source_bank, - &sites_outside); + simulation::source_frac = simulation::ufs_mesh->count_sites( + simulation::source_bank, &sites_outside); // Check for sites outside of the mesh if (mpi::master && sites_outside) { @@ -587,7 +588,7 @@ void ufs_count_sites() #ifdef OPENMC_MPI // Send source fraction to all processors - int n_bins = xt::prod(m->shape_)(); + int n_bins = xt::prod(simulation::ufs_mesh->shape_)(); MPI_Bcast(simulation::source_frac.data(), n_bins, MPI_DOUBLE, 0, mpi::intracomm); #endif @@ -605,17 +606,16 @@ void ufs_count_sites() double ufs_get_weight(const Particle* p) { - auto& m = model::meshes[settings::index_ufs_mesh]; - // Determine indices on ufs mesh for current location - int mesh_bin = m->get_bin(p->r()); + int mesh_bin = simulation::ufs_mesh->get_bin(p->r()); if (mesh_bin < 0) { p->write_restart(); fatal_error("Source site outside UFS mesh!"); } if (simulation::source_frac(mesh_bin) != 0.0) { - return m->volume_frac_ / simulation::source_frac(mesh_bin); + return simulation::ufs_mesh->volume_frac_ + / simulation::source_frac(mesh_bin); } else { return 1.0; } diff --git a/src/finalize.cpp b/src/finalize.cpp index 34c4b9781b..633cf2991d 100644 --- a/src/finalize.cpp +++ b/src/finalize.cpp @@ -71,8 +71,6 @@ int openmc_finalize() settings::energy_cutoff = {0.0, 1000.0, 0.0, 0.0}; settings::entropy_on = false; settings::gen_per_batch = 1; - settings::index_entropy_mesh = -1; - settings::index_ufs_mesh = -1; settings::legendre_to_tabular = true; settings::legendre_to_tabular_points = -1; settings::n_particles = -1; @@ -114,10 +112,13 @@ int openmc_finalize() simulation::satisfy_triggers = false; simulation::total_gen = 0; + simulation::entropy_mesh = nullptr; + simulation::ufs_mesh = nullptr; + data::energy_max = {INFTY, INFTY}; data::energy_min = {0.0, 0.0}; model::root_universe = -1; - openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_seed(DEFAULT_SEED); // Deallocate arrays free_memory(); @@ -159,6 +160,6 @@ int openmc_hard_reset() simulation::total_gen = 0; // Reset the random number generator state - openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_seed(DEFAULT_SEED); return 0; } diff --git a/src/geometry.cpp b/src/geometry.cpp index fbb0486500..0f89a0ecd8 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -32,7 +32,7 @@ std::vector overlap_check_count; // Non-member functions //============================================================================== -bool check_cell_overlap(Particle* p) +bool check_cell_overlap(Particle* p, bool error) { int n_coord = p->n_coord_; @@ -45,11 +45,14 @@ bool check_cell_overlap(Particle* p) Cell& c = *model::cells[index_cell]; if (c.contains(p->coord_[j].r, p->coord_[j].u, p->surface_)) { if (index_cell != p->coord_[j].cell) { - std::stringstream err_msg; - err_msg << "Overlapping cells detected: " << c.id_ << ", " - << model::cells[p->coord_[j].cell]->id_ << " on universe " - << univ.id_; - fatal_error(err_msg); + if (error) { + std::stringstream err_msg; + err_msg << "Overlapping cells detected: " << c.id_ << ", " + << model::cells[p->coord_[j].cell]->id_ << " on universe " + << univ.id_; + fatal_error(err_msg); + } + return true; } ++model::overlap_check_count[index_cell]; } diff --git a/src/hdf5_interface.cpp b/src/hdf5_interface.cpp index 1fb10b7066..b1a90cb8f7 100644 --- a/src/hdf5_interface.cpp +++ b/src/hdf5_interface.cpp @@ -366,10 +366,11 @@ member_names(hid_t group_id, H5O_type_t type) i, nullptr, 0, H5P_DEFAULT); // Read name - char buffer[size]; + char* buffer = new char[size]; H5Lget_name_by_idx(group_id, ".", H5_INDEX_NAME, H5_ITER_INC, i, buffer, size, H5P_DEFAULT); names.emplace_back(&buffer[0]); + delete[] buffer; } return names; } @@ -404,11 +405,13 @@ object_name(hid_t obj_id) { // Determine size and create buffer size_t size = 1 + H5Iget_name(obj_id, nullptr, 0); - char buffer[size]; + char* buffer = new char[size]; // Read and return name H5Iget_name(obj_id, buffer, size); - return buffer; + std::string str = buffer; + delete[] buffer; + return str; } @@ -789,6 +792,8 @@ const hid_t H5TypeMap::type_id = H5T_NATIVE_INT; template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_ULONG; template<> +const hid_t H5TypeMap::type_id = H5T_NATIVE_ULLONG; +template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_UINT; template<> const hid_t H5TypeMap::type_id = H5T_NATIVE_INT64; diff --git a/src/initialize.cpp b/src/initialize.cpp index f8a4f4a39a..fd524a2de9 100644 --- a/src/initialize.cpp +++ b/src/initialize.cpp @@ -68,7 +68,7 @@ int openmc_init(int argc, char* argv[], const void* intracomm) // Initialize random number generator -- if the user specifies a seed, it // will be re-initialized later - openmc_set_seed(DEFAULT_SEED); + openmc::openmc_set_seed(DEFAULT_SEED); // Read XML input files read_input_xml(); diff --git a/src/lattice.cpp b/src/lattice.cpp index b16fecc6e3..6f10ed3584 100644 --- a/src/lattice.cpp +++ b/src/lattice.cpp @@ -388,7 +388,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const hsize_t nx {static_cast(n_cells_[0])}; hsize_t ny {static_cast(n_cells_[1])}; hsize_t nz {static_cast(n_cells_[2])}; - int out[nx*ny*nz]; + std::vector out(nx*ny*nz); for (int m = 0; m < nz; m++) { for (int k = 0; k < ny; k++) { @@ -401,12 +401,12 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const } hsize_t dims[3] {nz, ny, nx}; - write_int(lat_group, 3, dims, "universes", out, false); + write_int(lat_group, 3, dims, "universes", out.data(), false); } else { hsize_t nx {static_cast(n_cells_[0])}; hsize_t ny {static_cast(n_cells_[1])}; - int out[nx*ny]; + std::vector out(nx*ny); for (int k = 0; k < ny; k++) { for (int j = 0; j < nx; j++) { @@ -417,7 +417,7 @@ RectLattice::to_hdf5_inner(hid_t lat_group) const } hsize_t dims[3] {1, ny, nx}; - write_int(lat_group, 3, dims, "universes", out, false); + write_int(lat_group, 3, dims, "universes", out.data(), false); } } @@ -440,6 +440,21 @@ HexLattice::HexLattice(pugi::xml_node lat_node) is_3d_ = false; } + // Read the lattice orientation. Default to 'y'. + if (check_for_node(lat_node, "orientation")) { + std::string orientation = get_node_value(lat_node, "orientation"); + if (orientation == "y") { + orientation_ = Orientation::y; + } else if (orientation == "x") { + orientation_ = Orientation::x; + } else { + fatal_error("Unrecognized orientation '" + orientation + + "' for lattice " + std::to_string(id_)); + } + } else { + orientation_ = Orientation::y; + } + // Read the lattice center. std::string center_str {get_node_value(lat_node, "center")}; std::vector center_words {split(center_str)}; @@ -482,14 +497,79 @@ HexLattice::HexLattice(pugi::xml_node lat_node) // Parse the universes. // Universes in hexagonal lattices are stored in a manner that represents - // a skewed coordinate system: (x, alpha) rather than (x, y). There is + // a skewed coordinate system: (x, alpha) in case of 'y' orientation + // and (alpha,y) in 'x' one rather than (x, y). There is // no obvious, direct relationship between the order of universes in the // input and the order that they will be stored in the skewed array so // the following code walks a set of index values across the skewed array // in a manner that matches the input order. Note that i_x = 0, i_a = 0 - // corresponds to the center of the hexagonal lattice. - + // or i_a = 0, i_y = 0 corresponds to the center of the hexagonal lattice. universes_.resize((2*n_rings_-1) * (2*n_rings_-1) * n_axial_, C_NONE); + if (orientation_ == Orientation::y) { + fill_lattice_y(univ_words); + } else { + fill_lattice_x(univ_words); + } +} + +//============================================================================== + +void +HexLattice::fill_lattice_x(const std::vector& univ_words) +{ + int input_index = 0; + for (int m = 0; m < n_axial_; m++) { + // Initialize lattice indecies. + int i_a = -(n_rings_ - 1); + int i_y = n_rings_ - 1; + + // Map upper region of hexagonal lattice which is found in the + // first n_rings-1 rows of the input. + for (int k = 0; k < n_rings_-1; k++) { + + // Iterate over the input columns. + for (int j = 0; j < k+n_rings_; j++) { + int indx = (2*n_rings_-1)*(2*n_rings_-1) * m + + (2*n_rings_-1) * (i_y+n_rings_-1) + + (i_a+n_rings_-1); + universes_[indx] = std::stoi(univ_words[input_index]); + input_index++; + // Move to the next right neighbour cell + i_a += 1; + } + + // Return the lattice index to the start of the current row. + i_a = -(n_rings_ - 1); + i_y -= 1; + } + + // Map the lower region from the centerline of cart to down side + for (int k = 0; k < n_rings_; k++) { + // Walk the index to the lower-right neighbor of the last row start. + i_a = -(n_rings_ - 1) + k; + + // Iterate over the input columns. + for (int j = 0; j < 2*n_rings_-k-1; j++) { + int indx = (2*n_rings_-1)*(2*n_rings_-1) * m + + (2*n_rings_-1) * (i_y+n_rings_-1) + + (i_a+n_rings_-1); + universes_[indx] = std::stoi(univ_words[input_index]); + input_index++; + // Move to the next right neighbour cell + i_a += 1; + } + + // Return lattice index to start of current row. + i_y -= 1; + } + } +} + +//============================================================================== + +void +HexLattice::fill_lattice_y(const std::vector& univ_words) +{ int input_index = 0; for (int m = 0; m < n_axial_; m++) { // Initialize lattice indecies. @@ -610,9 +690,33 @@ std::pair> HexLattice::distance(Position r, Direction u, const std::array& i_xyz) const { - // Compute the direction on the hexagonal basis. - double beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0; - double gamma_dir = u.x * std::sqrt(3.0) / 2.0 - u.y / 2.0; + // Short description of the direction vectors used here. The beta, gamma, and + // delta vectors point towards the flat sides of each hexagonal tile. + // Y - orientation: + // basis0 = (1, 0) + // basis1 = (-1/sqrt(3), 1) = +120 degrees from basis0 + // beta = (sqrt(3)/2, 1/2) = +30 degrees from basis0 + // gamma = (sqrt(3)/2, -1/2) = -60 degrees from beta + // delta = (0, 1) = +60 degrees from beta + // X - orientation: + // basis0 = (1/sqrt(3), -1) + // basis1 = (0, 1) = +120 degrees from basis0 + // beta = (1, 0) = +30 degrees from basis0 + // gamma = (1/2, -sqrt(3)/2) = -60 degrees from beta + // delta = (1/2, sqrt(3)/2) = +60 degrees from beta + // The z-axis is considered separately. + double beta_dir; + double gamma_dir; + double delta_dir; + if (orientation_ == Orientation::y) { + beta_dir = u.x * std::sqrt(3.0) / 2.0 + u.y / 2.0; + gamma_dir = u.x * std::sqrt(3.0) / 2.0 - u.y / 2.0; + delta_dir = u.y; + } else { + beta_dir = u.x; + gamma_dir = u.x / 2.0 - u.y * std::sqrt(3.0) / 2.0; + delta_dir = u.x / 2.0 + u.y * std::sqrt(3.0) / 2.0; + } // Note that hexagonal lattice distance calculations are performed // using the particle's coordinates relative to the neighbor lattice @@ -620,7 +724,7 @@ const // because there is significant disagreement between neighboring cells // on where the lattice boundary is due to finite precision issues. - // Upper-right and lower-left sides. + // beta direction double d {INFTY}; std::array lattice_trans; double edge = -copysign(0.5*pitch_[0], beta_dir); // Oncoming edge @@ -632,7 +736,12 @@ const const std::array i_xyz_t {i_xyz[0]-1, i_xyz[1], i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } - double beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0; + double beta; + if (orientation_ == Orientation::y) { + beta = r_t.x * std::sqrt(3.0) / 2.0 + r_t.y / 2.0; + } else { + beta = r_t.x; + } if ((std::abs(beta - edge) > FP_PRECISION) && beta_dir != 0) { d = (edge - beta) / beta_dir; if (beta_dir > 0) { @@ -642,7 +751,7 @@ const } } - // Lower-right and upper-left sides. + // gamma direction edge = -copysign(0.5*pitch_[0], gamma_dir); if (gamma_dir > 0) { const std::array i_xyz_t {i_xyz[0]+1, i_xyz[1]-1, i_xyz[2]}; @@ -651,7 +760,12 @@ const const std::array i_xyz_t {i_xyz[0]-1, i_xyz[1]+1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } - double gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0; + double gamma; + if (orientation_ == Orientation::y) { + gamma = r_t.x * std::sqrt(3.0) / 2.0 - r_t.y / 2.0; + } else { + gamma = r_t.x / 2.0 - r_t.y * std::sqrt(3.0) / 2.0; + } if ((std::abs(gamma - edge) > FP_PRECISION) && gamma_dir != 0) { double this_d = (edge - gamma) / gamma_dir; if (this_d < d) { @@ -664,19 +778,25 @@ const } } - // Upper and lower sides. - edge = -copysign(0.5*pitch_[0], u.y); - if (u.y > 0) { + // delta direction + edge = -copysign(0.5*pitch_[0], delta_dir); + if (delta_dir > 0) { const std::array i_xyz_t {i_xyz[0], i_xyz[1]+1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } else { const std::array i_xyz_t {i_xyz[0], i_xyz[1]-1, i_xyz[2]}; r_t = get_local_position(r, i_xyz_t); } - if ((std::abs(r_t.y - edge) > FP_PRECISION) && u.y != 0) { - double this_d = (edge - r_t.y) / u.y; + double delta; + if (orientation_ == Orientation::y) { + delta = r_t.y; + } else { + delta = r_t.x / 2.0 + r_t.y * std::sqrt(3.0) / 2.0; + } + if ((std::abs(delta - edge) > FP_PRECISION) && delta_dir != 0) { + double this_d = (edge - delta) / delta_dir; if (this_d < d) { - if (u.y > 0) { + if (delta_dir > 0) { lattice_trans = {0, 1, 0}; } else { lattice_trans = {0, -1, 0}; @@ -727,16 +847,25 @@ HexLattice::get_indices(Position r, Direction u) const } } - // Convert coordinates into skewed bases. The (x, alpha) basis is used to - // find the index of the global coordinates to within 4 cells. - double alpha = r_o.y - r_o.x / std::sqrt(3.0); - int ix = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0])); - int ia = std::floor(alpha / pitch_[0]); + int i1, i2; + if (orientation_ == Orientation::y) { + // Convert coordinates into skewed bases. The (x, alpha) basis is used to + // find the index of the global coordinates to within 4 cells. + double alpha = r_o.y - r_o.x / std::sqrt(3.0); + i1 = std::floor(r_o.x / (0.5*std::sqrt(3.0) * pitch_[0])); + i2 = std::floor(alpha / pitch_[0]); + } else { + // Convert coordinates into skewed bases. The (alpha, y) basis is used to + // find the index of the global coordinates to within 4 cells. + double alpha = r_o.y - r_o.x * std::sqrt(3.0); + i1 = std::floor(-alpha / (std::sqrt(3.0) * pitch_[0])); + i2 = std::floor(r_o.y / (0.5*std::sqrt(3.0) * pitch_[0])); + } - // Add offset to indices (the center cell is (i_x, i_alpha) = (0, 0) but + // Add offset to indices (the center cell is (i1, i2) = (0, 0) but // the array is offset so that the indices never go below 0). - ix += n_rings_-1; - ia += n_rings_-1; + i1 += n_rings_-1; + i2 += n_rings_-1; // Calculate the (squared) distance between the particle and the centers of // the four possible cells. Regular hexagonal tiles form a Voronoi @@ -755,14 +884,14 @@ HexLattice::get_indices(Position r, Direction u) const // is kept (i.e. the cell with the lowest dot product as the vectors will be // completely opposed if the particle is moving directly toward the center of // the cell). - int ix_chg {}; - int ia_chg {}; + int i1_chg {}; + int i2_chg {}; double d_min {INFTY}; double dp_min {INFTY}; for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { // get local coordinates - const std::array i_xyz {ix + j, ia + i, 0}; + const std::array i_xyz {i1 + j, i2 + i, 0}; Position r_t = get_local_position(r, i_xyz); // calculate distance double d = r_t.x*r_t.x + r_t.y*r_t.y; @@ -777,18 +906,18 @@ HexLattice::get_indices(Position r, Direction u) const if (on_boundary && dp > dp_min) continue; // update values d_min = d; - ix_chg = j; - ia_chg = i; + i1_chg = j; + i2_chg = i; dp_min = dp; } } } // update outgoing indices - ix += ix_chg; - ia += ia_chg; + i1 += i1_chg; + i2 += i2_chg; - return {ix, ia, iz}; + return {i1, i2, iz}; } //============================================================================== @@ -797,15 +926,26 @@ Position HexLattice::get_local_position(Position r, const std::array i_xyz) const { - // x_l = x_g - (center + pitch_x*cos(30)*index_x) - r.x -= center_.x + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings_ + 1) * pitch_[0]; - // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) - r.y -= (center_.y + (i_xyz[1] - n_rings_ + 1) * pitch_[0] - + (i_xyz[0] - n_rings_ + 1) * pitch_[0] / 2.0); - if (is_3d_) { - r.z -= center_.z - (0.5 * n_axial_ - i_xyz[2] - 0.5) * pitch_[1]; + if (orientation_ == Orientation::y) { + // x_l = x_g - (center + pitch_x*cos(30)*index_x) + r.x -= center_.x + + std::sqrt(3.0)/2.0 * (i_xyz[0] - n_rings_ + 1) * pitch_[0]; + // y_l = y_g - (center + pitch_x*index_x + pitch_y*sin(30)*index_y) + r.y -= (center_.y + (i_xyz[1] - n_rings_ + 1) * pitch_[0] + + (i_xyz[0] - n_rings_ + 1) * pitch_[0] / 2.0); + } else { + // x_l = x_g - (center + pitch_x*index_a + pitch_y*sin(30)*index_y) + r.x -= (center_.x + (i_xyz[0] - n_rings_ + 1) * pitch_[0] + + (i_xyz[1] - n_rings_ + 1) * pitch_[0] / 2.0); + // y_l = y_g - (center + pitch_y*cos(30)*index_y) + r.y -= center_.y + + std::sqrt(3.0)/2.0 * (i_xyz[1] - n_rings_ + 1) * pitch_[0]; } + if (is_3d_) { + r.z -= center_.z - (0.5 * n_axial_ - i_xyz[2] - 0.5) * pitch_[1]; + } + return r; } @@ -863,6 +1003,11 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const write_string(lat_group, "type", "hexagonal", false); write_dataset(lat_group, "n_rings", n_rings_); write_dataset(lat_group, "n_axial", n_axial_); + if (orientation_ == Orientation::y) { + write_string(lat_group, "orientation", "y", false); + } else { + write_string(lat_group, "orientation", "x", false); + } if (is_3d_) { write_dataset(lat_group, "pitch", pitch_); write_dataset(lat_group, "center", center_); @@ -877,7 +1022,7 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const hsize_t nx {static_cast(2*n_rings_ - 1)}; hsize_t ny {static_cast(2*n_rings_ - 1)}; hsize_t nz {static_cast(n_axial_)}; - int out[nx*ny*nz]; + std::vector out(nx*ny*nz); for (int m = 0; m < nz; m++) { for (int k = 0; k < ny; k++) { @@ -897,7 +1042,7 @@ HexLattice::to_hdf5_inner(hid_t lat_group) const } hsize_t dims[3] {nz, ny, nx}; - write_int(lat_group, 3, dims, "universes", out, false); + write_int(lat_group, 3, dims, "universes", out.data(), false); } //============================================================================== diff --git a/src/material.cpp b/src/material.cpp index b5cb2c47a7..5667821fb7 100644 --- a/src/material.cpp +++ b/src/material.cpp @@ -1169,7 +1169,7 @@ openmc_material_add_nuclide(int32_t index, const char* name, double density) // Create copy of atom_density_ array with one extra entry xt::xtensor atom_density = xt::zeros({n}); xt::view(atom_density, xt::range(0, n-1)) = m->atom_density_; - atom_density(n) = density; + atom_density(n-1) = density; m->atom_density_ = atom_density; m->density_ += density; @@ -1203,6 +1203,19 @@ openmc_material_get_densities(int32_t index, int** nuclides, double** densities, } } +extern "C" int +openmc_material_get_density(int32_t index, double* density) +{ + if (index >= 0 && index < model::materials.size()) { + auto& mat = model::materials[index]; + *density = mat->density_gpcc_; + return 0; + } else { + set_errmsg("Index in materials array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } +} + extern "C" int openmc_material_get_fissionable(int32_t index, bool* fissionable) { diff --git a/src/math_functions.cpp b/src/math_functions.cpp index 54d0e93441..ec5d64eac7 100644 --- a/src/math_functions.cpp +++ b/src/math_functions.cpp @@ -110,12 +110,13 @@ void calc_pn_c(int n, double x, double pnx[]) double evaluate_legendre(int n, const double data[], double x) { - double pnx[n + 1]; + double* pnx = new double[n + 1]; double val = 0.0; calc_pn_c(n, x, pnx); for (int l = 0; l <= n; l++) { val += (l + 0.5) * data[l] * pnx[l]; } + delete[] pnx; return val; } @@ -536,8 +537,8 @@ void calc_zn(int n, double rho, double phi, double zn[]) { double sin_phi = std::sin(phi); double cos_phi = std::cos(phi); - double sin_phi_vec[n + 1]; // Sin[n * phi] - double cos_phi_vec[n + 1]; // Cos[n * phi] + std::vector sin_phi_vec(n + 1); // Sin[n * phi] + std::vector cos_phi_vec(n + 1); // Cos[n * phi] sin_phi_vec[0] = 1.0; cos_phi_vec[0] = 1.0; sin_phi_vec[1] = 2.0 * cos_phi; @@ -554,8 +555,8 @@ void calc_zn(int n, double rho, double phi, double zn[]) { // =========================================================================== // Calculate R_pq(rho) - double zn_mat[n + 1][n + 1]; // Matrix forms of the coefficients which are - // easier to work with + // Matrix forms of the coefficients which are easier to work with + std::vector> zn_mat(n + 1, std::vector(n + 1)); // Fill the main diagonal first (Eq 3.9 in Chong) for (int p = 0; p <= n; p++) { @@ -763,7 +764,7 @@ void broaden_wmp_polynomials(double E, double dopp, int n, double factors[]) void spline(int n, const double x[], const double y[], double z[]) { - double c_new[n-1]; + std::vector c_new(n-1); // Set natural boundary conditions c_new[0] = 0.0; diff --git a/src/mesh.cpp b/src/mesh.cpp index ddfa272a6d..ac02b679e8 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -31,7 +31,7 @@ namespace openmc { namespace model { -std::vector> meshes; +std::vector> meshes; std::unordered_map mesh_map; } // namespace model @@ -63,10 +63,10 @@ inline bool check_intersection_point(double x1, double x0, double y1, } //============================================================================== -// RegularMesh implementation +// Mesh implementation //============================================================================== -RegularMesh::RegularMesh(pugi::xml_node node) +Mesh::Mesh(pugi::xml_node node) { // Copy mesh id if (check_for_node(node, "id")) { @@ -78,17 +78,15 @@ RegularMesh::RegularMesh(pugi::xml_node node) std::to_string(id_)); } } +} - // Read mesh type - if (check_for_node(node, "type")) { - auto temp = get_node_value(node, "type", true, true); - if (temp == "regular") { - // TODO: move elsewhere - } else { - fatal_error("Invalid mesh type: " + temp); - } - } +//============================================================================== +// RegularMesh implementation +//============================================================================== +RegularMesh::RegularMesh(pugi::xml_node node) + : Mesh {node} +{ // Determine number of dimensions for mesh if (check_for_node(node, "dimension")) { shape_ = get_node_xarray(node, "dimension"); @@ -181,13 +179,13 @@ int RegularMesh::get_bin(Position r) const } // Determine indices - int ijk[n_dimension_]; + std::vector ijk(n_dimension_); bool in_mesh; - get_indices(r, ijk, &in_mesh); + get_indices(r, ijk.data(), &in_mesh); if (!in_mesh) return -1; // Convert indices to bin - return get_bin_from_indices(ijk); + return get_bin_from_indices(ijk.data()); } int RegularMesh::get_bin_from_indices(const int* ijk) const @@ -230,6 +228,18 @@ void RegularMesh::get_indices_from_bin(int bin, int* ijk) const } } +int RegularMesh::n_bins() const +{ + int n_bins = 1; + for (auto dim : shape_) n_bins *= dim; + return n_bins; +} + +int RegularMesh::n_surface_bins() const +{ + return 4 * n_dimension_ * n_bins(); +} + bool RegularMesh::intersects(Position& r0, Position r1, int* ijk) const { switch(n_dimension_) { @@ -495,11 +505,11 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, // Determine the mesh indices for the starting and ending coords. int n = n_dimension_; - int ijk0[n], ijk1[n]; + std::vector ijk0(n), ijk1(n); bool start_in_mesh; - get_indices(r0, ijk0, &start_in_mesh); + get_indices(r0, ijk0.data(), &start_in_mesh); bool end_in_mesh; - get_indices(r1, ijk1, &end_in_mesh); + get_indices(r1, ijk1.data(), &end_in_mesh); // Reset coordinates and check for a mesh intersection if necessary. if (start_in_mesh) { @@ -509,7 +519,7 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, // The initial coords do not lie in the mesh. Check to see if the particle // eventually intersects the mesh and compute the relevant coords and // indices. - if (!intersects(r0, r1, ijk0)) return; + if (!intersects(r0, r1, ijk0.data())) return; } r1 = r; @@ -517,17 +527,17 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, // Find which mesh cells are traversed and the length of each traversal. while (true) { - if (std::equal(ijk0, ijk0+n, ijk1)) { + if (ijk0 == ijk1) { // The track ends in this cell. Use the particle end location rather // than the mesh surface and stop iterating. double distance = (r1 - r0).norm(); - bins.push_back(get_bin_from_indices(ijk0)); + bins.push_back(get_bin_from_indices(ijk0.data())); lengths.push_back(distance / total_distance); break; } // The track exits this cell. Determine the distance to each mesh surface. - double d[n]; + std::vector d(n); for (int k = 0; k < n; ++k) { if (std::fabs(u[k]) < FP_PRECISION) { d[k] = INFTY; @@ -541,9 +551,9 @@ void RegularMesh::bins_crossed(const Particle* p, std::vector& bins, } // Pick the closest mesh surface and append this traversal to the output. - auto j = std::min_element(d, d+n) - d; + auto j = std::min_element(d.begin(), d.end()) - d.begin(); double distance = d[j]; - bins.push_back(get_bin_from_indices(ijk0)); + bins.push_back(get_bin_from_indices(ijk0.data())); lengths.push_back(distance / total_distance); // Translate to the oncoming mesh surface. @@ -582,18 +592,17 @@ void RegularMesh::surface_bins_crossed(const Particle* p, // Determine indices for starting and ending location. int n = n_dimension_; - int ijk0[n], ijk1[n]; + std::vector ijk0(n), ijk1(n); bool start_in_mesh; - get_indices(r0, ijk0, &start_in_mesh); + get_indices(r0, ijk0.data(), &start_in_mesh); bool end_in_mesh; - get_indices(r1, ijk1, &end_in_mesh); + get_indices(r1, ijk1.data(), &end_in_mesh); // Check if the track intersects any part of the mesh. if (!start_in_mesh) { Position r0_copy = r0; - int ijk0_copy[n]; - for (int i = 0; i < n; ++i) ijk0_copy[i] = ijk0[i]; - if (!intersects(r0_copy, r1, ijk0_copy)) return; + std::vector ijk0_copy(ijk0); + if (!intersects(r0_copy, r1, ijk0_copy.data())) return; } // ======================================================================== @@ -651,7 +660,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p, // Outward current on i max surface if (in_mesh) { int i_surf = 4*i + 3; - int i_mesh = get_bin_from_indices(ijk0); + int i_mesh = get_bin_from_indices(ijk0.data()); int i_bin = 4*n*i_mesh + i_surf - 1; bins.push_back(i_bin); @@ -672,7 +681,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p, // i min surface if (in_mesh) { int i_surf = 4*i + 2; - int i_mesh = get_bin_from_indices(ijk0); + int i_mesh = get_bin_from_indices(ijk0.data()); int i_bin = 4*n*i_mesh + i_surf - 1; bins.push_back(i_bin); @@ -684,7 +693,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p, // Outward current on i min surface if (in_mesh) { int i_surf = 4*i + 1; - int i_mesh = get_bin_from_indices(ijk0); + int i_mesh = get_bin_from_indices(ijk0.data()); int i_bin = 4*n*i_mesh + i_surf - 1; bins.push_back(i_bin); @@ -705,7 +714,7 @@ void RegularMesh::surface_bins_crossed(const Particle* p, // i max surface if (in_mesh) { int i_surf = 4*i + 4; - int i_mesh = get_bin_from_indices(ijk0); + int i_mesh = get_bin_from_indices(ijk0.data()); int i_bin = 4*n*i_mesh + i_surf - 1; bins.push_back(i_bin); @@ -719,6 +728,42 @@ void RegularMesh::surface_bins_crossed(const Particle* p, } } +std::pair, std::vector> +RegularMesh::plot(Position plot_ll, Position plot_ur) const +{ + // Figure out which axes lie in the plane of the plot. + std::array axes {-1, -1}; + if (plot_ur.z == plot_ll.z) { + axes[0] = 0; + if (n_dimension_ > 1) axes[1] = 1; + } else if (plot_ur.y == plot_ll.y) { + axes[0] = 0; + if (n_dimension_ > 2) axes[1] = 2; + } else if (plot_ur.x == plot_ll.x) { + if (n_dimension_ > 1) axes[0] = 1; + if (n_dimension_ > 2) axes[1] = 2; + } else { + fatal_error("Can only plot mesh lines on an axis-aligned plot"); + } + + // Get the coordinates of the mesh lines along both of the axes. + std::array, 2> axis_lines; + for (int i_ax = 0; i_ax < 2; ++i_ax) { + int axis = axes[i_ax]; + if (axis == -1) continue; + auto& lines {axis_lines[i_ax]}; + + double coord = lower_left_[axis]; + for (int i = 0; i < shape_[axis] + 1; ++i) { + if (coord >= plot_ll[axis] && coord <= plot_ur[axis]) + lines.push_back(coord); + coord += width_[axis]; + } + } + + return {axis_lines[0], axis_lines[1]}; +} + void RegularMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); @@ -732,7 +777,7 @@ void RegularMesh::to_hdf5(hid_t group) const close_group(mesh_group); } -xt::xarray +xt::xtensor RegularMesh::count_sites(const std::vector& bank, bool* outside) const { @@ -783,6 +828,529 @@ RegularMesh::count_sites(const std::vector& bank, return counts; } +//============================================================================== +// RectilinearMesh implementation +//============================================================================== + +RectilinearMesh::RectilinearMesh(pugi::xml_node node) + : Mesh {node} +{ + n_dimension_ = 3; + + grid_.resize(3); + grid_[0] = get_node_array(node, "x_grid"); + grid_[1] = get_node_array(node, "y_grid"); + grid_[2] = get_node_array(node, "z_grid"); + + shape_ = {static_cast(grid_[0].size()) - 1, + static_cast(grid_[1].size()) - 1, + static_cast(grid_[2].size()) - 1}; + + for (const auto& g : grid_) { + if (g.size() < 2) fatal_error("x-, y-, and z- grids for rectilinear meshes " + "must each have at least 2 points"); + for (int i = 1; i < g.size(); ++i) { + if (g[i] <= g[i-1]) fatal_error("Values in for x-, y-, and z- grids for " + "rectilinear meshes must be sorted and unique."); + } + } + + lower_left_ = {grid_[0].front(), grid_[1].front(), grid_[2].front()}; + upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()}; +} + +void RectilinearMesh::bins_crossed(const Particle* p, std::vector& bins, + std::vector& lengths) const +{ + // ======================================================================== + // Determine where the track intersects the mesh and if it intersects at all. + + // Copy the starting and ending coordinates of the particle. + Position last_r {p->r_last_}; + Position r {p->r()}; + Direction u {p->u()}; + + // Compute the length of the entire track. + double total_distance = (r - last_r).norm(); + + // While determining if this track intersects the mesh, offset the starting + // and ending coords by a bit. This avoid finite-precision errors that can + // occur when the mesh surfaces coincide with lattice or geometric surfaces. + Position r0 = last_r + TINY_BIT*u; + Position r1 = r - TINY_BIT*u; + + // Determine the mesh indices for the starting and ending coords. + int ijk0[3], ijk1[3]; + bool start_in_mesh; + get_indices(r0, ijk0, &start_in_mesh); + bool end_in_mesh; + get_indices(r1, ijk1, &end_in_mesh); + + // Reset coordinates and check for a mesh intersection if necessary. + if (start_in_mesh) { + // The initial coords lie in the mesh, use those coords for tallying. + r0 = last_r; + } else { + // The initial coords do not lie in the mesh. Check to see if the particle + // eventually intersects the mesh and compute the relevant coords and + // indices. + if (!intersects(r0, r1, ijk0)) return; + } + r1 = r; + + // ======================================================================== + // Find which mesh cells are traversed and the length of each traversal. + + while (true) { + if (std::equal(ijk0, ijk0+3, ijk1)) { + // The track ends in this cell. Use the particle end location rather + // than the mesh surface and stop iterating. + double distance = (r1 - r0).norm(); + bins.push_back(get_bin_from_indices(ijk0)); + lengths.push_back(distance / total_distance); + break; + } + + // The track exits this cell. Determine the distance to each mesh surface. + double d[3]; + for (int k = 0; k < 3; ++k) { + if (std::fabs(u[k]) < FP_PRECISION) { + d[k] = INFTY; + } else if (u[k] > 0) { + double xyz_cross = grid_[k][ijk0[k]]; + d[k] = (xyz_cross - r0[k]) / u[k]; + } else { + double xyz_cross = grid_[k][ijk0[k] - 1]; + d[k] = (xyz_cross - r0[k]) / u[k]; + } + } + + // Pick the closest mesh surface and append this traversal to the output. + auto j = std::min_element(d, d+3) - d; + double distance = d[j]; + bins.push_back(get_bin_from_indices(ijk0)); + lengths.push_back(distance / total_distance); + + // Translate to the oncoming mesh surface. + r0 += distance * u; + + // Increment the indices into the next mesh cell. + if (u[j] > 0.0) { + ++ijk0[j]; + } else { + --ijk0[j]; + } + + // If the next indices are invalid, then the track has left the mesh and + // we are done. + bool in_mesh = true; + for (int i = 0; i < 3; ++i) { + if (ijk0[i] < 1 || ijk0[i] > shape_[i]) { + in_mesh = false; + break; + } + } + if (!in_mesh) break; + } +} + +void RectilinearMesh::surface_bins_crossed(const Particle* p, + std::vector& bins) const +{ + // ======================================================================== + // Determine if the track intersects the tally mesh. + + // Copy the starting and ending coordinates of the particle. + Position r0 {p->r_last_current_}; + Position r1 {p->r()}; + Direction u {p->u()}; + + // Determine indices for starting and ending location. + int ijk0[3], ijk1[3]; + bool start_in_mesh; + get_indices(r0, ijk0, &start_in_mesh); + bool end_in_mesh; + get_indices(r1, ijk1, &end_in_mesh); + + // If the starting coordinates do not lie in the mesh, compute the coords and + // mesh indices of the first intersection, and add the bin for this first + // intersection. Return if the particle does not intersect the mesh at all. + if (!start_in_mesh) { + // Compute the incoming intersection coordinates and indices. + if (!intersects(r0, r1, ijk0)) return; + + // Determine which surface the particle entered. + double min_dist = INFTY; + int i_surf; + for (int i = 0; i < 3; ++i) { + if (u[i] > 0.0 && ijk0[i] == 1) { + double d = std::abs(r0[i] - grid_[i][0]); + if (d < min_dist) { + min_dist = d; + i_surf = 4*i + 2; + } + } else if (u[i] < 0.0 && ijk0[i] == shape_[i]) { + double d = std::abs(r0[i] - grid_[i][shape_[i]]); + if (d < min_dist) { + min_dist = d; + i_surf = 4*i + 4; + } + } // u[i] == 0 intentionally skipped + } + + // Add the incoming current bin. + int i_mesh = get_bin_from_indices(ijk0); + int i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + } + + // If the ending coordinates do not lie in the mesh, compute the coords and + // mesh indices of the last intersection, and add the bin for this last + // intersection. + if (!end_in_mesh) { + // Compute the outgoing intersection coordinates and indices. + intersects(r1, r0, ijk1); + + // Determine which surface the particle exited. + double min_dist = INFTY; + int i_surf; + for (int i = 0; i < 3; ++i) { + if (u[i] > 0.0 && ijk1[i] == shape_[i]) { + double d = std::abs(r1[i] - grid_[i][shape_[i]]); + if (d < min_dist) { + min_dist = d; + i_surf = 4*i + 3; + } + } else if (u[i] < 0.0 && ijk1[i] == 1) { + double d = std::abs(r1[i] - grid_[i][0]); + if (d < min_dist) { + min_dist = d; + i_surf = 4*i + 1; + } + } // u[i] == 0 intentionally skipped + } + + // Add the outgoing current bin. + int i_mesh = get_bin_from_indices(ijk1); + int i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + } + + // ======================================================================== + // Find which mesh surfaces are crossed. + + // Calculate number of surface crossings + int n_cross = 0; + for (int i = 0; i < 3; ++i) n_cross += std::abs(ijk1[i] - ijk0[i]); + if (n_cross == 0) return; + + // Bounding coordinates + Position xyz_cross; + for (int i = 0; i < 3; ++i) { + if (u[i] > 0.0) { + xyz_cross[i] = grid_[i][ijk0[i]]; + } else { + xyz_cross[i] = grid_[i][ijk0[i] - 1]; + } + } + + for (int j = 0; j < n_cross; ++j) { + // Set the distances to infinity + Position d {INFTY, INFTY, INFTY}; + + // Determine closest bounding surface. We need to treat + // special case where the cosine of the angle is zero since this would + // result in a divide-by-zero. + double distance = INFTY; + for (int i = 0; i < 3; ++i) { + if (u[i] == 0) { + d[i] = INFTY; + } else { + d[i] = (xyz_cross[i] - r0[i])/u[i]; + } + distance = std::min(distance, d[i]); + } + + // Loop over the dimensions + for (int i = 0; i < 3; ++i) { + // Check whether distance is the shortest distance + if (distance == d[i]) { + + // Check whether particle is moving in positive i direction + if (u[i] > 0) { + + // Outward current on i max surface + int i_surf = 4*i + 3; + int i_mesh = get_bin_from_indices(ijk0); + int i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + + // Advance position + ++ijk0[i]; + xyz_cross[i] = grid_[i][ijk0[i]]; + + // Inward current on i min surface + i_surf = 4*i + 2; + i_mesh = get_bin_from_indices(ijk0); + i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + + } else { + // The particle is moving in the negative i direction + + // Outward current on i min surface + int i_surf = 4*i + 1; + int i_mesh = get_bin_from_indices(ijk0); + int i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + + // Advance position + --ijk0[i]; + xyz_cross[i] = grid_[i][ijk0[i] - 1]; + + // Inward current on i min surface + i_surf = 4*i + 4; + i_mesh = get_bin_from_indices(ijk0); + i_bin = 4*3*i_mesh + i_surf - 1; + bins.push_back(i_bin); + } + } + } + + // Calculate new coordinates + r0 += distance * u; + } +} + +int RectilinearMesh::get_bin(Position r) const +{ + // Determine indices + int ijk[3]; + bool in_mesh; + get_indices(r, ijk, &in_mesh); + if (!in_mesh) return -1; + + // Convert indices to bin + return get_bin_from_indices(ijk); +} + +int RectilinearMesh::get_bin_from_indices(const int* ijk) const +{ + return ((ijk[2] - 1)*shape_[1] + (ijk[1] - 1))*shape_[0] + ijk[0] - 1; +} + +void RectilinearMesh::get_indices(Position r, int* ijk, bool* in_mesh) const +{ + *in_mesh = true; + + for (int i = 0; i < 3; ++i) { + if (r[i] < grid_[i].front() || r[i] > grid_[i].back()) { + ijk[i] = -1; + *in_mesh = false; + } else { + ijk[i] = lower_bound_index(grid_[i].begin(), grid_[i].end(), r[i]) + 1; + } + } +} + +void RectilinearMesh::get_indices_from_bin(int bin, int* ijk) const +{ + ijk[0] = bin % shape_[0] + 1; + ijk[1] = (bin % (shape_[0] * shape_[1])) / shape_[0] + 1; + ijk[2] = bin / (shape_[0] * shape_[1]) + 1; +} + +int RectilinearMesh::n_bins() const +{ + int n_bins = 1; + for (auto dim : shape_) n_bins *= dim; + return n_bins; +} + +int RectilinearMesh::n_surface_bins() const +{ + return 4 * n_dimension_ * n_bins(); +} + +std::pair, std::vector> +RectilinearMesh::plot(Position plot_ll, Position plot_ur) const +{ + // Figure out which axes lie in the plane of the plot. + std::array axes {-1, -1}; + if (plot_ur.z == plot_ll.z) { + axes = {0, 1}; + } else if (plot_ur.y == plot_ll.y) { + axes = {0, 2}; + } else if (plot_ur.x == plot_ll.x) { + axes = {1, 2}; + } else { + fatal_error("Can only plot mesh lines on an axis-aligned plot"); + } + + // Get the coordinates of the mesh lines along both of the axes. + std::array, 2> axis_lines; + for (int i_ax = 0; i_ax < 2; ++i_ax) { + int axis = axes[i_ax]; + std::vector& lines {axis_lines[i_ax]}; + + for (auto coord : grid_[axis]) { + if (coord >= plot_ll[axis] && coord <= plot_ur[axis]) + lines.push_back(coord); + } + } + + return {axis_lines[0], axis_lines[1]}; +} + +void RectilinearMesh::to_hdf5(hid_t group) const +{ + hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); + + write_dataset(mesh_group, "type", "rectilinear"); + write_dataset(mesh_group, "x_grid", grid_[0]); + write_dataset(mesh_group, "y_grid", grid_[1]); + write_dataset(mesh_group, "z_grid", grid_[2]); + + close_group(mesh_group); +} + +bool RectilinearMesh::intersects(Position& r0, Position r1, int* ijk) const +{ + // Copy coordinates of starting point + double x0 = r0.x; + double y0 = r0.y; + double z0 = r0.z; + + // Copy coordinates of ending point + double x1 = r1.x; + double y1 = r1.y; + double z1 = r1.z; + + // Copy coordinates of mesh lower_left + double xm0 = grid_[0].front(); + double ym0 = grid_[1].front(); + double zm0 = grid_[2].front(); + + // Copy coordinates of mesh upper_right + double xm1 = grid_[0].back(); + double ym1 = grid_[1].back(); + double zm1 = grid_[2].back(); + + double min_dist = INFTY; + + // Check if line intersects left surface -- calculate the intersection point + // (y,z) + if ((x0 < xm0 && x1 > xm0) || (x0 > xm0 && x1 < xm0)) { + double yi = y0 + (xm0 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm0 - x0) * (z1 - z0) / (x1 - x0); + if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { + if (check_intersection_point(xm0, x0, yi, y0, zi, z0, r0, min_dist)) { + ijk[0] = 1; + ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1; + ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1; + } + } + } + + // Check if line intersects back surface -- calculate the intersection point + // (x,z) + if ((y0 < ym0 && y1 > ym0) || (y0 > ym0 && y1 < ym0)) { + double xi = x0 + (ym0 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym0 - y0) * (z1 - z0) / (y1 - y0); + if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { + if (check_intersection_point(xi, x0, ym0, y0, zi, z0, r0, min_dist)) { + ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1; + ijk[1] = 1; + ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1; + } + } + } + + // Check if line intersects bottom surface -- calculate the intersection + // point (x,y) + if ((z0 < zm0 && z1 > zm0) || (z0 > zm0 && z1 < zm0)) { + double xi = x0 + (zm0 - z0) * (x1 - x0) / (z1 - z0); + double yi = y0 + (zm0 - z0) * (y1 - y0) / (z1 - z0); + if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { + if (check_intersection_point(xi, x0, yi, y0, zm0, z0, r0, min_dist)) { + ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1; + ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1; + ijk[2] = 1; + } + } + } + + // Check if line intersects right surface -- calculate the intersection point + // (y,z) + if ((x0 < xm1 && x1 > xm1) || (x0 > xm1 && x1 < xm1)) { + double yi = y0 + (xm1 - x0) * (y1 - y0) / (x1 - x0); + double zi = z0 + (xm1 - x0) * (z1 - z0) / (x1 - x0); + if (yi >= ym0 && yi < ym1 && zi >= zm0 && zi < zm1) { + if (check_intersection_point(xm1, x0, yi, y0, zi, z0, r0, min_dist)) { + ijk[0] = shape_[0]; + ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1; + ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1; + } + } + } + + // Check if line intersects front surface -- calculate the intersection point + // (x,z) + if ((y0 < ym1 && y1 > ym1) || (y0 > ym1 && y1 < ym1)) { + double xi = x0 + (ym1 - y0) * (x1 - x0) / (y1 - y0); + double zi = z0 + (ym1 - y0) * (z1 - z0) / (y1 - y0); + if (xi >= xm0 && xi < xm1 && zi >= zm0 && zi < zm1) { + if (check_intersection_point(xi, x0, ym1, y0, zi, z0, r0, min_dist)) { + ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1; + ijk[1] = shape_[1]; + ijk[2] = lower_bound_index(grid_[2].begin(), grid_[2].end(), zi) + 1; + } + } + } + + // Check if line intersects top surface -- calculate the intersection point + // (x,y) + if ((z0 < zm1 && z1 > zm1) || (z0 > zm1 && z1 < zm1)) { + double xi = x0 + (zm1 - z0) * (x1 - x0) / (z1 - z0); + double yi = y0 + (zm1 - z0) * (y1 - y0) / (z1 - z0); + if (xi >= xm0 && xi < xm1 && yi >= ym0 && yi < ym1) { + if (check_intersection_point(xi, x0, yi, y0, zm1, z0, r0, min_dist)) { + ijk[0] = lower_bound_index(grid_[0].begin(), grid_[0].end(), xi) + 1; + ijk[1] = lower_bound_index(grid_[1].begin(), grid_[1].end(), yi) + 1; + ijk[2] = shape_[2]; + } + } + } + + return min_dist < INFTY; +} + +//============================================================================== +// Helper functions for the C API +//============================================================================== + +int +check_mesh(int32_t index) +{ + if (index < 0 || index >= model::meshes.size()) { + set_errmsg("Index in meshes array is out of bounds."); + return OPENMC_E_OUT_OF_BOUNDS; + } + return 0; +} + +int +check_regular_mesh(int32_t index, RegularMesh** mesh) +{ + if (int err = check_mesh(index)) return err; + *mesh = dynamic_cast(model::meshes[index].get()); + if (!*mesh) { + set_errmsg("This function is only valid for regular meshes."); + return OPENMC_E_INVALID_TYPE; + } + return 0; +} + //============================================================================== // C API functions //============================================================================== @@ -817,10 +1385,7 @@ openmc_get_mesh_index(int32_t id, int32_t* index) extern "C" int openmc_mesh_get_id(int32_t index, int32_t* id) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } + if (int err = check_mesh(index)) return err; *id = model::meshes[index]->id_; return 0; } @@ -829,10 +1394,7 @@ openmc_mesh_get_id(int32_t index, int32_t* id) extern "C" int openmc_mesh_set_id(int32_t index, int32_t id) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } + if (int err = check_mesh(index)) return err; model::meshes[index]->id_ = id; model::mesh_map[id] = index; return 0; @@ -842,12 +1404,10 @@ openmc_mesh_set_id(int32_t index, int32_t id) extern "C" int openmc_mesh_get_dimension(int32_t index, int** dims, int* n) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } - *dims = model::meshes[index]->shape_.data(); - *n = model::meshes[index]->n_dimension_; + RegularMesh* mesh; + if (int err = check_regular_mesh(index, &mesh)) return err; + *dims = mesh->shape_.data(); + *n = mesh->n_dimension_; return 0; } @@ -855,16 +1415,13 @@ openmc_mesh_get_dimension(int32_t index, int** dims, int* n) extern "C" int openmc_mesh_set_dimension(int32_t index, int n, const int* dims) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } + RegularMesh* mesh; + if (int err = check_regular_mesh(index, &mesh)) return err; // Copy dimension std::vector shape = {static_cast(n)}; - auto& m = model::meshes[index]; - m->shape_ = xt::adapt(dims, n, xt::no_ownership(), shape); - m->n_dimension_ = m->shape_.size(); + mesh->shape_ = xt::adapt(dims, n, xt::no_ownership(), shape); + mesh->n_dimension_ = mesh->shape_.size(); return 0; } @@ -873,12 +1430,9 @@ openmc_mesh_set_dimension(int32_t index, int n, const int* dims) extern "C" int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } + RegularMesh* m; + if (int err = check_regular_mesh(index, &m)) return err; - auto& m = model::meshes[index]; if (m->lower_left_.dimension() == 0) { set_errmsg("Mesh parameters have not been set."); return OPENMC_E_ALLOCATE; @@ -896,12 +1450,9 @@ extern "C" int openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width) { - if (index < 0 || index >= model::meshes.size()) { - set_errmsg("Index in meshes array is out of bounds."); - return OPENMC_E_OUT_OF_BOUNDS; - } + RegularMesh* m; + if (int err = check_regular_mesh(index, &m)) return err; - auto& m = model::meshes[index]; std::vector shape = {static_cast(n)}; if (ll && ur) { m->lower_left_ = xt::adapt(ll, n, xt::no_ownership(), shape); @@ -930,8 +1481,21 @@ openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, void read_meshes(pugi::xml_node root) { for (auto node : root.children("mesh")) { + std::string mesh_type; + if (check_for_node(node, "type")) { + mesh_type = get_node_value(node, "type", true, true); + } else { + mesh_type = "regular"; + } + // Read mesh and add to vector - model::meshes.push_back(std::make_unique(node)); + if (mesh_type == "regular") { + model::meshes.push_back(std::make_unique(node)); + } else if (mesh_type == "rectilinear") { + model::meshes.push_back(std::make_unique(node)); + } else { + fatal_error("Invalid mesh type: " + mesh_type); + } // Map ID to position in vector model::mesh_map[model::meshes.back()->id_] = model::meshes.size() - 1; diff --git a/src/mgxs.cpp b/src/mgxs.cpp index efe562fa28..3aac41f35b 100644 --- a/src/mgxs.cpp +++ b/src/mgxs.cpp @@ -96,7 +96,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector& temperature, // Determine the available temperatures hid_t kT_group = open_group(xs_id, "kTs"); int num_temps = get_num_datasets(kT_group); - char* dset_names[num_temps]; + char** dset_names = new char*[num_temps]; for (int i = 0; i < num_temps; i++) { dset_names[i] = new char[151]; } @@ -111,6 +111,7 @@ Mgxs::metadata_from_hdf5(hid_t xs_id, const std::vector& temperature, // Done with dset_names, so delete it delete[] dset_names[i]; } + delete[] dset_names; std::sort(available_temps.begin(), available_temps.end()); // If only one temperature is available, lets just use nearest temperature diff --git a/src/output.cpp b/src/output.cpp index bbc2588ac4..dff39c15fe 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -75,7 +75,7 @@ void title() " Copyright | 2011-2019 MIT and OpenMC contributors\n" << " License | http://openmc.readthedocs.io/en/latest/license.html\n" << " Version | " << VERSION_MAJOR << '.' << VERSION_MINOR << '.' - << VERSION_RELEASE << '\n'; + << VERSION_RELEASE << (VERSION_DEV ? "-dev" : "") << '\n'; #ifdef GIT_SHA1 std::cout << " Git SHA1 | " << GIT_SHA1 << '\n'; #endif diff --git a/src/particle.cpp b/src/particle.cpp index 988130ec26..1fb1eadb09 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -379,6 +379,10 @@ Particle::transport() } } + #ifdef DAGMC + if (settings::dagmc) simulation::history.reset(); + #endif + // Finish particle track output. if (write_track_) { write_particle_track(*this); @@ -467,12 +471,14 @@ Particle::cross_surface() // If a reflective surface is coincident with a lattice or universe // boundary, it is necessary to redetermine the particle's coordinates in // the lower universes. - - n_coord_ = 1; - if (!find_cell(this, true)) { - this->mark_as_lost("Couldn't find particle after reflecting from surface " - + std::to_string(surf->id_) + "."); - return; + // (unless we're using a dagmc model, which has exactly one universe) + if (!settings::dagmc) { + n_coord_ = 1; + if (!find_cell(this, true)) { + this->mark_as_lost("Couldn't find particle after reflecting from surface " + + std::to_string(surf->id_) + "."); + return; + } } // Set previous coordinate going slightly past surface crossing diff --git a/src/plot.cpp b/src/plot.cpp index 57466eca1b..98a3645c76 100644 --- a/src/plot.cpp +++ b/src/plot.cpp @@ -19,6 +19,7 @@ #include "openmc/progress_bar.h" #include "openmc/random_lcg.h" #include "openmc/settings.h" +#include "openmc/simulation.h" #include "openmc/string_utils.h" namespace openmc { @@ -27,9 +28,10 @@ namespace openmc { // Constants //============================================================================== -const RGBColor WHITE {255, 255, 255}; + constexpr int PLOT_LEVEL_LOWEST {-1}; //!< lower bound on plot universe level constexpr int32_t NOT_FOUND {-2}; +constexpr int32_t OVERLAP {-3}; IdData::IdData(size_t h_res, size_t v_res) : data_({v_res, h_res, 2}, NOT_FOUND) @@ -48,6 +50,10 @@ IdData::set_value(size_t y, size_t x, const Particle& p, int level) { } } +void IdData::set_overlap(size_t y, size_t x) { + xt::view(data_, y, x, xt::all()) = OVERLAP; +} + PropertyData::PropertyData(size_t h_res, size_t v_res) : data_({v_res, h_res, 2}, NOT_FOUND) { } @@ -62,6 +68,10 @@ PropertyData::set_value(size_t y, size_t x, const Particle& p, int level) { } } +void PropertyData::set_overlap(size_t y, size_t x) { + data_(y, x) = OVERLAP; +} + //============================================================================== // Global variables //============================================================================== @@ -142,6 +152,10 @@ void create_ppm(Plot pl) auto id = ids.data_(y, x, pl.color_by_); // no setting needed if not found if (id == NOT_FOUND) { continue; } + if (id == OVERLAP) { + data(x,y) = pl.overlap_color_; + continue; + } if (PlotColorBy::cells == pl.color_by_) { data(x,y) = pl.colors_[model::cell_map[id]]; } else if (PlotColorBy::mats == pl.color_by_) { @@ -275,9 +289,6 @@ Plot::set_bg_color(pugi::xml_node plot_node) << id_; fatal_error(err_msg); } - } else { - // default to a white background - not_found_ = WHITE; } } @@ -387,6 +398,10 @@ Plot::set_default_colors(pugi::xml_node plot_node) for (auto& c : colors_) { c = random_color(); + // make sure we don't interfere with some default colors + while (c == RED || c == WHITE) { + c = random_color(); + } } } @@ -497,20 +512,38 @@ Plot::set_meshlines(pugi::xml_node plot_node) // Set mesh based on type if ("ufs" == meshtype) { - if (settings::index_ufs_mesh < 0) { + if (!simulation::ufs_mesh) { std::stringstream err_msg; err_msg << "No UFS mesh for meshlines on plot " << id_; fatal_error(err_msg); } else { - index_meshlines_mesh_ = settings::index_ufs_mesh; + for (int i = 0; i < model::meshes.size(); ++i) { + if (const auto* m + = dynamic_cast(model::meshes[i].get())) { + if (m == simulation::ufs_mesh) { + index_meshlines_mesh_ = i; + } + } + } + if (index_meshlines_mesh_ == -1) + fatal_error("Could not find the UFS mesh for meshlines plot"); } } else if ("entropy" == meshtype) { - if (settings::index_entropy_mesh < 0) { + if (!simulation::entropy_mesh) { std::stringstream err_msg; - err_msg <<"No entropy mesh for meshlines on plot " << id_; + err_msg << "No entropy mesh for meshlines on plot " << id_; fatal_error(err_msg); } else { - index_meshlines_mesh_ = settings::index_entropy_mesh; + for (int i = 0; i < model::meshes.size(); ++i) { + if (const auto* m + = dynamic_cast(model::meshes[i].get())) { + if (m == simulation::entropy_mesh) { + index_meshlines_mesh_ = i; + } + } + } + if (index_meshlines_mesh_ == -1) + fatal_error("Could not find the entropy mesh for meshlines plot"); } } else if ("tally" == meshtype) { // Ensure that there is a mesh id if the type is tally @@ -619,8 +652,39 @@ Plot::set_mask(pugi::xml_node plot_node) } } +void Plot::set_overlap_color(pugi::xml_node plot_node) { + color_overlaps_ = false; + if (check_for_node(plot_node, "show_overlaps")) { + color_overlaps_ = get_node_value_bool(plot_node, "show_overlaps"); + // check for custom overlap color + if (check_for_node(plot_node, "overlap_color")) { + if (!color_overlaps_) { + std::stringstream wrn_msg; + wrn_msg << "Overlap color specified in plot " << id_ + << " but overlaps won't be shown."; + warning(wrn_msg); + } + std::vector olap_clr = get_node_array(plot_node, "overlap_color"); + if (olap_clr.size() == 3) { + overlap_color_ = olap_clr; + } else { + std::stringstream err_msg; + err_msg << "Bad overlap RGB in plot " << id_; + fatal_error(err_msg); + } + } + } + + // make sure we allocate the vector for counting overlap checks if + // they're going to be plotted + if (color_overlaps_ && settings::run_mode == RUN_MODE_PLOTTING) { + settings::check_overlaps = true; + model::overlap_check_count.resize(model::cells.size(), 0); + } +} + Plot::Plot(pugi::xml_node plot_node) - : index_meshlines_mesh_{-1} + : index_meshlines_mesh_{-1}, overlap_color_{RED} { set_id(plot_node); set_type(plot_node); @@ -634,6 +698,7 @@ Plot::Plot(pugi::xml_node plot_node) set_user_colors(plot_node); set_meshlines(plot_node); set_mask(plot_node); + set_overlap_color(plot_node); } // End Plot constructor //============================================================================== @@ -679,19 +744,19 @@ void draw_mesh_lines(Plot pl, ImageData& data) RGBColor rgb; rgb = pl.meshlines_color_; - int outer, inner; + int ax1, ax2; switch(pl.basis_) { case PlotBasis::xy : - outer = 0; - inner = 1; + ax1 = 0; + ax2 = 1; break; case PlotBasis::xz : - outer = 0; - inner = 2; + ax1 = 0; + ax2 = 2; break; case PlotBasis::yz : - outer = 1; - inner = 2; + ax1 = 1; + ax2 = 2; break; default: UNREACHABLE(); @@ -700,70 +765,73 @@ void draw_mesh_lines(Plot pl, ImageData& data) Position ll_plot {pl.origin_}; Position ur_plot {pl.origin_}; - ll_plot[outer] -= pl.width_[0] / 2.; - ll_plot[inner] -= pl.width_[1] / 2.; - ur_plot[outer] += pl.width_[0] / 2.; - ur_plot[inner] += pl.width_[1] / 2.; + ll_plot[ax1] -= pl.width_[0] / 2.; + ll_plot[ax2] -= pl.width_[1] / 2.; + ur_plot[ax1] += pl.width_[0] / 2.; + ur_plot[ax2] += pl.width_[1] / 2.; Position width = ur_plot - ll_plot; - auto& m = model::meshes[pl.index_meshlines_mesh_]; + // Find the (axis-aligned) lines of the mesh that intersect this plot. + auto axis_lines = model::meshes[pl.index_meshlines_mesh_] + ->plot(ll_plot, ur_plot); - int ijk_ll[3], ijk_ur[3]; - bool in_mesh; - m->get_indices(ll_plot, &(ijk_ll[0]), &in_mesh); - m->get_indices(ur_plot, &(ijk_ur[0]), &in_mesh); + // Find the bounds along the second axis (accounting for low-D meshes). + int ax2_min, ax2_max; + if (axis_lines.second.size() > 0) { + double frac = (axis_lines.second.back() - ll_plot[ax2]) / width[ax2]; + ax2_min = (1.0 - frac) * pl.pixels_[1]; + if (ax2_min < 0) ax2_min = 0; + frac = (axis_lines.second.front() - ll_plot[ax2]) / width[ax2]; + ax2_max = (1.0 - frac) * pl.pixels_[1]; + if (ax2_max > pl.pixels_[1]) ax2_max = pl.pixels_[1]; + } else { + ax2_min = 0; + ax2_max = pl.pixels_[1]; + } - // Fortran/C++ index correction - ijk_ur[0]++; ijk_ur[1]++; ijk_ur[2]++; - - Position r_ll, r_ur; - // sweep through all meshbins on this plane and draw borders - for (int i = ijk_ll[outer]; i <= ijk_ur[outer]; i++) { - for (int j = ijk_ll[inner]; j <= ijk_ur[inner]; j++) { - // check if we're in the mesh for this ijk - if (i > 0 && i <= m->shape_[outer] && j >0 && j <= m->shape_[inner] ) { - int outrange[3], inrange[3]; - // get xyz's of lower left and upper right of this mesh cell - r_ll[outer] = m->lower_left_[outer] + m->width_[outer] * (i - 1); - r_ll[inner] = m->lower_left_[inner] + m->width_[inner] * (j - 1); - r_ur[outer] = m->lower_left_[outer] + m->width_[outer] * i; - r_ur[inner] = m->lower_left_[inner] + m->width_[inner] * j; - - // map the xyz ranges to pixel ranges - double frac = (r_ll[outer] - ll_plot[outer]) / width[outer]; - outrange[0] = int(frac * double(pl.pixels_[0])); - frac = (r_ur[outer] - ll_plot[outer]) / width[outer]; - outrange[1] = int(frac * double(pl.pixels_[0])); - - frac = (r_ur[inner] - ll_plot[inner]) / width[inner]; - inrange[0] = int((1. - frac) * (double)pl.pixels_[1]); - frac = (r_ll[inner] - ll_plot[inner]) / width[inner]; - inrange[1] = int((1. - frac) * (double)pl.pixels_[1]); - - // draw lines - for (int out_ = outrange[0]; out_ <= outrange[1]; out_++) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - data(out_, inrange[0] + plus) = rgb; - data(out_, inrange[1] + plus) = rgb; - data(out_, inrange[0] - plus) = rgb; - data(out_, inrange[1] - plus) = rgb; - } - } - - for (int in_ = inrange[0]; in_ <= inrange[1]; in_++) { - for (int plus = 0; plus <= pl.meshlines_width_; plus++) { - data(outrange[0] + plus, in_) = rgb; - data(outrange[1] + plus, in_) = rgb; - data(outrange[0] - plus, in_) = rgb; - data(outrange[1] - plus, in_) = rgb; - } - } - - } // end if(in mesh) + // Iterate across the first axis and draw lines. + for (auto ax1_val : axis_lines.first) { + double frac = (ax1_val - ll_plot[ax1]) / width[ax1]; + int ax1_ind = frac * pl.pixels_[0]; + for (int ax2_ind = ax2_min; ax2_ind < ax2_max; ++ax2_ind) { + for (int plus = 0; plus <= pl.meshlines_width_; plus++) { + if (ax1_ind+plus >= 0 && ax1_ind+plus < pl.pixels_[0]) + data(ax1_ind+plus, ax2_ind) = rgb; + if (ax1_ind-plus >= 0 && ax1_ind-plus < pl.pixels_[0]) + data(ax1_ind-plus, ax2_ind) = rgb; + } } - } // end outer loops -} // end draw_mesh_lines + } + + // Find the bounds along the first axis. + int ax1_min, ax1_max; + if (axis_lines.first.size() > 0) { + double frac = (axis_lines.first.front() - ll_plot[ax1]) / width[ax1]; + ax1_min = frac * pl.pixels_[0]; + if (ax1_min < 0) ax1_min = 0; + frac = (axis_lines.first.back() - ll_plot[ax1]) / width[ax1]; + ax1_max = frac * pl.pixels_[0]; + if (ax1_max > pl.pixels_[0]) ax1_max = pl.pixels_[0]; + } else { + ax1_min = 0; + ax1_max = pl.pixels_[0]; + } + + // Iterate across the second axis and draw lines. + for (auto ax2_val : axis_lines.second) { + double frac = (ax2_val - ll_plot[ax2]) / width[ax2]; + int ax2_ind = (1.0 - frac) * pl.pixels_[1]; + for (int ax1_ind = ax1_min; ax1_ind < ax1_max; ++ax1_ind) { + for (int plus = 0; plus <= pl.meshlines_width_; plus++) { + if (ax2_ind+plus >= 0 && ax2_ind+plus < pl.pixels_[1]) + data(ax1_ind, ax2_ind+plus) = rgb; + if (ax2_ind-plus >= 0 && ax2_ind-plus < pl.pixels_[1]) + data(ax1_ind, ax2_ind-plus) = rgb; + } + } + } +} //============================================================================== // CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D @@ -827,6 +895,7 @@ void create_voxel(Plot pl) pltbase.basis_ = PlotBasis::xy; pltbase.pixels_ = pl.pixels_; pltbase.level_ = -1; // all universes for voxel files + pltbase.color_overlaps_ = pl.color_overlaps_; ProgressBar pb; for (int z = 0; z < pl.pixels_[2]; z++) { @@ -900,6 +969,10 @@ extern "C" int openmc_id_map(const void* plot, int32_t* data_out) return OPENMC_E_INVALID_ARGUMENT; } + if (plt->color_overlaps_ && model::overlap_check_count.size() == 0) { + model::overlap_check_count.resize(model::cells.size()); + } + auto ids = plt->get_map(); // write id data to array @@ -916,6 +989,10 @@ extern "C" int openmc_property_map(const void* plot, double* data_out) { return OPENMC_E_INVALID_ARGUMENT; } + if (plt->color_overlaps_ && model::overlap_check_count.size() == 0) { + model::overlap_check_count.resize(model::cells.size()); + } + auto props = plt->get_map(); // write id data to array diff --git a/src/relaxng/geometry.rnc b/src/relaxng/geometry.rnc index c9c53f6d64..e3c88c445c 100644 --- a/src/relaxng/geometry.rnc +++ b/src/relaxng/geometry.rnc @@ -48,6 +48,7 @@ element geometry { (element n_axial { xsd:int } | attribute n_axial { xsd:int })? & (element center { list { xsd:double+ } } | attribute center { list { xsd:double+ } }) & (element pitch { list { xsd:double+ } } | attribute pitch { list { xsd:double+ } }) & + (element orientation { ( "x" | "y" ) } | attribute orientation { ( "x" | "y" ) })? & (element universes { list { xsd:int+ } } | attribute universes { list { xsd:int+ } }) & (element outer { xsd:int } | attribute outer { xsd:int })? }* diff --git a/src/relaxng/geometry.rng b/src/relaxng/geometry.rng index d2c1f635ee..56bf385807 100644 --- a/src/relaxng/geometry.rng +++ b/src/relaxng/geometry.rng @@ -398,6 +398,22 @@ + + + + + x + y + + + + + x + y + + + + diff --git a/src/relaxng/tallies.rnc b/src/relaxng/tallies.rnc index 204284c480..ec511481f6 100644 --- a/src/relaxng/tallies.rnc +++ b/src/relaxng/tallies.rnc @@ -1,17 +1,30 @@ element tallies { element mesh { (element id { xsd:int } | attribute id { xsd:int }) & - (element type { ( "regular" ) } | - attribute type { ( "regular" ) }) & - (element dimension { list { xsd:positiveInteger+ } } | - attribute dimension { list { xsd:positiveInteger+ } }) & - (element lower_left { list { xsd:double+ } } | - attribute lower_left { list { xsd:double+ } }) & ( - (element upper_right { list { xsd:double+ } } | - attribute upper_right { list { xsd:double+ } }) | - (element width { list { xsd:double+ } } | - attribute width { list { xsd:double+ } }) + ( + (element type { ( "regular" ) } | + attribute type { ( "regular" ) }) & + (element dimension { list { xsd:positiveInteger+ } } | + attribute dimension { list { xsd:positiveInteger+ } }) & + (element lower_left { list { xsd:double+ } } | + attribute lower_left { list { xsd:double+ } }) & + ( + (element upper_right { list { xsd:double+ } } | + attribute upper_right { list { xsd:double+ } }) | + (element width { list { xsd:double+ } } | + attribute width { list { xsd:double+ } }) + ) + ) | ( + (element type { ( "rectilinear" ) } | + attribute type { ( "rectilinear" ) }) & + (element x_grid { list { xsd:double+ } } | + attribute x_grid { list { xsd:double+ } }) & + (element y_grid { list { xsd:double+ } } | + attribute y_grid { list { xsd:double+ } }) & + (element z_grid { list { xsd:double+ } } | + attribute z_grid { list { xsd:double+ } }) + ) ) }* & diff --git a/src/relaxng/tallies.rng b/src/relaxng/tallies.rng index 15b6f5b249..98a48eeb85 100644 --- a/src/relaxng/tallies.rng +++ b/src/relaxng/tallies.rng @@ -13,78 +13,140 @@ - - regular - - - regular - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + regular + + + regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rectilinear + + + rectilinear + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings.cpp b/src/settings.cpp index 4c041a080d..1c9d1161a0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -40,6 +40,7 @@ namespace settings { // Default values for boolean flags bool assume_separate {false}; bool check_overlaps {false}; +bool cmfd_run {false}; bool confidence_intervals {false}; bool create_fission_neutrons {true}; bool dagmc {false}; @@ -73,9 +74,6 @@ std::string path_source; std::string path_sourcepoint; std::string path_statepoint; -int32_t index_entropy_mesh {-1}; -int32_t index_ufs_mesh {-1}; - int32_t n_batches; int32_t n_inactive {0}; int32_t gen_per_batch {1}; @@ -400,7 +398,7 @@ void read_settings_xml() SourceDistribution source { UPtrSpace{new SpatialPoint({0.0, 0.0, 0.0})}, UPtrAngle{new Isotropic()}, - UPtrDist{new Watt(0.988, 2.249e-6)} + UPtrDist{new Watt(0.988e6, 2.249e-6)} }; model::external_sources.push_back(std::move(source)); } @@ -481,6 +479,7 @@ void read_settings_xml() read_meshes(root); // Shannon Entropy mesh + int32_t index_entropy_mesh = -1; if (check_for_node(root, "entropy_mesh")) { int temp = std::stoi(get_node_value(root, "entropy_mesh")); if (model::mesh_map.find(temp) == model::mesh_map.end()) { @@ -508,17 +507,21 @@ void read_settings_xml() } if (index_entropy_mesh >= 0) { - auto& m = *model::meshes[index_entropy_mesh]; - if (m.shape_.dimension() == 0) { + auto* m = dynamic_cast( + model::meshes[index_entropy_mesh].get()); + if (!m) fatal_error("Only regular meshes can be used as an entropy mesh"); + simulation::entropy_mesh = m; + + if (m->shape_.dimension() == 0) { // If the user did not specify how many mesh cells are to be used in // each direction, we automatically determine an appropriate number of // cells int n = std::ceil(std::pow(n_particles / 20.0, 1.0/3.0)); - m.shape_ = {n, n, n}; - m.n_dimension_ = 3; + m->shape_ = {n, n, n}; + m->n_dimension_ = 3; // Calculate width - m.width_ = (m.upper_right_ - m.lower_left_) / m.shape_; + m->width_ = (m->upper_right_ - m->lower_left_) / m->shape_; } // Turn on Shannon entropy calculation @@ -526,6 +529,7 @@ void read_settings_xml() } // Uniform fission source weighting mesh + int32_t i_ufs_mesh = -1; if (check_for_node(root, "ufs_mesh")) { auto temp = std::stoi(get_node_value(root, "ufs_mesh")); if (model::mesh_map.find(temp) == model::mesh_map.end()) { @@ -534,7 +538,7 @@ void read_settings_xml() "does not exist."; fatal_error(msg); } - index_ufs_mesh = model::mesh_map.at(temp); + i_ufs_mesh = model::mesh_map.at(temp); } else if (check_for_node(root, "uniform_fs")) { warning("Specifying a UFS mesh via the element " @@ -546,14 +550,18 @@ void read_settings_xml() model::meshes.push_back(std::make_unique(node_ufs)); // Set entropy mesh index - index_ufs_mesh = model::meshes.size() - 1; + i_ufs_mesh = model::meshes.size() - 1; // Assign ID and set mapping model::meshes.back()->id_ = 10001; model::mesh_map[10001] = index_entropy_mesh; } - if (index_ufs_mesh >= 0) { + if (i_ufs_mesh >= 0) { + auto* m = dynamic_cast(model::meshes[i_ufs_mesh].get()); + if (!m) fatal_error("Only regular meshes can be used as a UFS mesh"); + simulation::ufs_mesh = m; + // Turn on uniform fission source weighting ufs_on = true; } diff --git a/src/simulation.cpp b/src/simulation.cpp index fc7a788517..8de1e7fbcb 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -221,6 +221,16 @@ int openmc_next_batch(int* status) return 0; } +bool openmc_is_statepoint_batch() { + using namespace openmc; + using openmc::simulation::current_gen; + + if (!simulation::initialized) + return false; + else + return contains(settings::statepoint_batch, simulation::current_batch); +} + namespace openmc { //============================================================================== @@ -247,6 +257,9 @@ int total_gen {0}; double total_weight; int64_t work_per_rank; +const RegularMesh* entropy_mesh {nullptr}; +const RegularMesh* ufs_mesh {nullptr}; + std::vector k_generation; std::vector work_index; @@ -354,8 +367,10 @@ void finalize_batch() settings::statepoint_batch.insert(simulation::current_batch); } - // Write out state point if it's been specified for this batch - if (contains(settings::statepoint_batch, simulation::current_batch)) { + // Write out state point if it's been specified for this batch and is not + // a CMFD run instance + if (contains(settings::statepoint_batch, simulation::current_batch) + && !settings::cmfd_run) { if (contains(settings::sourcepoint_batch, simulation::current_batch) && settings::source_write && !settings::source_separate) { bool b = true; diff --git a/src/state_point.cpp b/src/state_point.cpp index 54f1cb38c5..7516b0d485 100644 --- a/src/state_point.cpp +++ b/src/state_point.cpp @@ -29,9 +29,6 @@ namespace openmc { -extern "C" void statepoint_write_f(hid_t file_id); -extern "C" void load_state_point_f(hid_t file_id); - extern "C" int openmc_statepoint_write(const char* filename, bool* write_source) { diff --git a/src/surface.cpp b/src/surface.cpp index f30b444310..f364694a02 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -6,6 +6,7 @@ #include #include "openmc/error.h" +#include "openmc/dagmc.h" #include "openmc/hdf5_interface.h" #include "openmc/settings.h" #include "openmc/string_utils.h" @@ -255,13 +256,19 @@ DAGSurface::distance(Position r, Direction u, bool coincident) const Direction DAGSurface::normal(Position r) const { moab::ErrorCode rval; - Direction u; moab::EntityHandle surf = dagmc_ptr_->entity_by_index(2, dag_index_); double pnt[3] = {r.x, r.y, r.z}; - double dir[3] = {u.x, u.y, u.z}; + double dir[3]; rval = dagmc_ptr_->get_angle(surf, pnt, dir); MB_CHK_ERR_CONT(rval); - return u; + return dir; +} + +Direction DAGSurface::reflect(Position r, Direction u) const +{ + simulation::history.reset_to_last_intersection(); + simulation::last_dir = Surface::reflect(r, u); + return simulation::last_dir; } BoundingBox DAGSurface::bounding_box() const @@ -297,7 +304,7 @@ template double axis_aligned_plane_distance(Position r, Direction u, bool coincident, double offset) { const double f = offset - r[i]; - if (coincident or std::abs(f) < FP_COINCIDENT or u[i] == 0.0) return INFTY; + if (coincident || std::abs(f) < FP_COINCIDENT || u[i] == 0.0) return INFTY; const double d = f / u[i]; if (d < 0.0) return INFTY; return d; @@ -493,7 +500,7 @@ SurfacePlane::distance(Position r, Direction u, bool coincident) const { const double f = A_*r.x + B_*r.y + C_*r.z - D_; const double projection = A_*u.x + B_*u.y + C_*u.z; - if (coincident or std::abs(f) < FP_COINCIDENT or projection == 0.0) { + if (coincident || std::abs(f) < FP_COINCIDENT || projection == 0.0) { return INFTY; } else { const double d = -f / projection; @@ -573,7 +580,7 @@ axis_aligned_cylinder_distance(Position r, Direction u, // No intersection with cylinder. return INFTY; - } else if (coincident or std::abs(c) < FP_COINCIDENT) { + } else if (coincident || std::abs(c) < FP_COINCIDENT) { // Particle is on the cylinder, thus one distance is positive/negative // and the other is zero. The sign of k determines if we are facing in or // out. @@ -743,7 +750,7 @@ double SurfaceSphere::distance(Position r, Direction u, bool coincident) const // No intersection with sphere. return INFTY; - } else if (coincident or std::abs(c) < FP_COINCIDENT) { + } else if (coincident || std::abs(c) < FP_COINCIDENT) { // Particle is on the sphere, thus one distance is positive/negative and // the other is zero. The sign of k determines if we are facing in or out. if (k >= 0.0) { @@ -820,7 +827,7 @@ axis_aligned_cone_distance(Position r, Direction u, // No intersection with cone. return INFTY; - } else if (coincident or std::abs(c) < FP_COINCIDENT) { + } else if (coincident || std::abs(c) < FP_COINCIDENT) { // Particle is on the cone, thus one distance is positive/negative // and the other is zero. The sign of k determines if we are facing in or // out. @@ -1008,7 +1015,7 @@ SurfaceQuadric::distance(Position r, Direction ang, bool coincident) const // No intersection with surface. return INFTY; - } else if (coincident or std::abs(c) < FP_COINCIDENT) { + } else if (coincident || std::abs(c) < FP_COINCIDENT) { // Particle is on the surface, thus one distance is positive/negative and // the other is zero. The sign of k determines which distance is zero and // which is not. @@ -1155,27 +1162,27 @@ void read_surfaces(pugi::xml_node node) // See if this surface makes part of the global bounding box. BoundingBox bb = surf->bounding_box(); - if (bb.xmin > -INFTY and bb.xmin < xmin) { + if (bb.xmin > -INFTY && bb.xmin < xmin) { xmin = bb.xmin; i_xmin = i_surf; } - if (bb.xmax < INFTY and bb.xmax > xmax) { + if (bb.xmax < INFTY && bb.xmax > xmax) { xmax = bb.xmax; i_xmax = i_surf; } - if (bb.ymin > -INFTY and bb.ymin < ymin) { + if (bb.ymin > -INFTY && bb.ymin < ymin) { ymin = bb.ymin; i_ymin = i_surf; } - if (bb.ymax < INFTY and bb.ymax > ymax) { + if (bb.ymax < INFTY && bb.ymax > ymax) { ymax = bb.ymax; i_ymax = i_surf; } - if (bb.zmin > -INFTY and bb.zmin < zmin) { + if (bb.zmin > -INFTY && bb.zmin < zmin) { zmin = bb.zmin; i_zmin = i_surf; } - if (bb.zmax < INFTY and bb.zmax > zmax) { + if (bb.zmax < INFTY && bb.zmax > zmax) { zmax = bb.zmax; i_zmax = i_surf; } diff --git a/src/tallies/filter_legendre.cpp b/src/tallies/filter_legendre.cpp index 1d1d440b28..6fa041c76b 100644 --- a/src/tallies/filter_legendre.cpp +++ b/src/tallies/filter_legendre.cpp @@ -18,8 +18,8 @@ void LegendreFilter::get_all_bins(const Particle* p, int estimator, FilterMatch& match) const { - double wgt[n_bins_]; - calc_pn_c(order_, p->mu_, wgt); + std::vector wgt(n_bins_); + calc_pn_c(order_, p->mu_, wgt.data()); for (int i = 0; i < n_bins_; i++) { match.bins_.push_back(i); match.weights_.push_back(wgt[i]); diff --git a/src/tallies/filter_mesh.cpp b/src/tallies/filter_mesh.cpp index f69a966523..e6fdad31e2 100644 --- a/src/tallies/filter_mesh.cpp +++ b/src/tallies/filter_mesh.cpp @@ -58,8 +58,8 @@ MeshFilter::text_label(int bin) const auto& mesh = *model::meshes[mesh_]; int n_dim = mesh.n_dimension_; - int ijk[n_dim]; - mesh.get_indices_from_bin(bin, ijk); + std::vector ijk(n_dim); + mesh.get_indices_from_bin(bin, ijk.data()); std::stringstream out; out << "Mesh Index (" << ijk[0]; @@ -74,8 +74,7 @@ void MeshFilter::set_mesh(int32_t mesh) { mesh_ = mesh; - n_bins_ = 1; - for (auto dim : model::meshes[mesh_]->shape_) n_bins_ *= dim; + n_bins_ = model::meshes[mesh_]->n_bins(); } //============================================================================== diff --git a/src/tallies/filter_meshsurface.cpp b/src/tallies/filter_meshsurface.cpp index 57f10b09c3..edb0492e9f 100644 --- a/src/tallies/filter_meshsurface.cpp +++ b/src/tallies/filter_meshsurface.cpp @@ -75,8 +75,7 @@ void MeshSurfaceFilter::set_mesh(int32_t mesh) { mesh_ = mesh; - n_bins_ = 4 * model::meshes[mesh_]->n_dimension_; - for (auto dim : model::meshes[mesh_]->shape_) n_bins_ *= dim; + n_bins_ = model::meshes[mesh_]->n_surface_bins(); } //============================================================================== diff --git a/src/tallies/filter_sph_harm.cpp b/src/tallies/filter_sph_harm.cpp index 1e97e64c1b..0b3a371187 100644 --- a/src/tallies/filter_sph_harm.cpp +++ b/src/tallies/filter_sph_harm.cpp @@ -35,16 +35,16 @@ SphericalHarmonicsFilter::get_all_bins(const Particle* p, int estimator, FilterMatch& match) const { // Determine cosine term for scatter expansion if necessary - double wgt[order_ + 1]; + std::vector wgt(order_ + 1); if (cosine_ == SphericalHarmonicsCosine::scatter) { - calc_pn_c(order_, p->mu_, wgt); + calc_pn_c(order_, p->mu_, wgt.data()); } else { for (int i = 0; i < order_ + 1; i++) wgt[i] = 1; } // Find the Rn,m values - double rn[n_bins_]; - calc_rn(order_, p->u_last_, rn); + std::vector rn(n_bins_); + calc_rn(order_, p->u_last_, rn.data()); int j = 0; for (int n = 0; n < order_ + 1; n++) { diff --git a/src/tallies/filter_sptl_legendre.cpp b/src/tallies/filter_sptl_legendre.cpp index 616dbbbd4d..6562ff01c7 100644 --- a/src/tallies/filter_sptl_legendre.cpp +++ b/src/tallies/filter_sptl_legendre.cpp @@ -50,8 +50,8 @@ SpatialLegendreFilter::get_all_bins(const Particle* p, int estimator, double x_norm = 2.0*(x - min_) / (max_ - min_) - 1.0; // Compute and return the Legendre weights. - double wgt[order_ + 1]; - calc_pn_c(order_, x_norm, wgt); + std::vector wgt(order_ + 1); + calc_pn_c(order_, x_norm, wgt.data()); for (int i = 0; i < order_ + 1; i++) { match.bins_.push_back(i); match.weights_.push_back(wgt[i]); diff --git a/src/tallies/filter_zernike.cpp b/src/tallies/filter_zernike.cpp index 9f66a68f98..125bfada89 100644 --- a/src/tallies/filter_zernike.cpp +++ b/src/tallies/filter_zernike.cpp @@ -36,8 +36,8 @@ ZernikeFilter::get_all_bins(const Particle* p, int estimator, if (r <= 1.0) { // Compute and return the Zernike weights. - double zn[n_bins_]; - calc_zn(order_, r, theta, zn); + std::vector zn(n_bins_); + calc_zn(order_, r, theta, zn.data()); for (int i = 0; i < n_bins_; i++) { match.bins_.push_back(i); match.weights_.push_back(zn[i]); @@ -93,8 +93,8 @@ ZernikeRadialFilter::get_all_bins(const Particle* p, int estimator, if (r <= 1.0) { // Compute and return the Zernike weights. - double zn[n_bins_]; - calc_zn_rad(order_, r, zn); + std::vector zn(n_bins_); + calc_zn_rad(order_, r, zn.data()); for (int i = 0; i < n_bins_; i++) { match.bins_.push_back(i); match.weights_.push_back(zn[i]); diff --git a/src/thermal.cpp b/src/thermal.cpp index 3c2d9ba432..40bb19e894 100644 --- a/src/thermal.cpp +++ b/src/thermal.cpp @@ -199,10 +199,10 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, // Calculate S(a,b) inelastic scattering cross section auto& xs = sab.inelastic_sigma_; - *inelastic = (1.0 - f) * xs[i_grid] + f * xs[i_grid + 1]; + *inelastic = xs[i_grid] + f * (xs[i_grid + 1] - xs[i_grid]); // Check for elastic data - if (E < sab.threshold_elastic_) { + if (!sab.elastic_e_in_.empty()) { // Determine whether elastic scattering is given in the coherent or // incoherent approximation. For coherent, the cross section is // represented as P/E whereas for incoherent, it is simply P @@ -231,7 +231,7 @@ ThermalScattering::calculate_xs(double E, double sqrtkT, int* i_temp, // Calculate S(a,b) elastic scattering cross section auto& xs = sab.elastic_P_; - *elastic = (1.0 - f) * xs[i_grid] + f * xs[i_grid + 1]; + *elastic = xs[i_grid] + f*(xs[i_grid + 1] - xs[i_grid]); } } else { // No elastic data diff --git a/tests/regression_tests/asymmetric_lattice/results_true.dat b/tests/regression_tests/asymmetric_lattice/results_true.dat index ecc30a056f..43f59aa5d2 100644 --- a/tests/regression_tests/asymmetric_lattice/results_true.dat +++ b/tests/regression_tests/asymmetric_lattice/results_true.dat @@ -1 +1 @@ -4b75e203d06d0fc1b4c4dfcb8c180d6f3df8fa2bc44e9775b59bbfd8f7a3785f956f9a9f301526f69c0f8a963ce2e553e0c62c7f6c696656ce1d47b415af6076 \ No newline at end of file +4401f503237c94e9d9cfc9f60e0269d5ae5bb67be3225e18c5510ed08616482964e2962a06268751f66a455fac3ddd5faf91555638dfb56fcd09eee60219edff \ No newline at end of file diff --git a/tests/regression_tests/cmfd_feed/results_true.dat b/tests/regression_tests/cmfd_feed/results_true.dat index 85498af19c..4a2c6eea25 100644 --- a/tests/regression_tests/cmfd_feed/results_true.dat +++ b/tests/regression_tests/cmfd_feed/results_true.dat @@ -378,7 +378,7 @@ tally 5: 5.253064E+00 1.396224E+00 2.996497E+01 -4.508840E+01 +4.508839E+01 3.818076E+00 7.509442E-01 1.574994E+01 @@ -461,11 +461,11 @@ cmfd dominance ratio cmfd openmc source comparison 6.959834E-03 5.655657E-03 -3.886185E-03 +3.886186E-03 4.035116E-03 3.043277E-03 -5.455475E-03 -4.515311E-03 +5.455474E-03 +4.515310E-03 2.439840E-03 2.114032E-03 2.673132E-03 diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 04ac442e0e..906c631ecc 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -15,15 +15,16 @@ def test_cmfd_physical_adjoint(): """ # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -10.0, -1.0, -1.0 - cmfd_mesh.upper_right = 10.0, 1.0, 1.0 - cmfd_mesh.dimension = 10, 1, 1 - cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 cmfd_run.feedback = True cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run_adjoint = True @@ -44,15 +45,16 @@ def test_cmfd_math_adjoint(): """ # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -10.0, -1.0, -1.0 - cmfd_mesh.upper_right = 10.0, 1.0, 1.0 - cmfd_mesh.dimension = 10, 1, 1 - cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 cmfd_run.feedback = True cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] cmfd_run.run_adjoint = True @@ -72,15 +74,16 @@ def test_cmfd_write_matrices(): """ # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -10.0, -1.0, -1.0 - cmfd_mesh.upper_right = 10.0, 1.0, 1.0 - cmfd_mesh.dimension = 10, 1, 1 - cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 cmfd_run.display = {'dominance': True} cmfd_run.feedback = True cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] @@ -119,15 +122,16 @@ def test_cmfd_feed(): """Test 1 group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -10.0, -1.0, -1.0 - cmfd_mesh.upper_right = 10.0, 1.0, 1.0 - cmfd_mesh.dimension = 10, 1, 1 - cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 cmfd_run.display = {'dominance': True} cmfd_run.feedback = True cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] diff --git a/tests/regression_tests/cmfd_feed_2g/results_true.dat b/tests/regression_tests/cmfd_feed_2g/results_true.dat index 8161b26457..f16d82309f 100644 --- a/tests/regression_tests/cmfd_feed_2g/results_true.dat +++ b/tests/regression_tests/cmfd_feed_2g/results_true.dat @@ -1,5 +1,5 @@ k-combined: -1.038883E+00 1.017026E-02 +1.038883E+00 1.017030E-02 tally 1: 1.167304E+02 1.362680E+03 @@ -49,7 +49,7 @@ tally 3: 0.000000E+00 2.033842E-02 4.375294E-05 -4.296338E+00 +4.296339E+00 9.280716E-01 3.493776E+00 6.157094E-01 @@ -74,15 +74,15 @@ tally 3: 9.717439E+01 4.724254E+02 8.435691E-01 -3.666151E-02 +3.666152E-02 6.192881E+01 1.924016E+02 0.000000E+00 0.000000E+00 1.796382E-02 -3.190854E-05 +3.190855E-05 4.343238E+00 -9.514039E-01 +9.514040E-01 3.504683E+00 6.157615E-01 0.000000E+00 @@ -90,7 +90,7 @@ tally 3: 9.818871E+01 4.822799E+02 8.401346E-01 -3.664037E-02 +3.664038E-02 6.130607E+01 1.882837E+02 0.000000E+00 @@ -106,7 +106,7 @@ tally 3: 9.885292E+01 4.888720E+02 9.509199E-01 -4.670951E-02 +4.670952E-02 tally 4: 0.000000E+00 0.000000E+00 @@ -377,11 +377,11 @@ cmfd balance 4.16856E-04 6.38469E-04 3.92822E-04 -3.78983E-04 +3.78984E-04 2.68486E-04 4.84991E-04 1.08402E-03 -1.09178E-03 +1.09177E-03 5.45977E-04 4.45554E-04 4.01147E-04 @@ -408,21 +408,21 @@ cmfd dominance ratio 5.996E-03 cmfd openmc source comparison 1.931386E-05 -2.839162E-05 -1.407962E-05 -6.718148E-06 -9.164193E-06 -1.382539E-05 +2.839161E-05 +1.407963E-05 +6.718156E-06 +9.164199E-06 +1.382540E-05 2.871606E-05 4.192300E-05 -5.327516E-05 +5.327515E-05 6.566500E-05 -6.655541E-05 +6.655540E-05 6.034977E-05 6.004677E-05 -5.711623E-05 -6.271264E-05 -6.425363E-05 +5.711622E-05 +6.271263E-05 +6.425362E-05 cmfd source 2.510278E-01 2.480592E-01 diff --git a/tests/regression_tests/cmfd_feed_2g/test.py b/tests/regression_tests/cmfd_feed_2g/test.py index 22cc7e1602..ba4d21609a 100644 --- a/tests/regression_tests/cmfd_feed_2g/test.py +++ b/tests/regression_tests/cmfd_feed_2g/test.py @@ -7,16 +7,17 @@ def test_cmfd_feed_2g(): """Test 2 group CMFD solver results with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 - cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 - cmfd_mesh.dimension = 2, 2, 1 - cmfd_mesh.energy = [0.0, 0.625, 20000000] - cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-1.25984, -1.25984, -1.0) + cmfd_mesh.upper_right = (1.25984, 1.25984, 1.0) + cmfd_mesh.dimension = (2, 2, 1) + cmfd_mesh.energy = (0.0, 0.625, 20000000) + cmfd_mesh.albedo = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 cmfd_run.display = {'dominance': True} cmfd_run.feedback = True cmfd_run.downscatter = True diff --git a/tests/regression_tests/uwuw/__init__.py b/tests/regression_tests/cmfd_feed_expanding_window/__init__.py similarity index 100% rename from tests/regression_tests/uwuw/__init__.py rename to tests/regression_tests/cmfd_feed_expanding_window/__init__.py diff --git a/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml b/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/regression_tests/cmfd_feed_expanding_window/materials.xml b/tests/regression_tests/cmfd_feed_expanding_window/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat b/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat new file mode 100644 index 0000000000..b67bef0f76 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/results_true.dat @@ -0,0 +1,488 @@ +k-combined: +1.165403E+00 1.129918E-02 +tally 1: +1.141961E+01 +1.307497E+01 +2.075941E+01 +4.316095E+01 +2.834539E+01 +8.063510E+01 +3.513869E+01 +1.238515E+02 +3.699858E+01 +1.372516E+02 +3.612668E+01 +1.310763E+02 +3.373368E+01 +1.140458E+02 +2.836844E+01 +8.075759E+01 +2.158837E+01 +4.674848E+01 +1.141868E+01 +1.310648E+01 +tally 2: +1.126626E+00 +1.269287E+00 +7.749042E-01 +6.004765E-01 +1.991608E+00 +3.966503E+00 +1.411775E+00 +1.993108E+00 +2.978264E+00 +8.870057E+00 +2.130066E+00 +4.537180E+00 +3.708857E+00 +1.375562E+01 +2.698010E+00 +7.279259E+00 +4.233192E+00 +1.791991E+01 +3.023990E+00 +9.144518E+00 +4.062113E+00 +1.650076E+01 +2.910472E+00 +8.470848E+00 +3.506681E+00 +1.229681E+01 +2.497751E+00 +6.238760E+00 +2.974675E+00 +8.848690E+00 +2.121163E+00 +4.499332E+00 +2.329248E+00 +5.425398E+00 +1.658233E+00 +2.749736E+00 +1.158552E+00 +1.342242E+00 +8.282698E-01 +6.860309E-01 +tally 3: +7.459301E-01 +5.564117E-01 +4.877614E-02 +2.379112E-03 +1.356702E+00 +1.840641E+00 +8.361624E-02 +6.991675E-03 +2.047332E+00 +4.191570E+00 +1.521351E-01 +2.314509E-02 +2.610287E+00 +6.813596E+00 +1.718778E-01 +2.954199E-02 +2.911379E+00 +8.476127E+00 +1.823299E-01 +3.324418E-02 +2.806920E+00 +7.878801E+00 +2.090406E-01 +4.369797E-02 +2.415727E+00 +5.835736E+00 +1.486511E-01 +2.209715E-02 +2.051064E+00 +4.206862E+00 +1.196177E-01 +1.430839E-02 +1.593746E+00 +2.540026E+00 +1.126497E-01 +1.268994E-02 +7.994935E-01 +6.391898E-01 +5.806683E-02 +3.371757E-03 +tally 4: +1.341719E-01 +1.800210E-02 +0.000000E+00 +0.000000E+00 +1.420707E-01 +2.018410E-02 +2.633150E-01 +6.933479E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.633150E-01 +6.933479E-02 +1.420707E-01 +2.018410E-02 +2.628613E-01 +6.909608E-02 +3.590382E-01 +1.289084E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.590382E-01 +1.289084E-01 +2.628613E-01 +6.909608E-02 +3.851599E-01 +1.483482E-01 +4.595809E-01 +2.112146E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.595809E-01 +2.112146E-01 +3.851599E-01 +1.483482E-01 +4.679967E-01 +2.190209E-01 +4.970187E-01 +2.470276E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.970187E-01 +2.470276E-01 +4.679967E-01 +2.190209E-01 +4.909661E-01 +2.410477E-01 +4.838785E-01 +2.341384E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.838785E-01 +2.341384E-01 +4.909661E-01 +2.410477E-01 +5.152690E-01 +2.655021E-01 +4.788649E-01 +2.293116E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.788649E-01 +2.293116E-01 +5.152690E-01 +2.655021E-01 +4.266876E-01 +1.820623E-01 +3.401342E-01 +1.156913E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.401342E-01 +1.156913E-01 +4.266876E-01 +1.820623E-01 +3.724186E-01 +1.386956E-01 +2.560013E-01 +6.553669E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.560013E-01 +6.553669E-02 +3.724186E-01 +1.386956E-01 +2.892134E-01 +8.364439E-02 +1.556176E-01 +2.421685E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.556176E-01 +2.421685E-02 +2.892134E-01 +8.364439E-02 +1.497744E-01 +2.243237E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +7.459301E-01 +5.564117E-01 +1.149820E-01 +1.322085E-02 +1.356702E+00 +1.840641E+00 +1.801102E-01 +3.243968E-02 +2.047332E+00 +4.191570E+00 +2.627497E-01 +6.903740E-02 +2.609217E+00 +6.808012E+00 +3.360314E-01 +1.129171E-01 +2.909457E+00 +8.464941E+00 +4.095451E-01 +1.677272E-01 +2.805131E+00 +7.868760E+00 +3.957627E-01 +1.566281E-01 +2.415727E+00 +5.835736E+00 +3.196414E-01 +1.021706E-01 +2.049859E+00 +4.201923E+00 +3.162399E-01 +1.000077E-01 +1.593746E+00 +2.540026E+00 +2.169985E-01 +4.708836E-02 +7.994935E-01 +6.391898E-01 +1.002481E-01 +1.004969E-02 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +1.143597E+00 +1.163387E+00 +1.173384E+00 +1.171035E+00 +1.147196E+00 +1.122260E+00 +1.106380E+00 +1.124693E+00 +1.133192E+00 +1.134435E+00 +1.142380E+00 +1.132168E+00 +1.132560E+00 +1.153781E+00 +1.170308E+00 +1.184540E+00 +cmfd entropy +3.212002E+00 +3.206393E+00 +3.223984E+00 +3.222764E+00 +3.232123E+00 +3.242083E+00 +3.246067E+00 +3.238869E+00 +3.235585E+00 +3.234611E+00 +3.233575E+00 +3.236922E+00 +3.229545E+00 +3.225232E+00 +3.221485E+00 +3.219108E+00 +cmfd balance +8.26180E-03 +4.27338E-03 +2.22686E-03 +1.93026E-03 +1.96979E-03 +2.13756E-03 +2.01479E-03 +1.74519E-03 +2.09248E-03 +1.25545E-03 +1.48370E-03 +1.75963E-03 +1.98194E-03 +1.87306E-03 +1.24780E-03 +1.15560E-03 +cmfd dominance ratio +5.404E-01 +5.406E-01 +5.449E-01 +5.473E-01 +5.534E-01 +5.623E-01 +5.738E-01 +5.611E-01 +5.569E-01 +5.556E-01 +5.555E-01 +5.583E-01 +5.544E-01 +5.486E-01 +5.412E-01 +5.383E-01 +cmfd openmc source comparison +1.575499E-02 +1.293688E-02 +3.531746E-03 +8.281178E-03 +5.771681E-03 +7.459013E-03 +5.012869E-03 +1.770224E-03 +5.242540E-03 +3.888027E-03 +6.653433E-03 +8.839928E-03 +5.456904E-03 +5.668412E-03 +4.016377E-03 +4.179381E-03 +cmfd source +4.116210E-02 +7.797480E-02 +1.062822E-01 +1.333719E-01 +1.481091E-01 +1.370422E-01 +1.299765E-01 +9.887040E-02 +8.228753E-02 +4.492330E-02 diff --git a/tests/regression_tests/cmfd_feed_expanding_window/settings.xml b/tests/regression_tests/cmfd_feed_expanding_window/settings.xml new file mode 100644 index 0000000000..24b0b6ab50 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/settings.xml @@ -0,0 +1,26 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml b/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_expanding_window/test.py b/tests/regression_tests/cmfd_feed_expanding_window/test.py new file mode 100644 index 0000000000..e8671e95c7 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_expanding_window/test.py @@ -0,0 +1,26 @@ +from tests.testing_harness import CMFDTestHarness +from openmc import cmfd + + +def test_cmfd_feed_rolling_window(): + """Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + + # Initialize and run CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 10 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.window_type = 'expanding' + cmfd_run.run() + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness.main() diff --git a/tests/regression_tests/cmfd_feed_ng/results_true.dat b/tests/regression_tests/cmfd_feed_ng/results_true.dat index 8308e8634f..d62fb5b4b6 100644 --- a/tests/regression_tests/cmfd_feed_ng/results_true.dat +++ b/tests/regression_tests/cmfd_feed_ng/results_true.dat @@ -1,5 +1,5 @@ k-combined: -1.029540E+00 1.765356E-02 +1.029540E+00 1.765357E-02 tally 1: 1.144958E+02 1.311468E+03 @@ -93,7 +93,7 @@ tally 3: 0.000000E+00 6.939496E+01 3.014077E+02 -6.294738E-01 +6.294737E-01 2.532146E-02 4.991526E+01 1.566280E+02 @@ -129,7 +129,7 @@ tally 3: 0.000000E+00 6.885265E+01 2.965191E+02 -6.537576E-01 +6.537575E-01 2.783733E-02 4.958341E+01 1.546656E+02 @@ -222,7 +222,7 @@ tally 4: 2.662637E-01 2.719284E+01 4.625227E+01 -7.106614E+00 +7.106615E+00 3.182962E+00 2.092906E+00 2.757959E-01 @@ -276,7 +276,7 @@ tally 4: 0.000000E+00 0.000000E+00 0.000000E+00 -7.106614E+00 +7.106615E+00 3.182962E+00 2.092906E+00 2.757959E-01 @@ -572,7 +572,7 @@ cmfd entropy 1.999693E+00 cmfd balance 8.98944E-04 -4.45875E-04 +4.45874E-04 2.18562E-04 2.83412E-04 3.60924E-04 @@ -599,13 +599,13 @@ cmfd openmc source comparison 2.585546E-05 2.217204E-05 2.121913E-05 -9.253920E-06 -2.940945E-05 -3.083595E-05 -2.547490E-05 -2.602689E-05 -2.561168E-05 -2.816824E-05 +9.253928E-06 +2.940947E-05 +3.083596E-05 +2.547491E-05 +2.602691E-05 +2.561169E-05 +2.816825E-05 cmfd source 2.417661E-01 2.547768E-01 diff --git a/tests/regression_tests/cmfd_feed_ng/test.py b/tests/regression_tests/cmfd_feed_ng/test.py index db0b44f3e1..dce674f905 100644 --- a/tests/regression_tests/cmfd_feed_ng/test.py +++ b/tests/regression_tests/cmfd_feed_ng/test.py @@ -7,17 +7,18 @@ def test_cmfd_feed_ng(): """Test n group CMFD solver with CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -1.25984, -1.25984, -1.0 - cmfd_mesh.upper_right = 1.25984, 1.25984, 1.0 - cmfd_mesh.dimension = 2, 2, 1 - cmfd_mesh.energy = [0.0, 0.625, 5.53080, 20000000] - cmfd_mesh.albedo = 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-1.25984, -1.25984, -1.0) + cmfd_mesh.upper_right = (1.25984, 1.25984, 1.0) + cmfd_mesh.dimension = (2, 2, 1) + cmfd_mesh.energy = (0.0, 0.625, 5.53080, 20000000) + cmfd_mesh.albedo = (1.0, 1.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh cmfd_run.reset = [5] - cmfd_run.begin = 10 + cmfd_run.tally_begin = 10 + cmfd_run.feedback_begin = 10 cmfd_run.display = {'dominance': True} cmfd_run.feedback = True cmfd_run.downscatter = True diff --git a/tests/regression_tests/cmfd_feed_ref_d/__init__.py b/tests/regression_tests/cmfd_feed_ref_d/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/cmfd_feed_ref_d/geometry.xml b/tests/regression_tests/cmfd_feed_ref_d/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/regression_tests/cmfd_feed_ref_d/materials.xml b/tests/regression_tests/cmfd_feed_ref_d/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_ref_d/results_true.dat b/tests/regression_tests/cmfd_feed_ref_d/results_true.dat new file mode 100644 index 0000000000..06440d81f6 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/results_true.dat @@ -0,0 +1,488 @@ +k-combined: +1.165408E+00 1.127320E-02 +tally 1: +1.141009E+01 +1.305436E+01 +2.074878E+01 +4.311543E+01 +2.834824E+01 +8.065180E+01 +3.513901E+01 +1.238542E+02 +3.699846E+01 +1.372507E+02 +3.612638E+01 +1.310741E+02 +3.373329E+01 +1.140431E+02 +2.836806E+01 +8.075546E+01 +2.158796E+01 +4.674672E+01 +1.141841E+01 +1.310586E+01 +tally 2: +1.126662E+00 +1.269368E+00 +7.749275E-01 +6.005127E-01 +1.991651E+00 +3.966673E+00 +1.411802E+00 +1.993185E+00 +2.978294E+00 +8.870233E+00 +2.130084E+00 +4.537258E+00 +3.708845E+00 +1.375553E+01 +2.698001E+00 +7.279210E+00 +4.233143E+00 +1.791950E+01 +3.023958E+00 +9.144324E+00 +4.062060E+00 +1.650033E+01 +2.910435E+00 +8.470632E+00 +3.506628E+00 +1.229644E+01 +2.497713E+00 +6.238573E+00 +2.974637E+00 +8.848463E+00 +2.121135E+00 +4.499215E+00 +2.329228E+00 +5.425303E+00 +1.658218E+00 +2.749687E+00 +1.158542E+00 +1.342220E+00 +8.282626E-01 +6.860190E-01 +tally 3: +7.459525E-01 +5.564451E-01 +4.877161E-02 +2.378670E-03 +1.356729E+00 +1.840713E+00 +8.360848E-02 +6.990377E-03 +2.047350E+00 +4.191641E+00 +1.521210E-01 +2.314079E-02 +2.610278E+00 +6.813552E+00 +1.718619E-01 +2.953650E-02 +2.911348E+00 +8.475950E+00 +1.823129E-01 +3.323800E-02 +2.806884E+00 +7.878600E+00 +2.090212E-01 +4.368986E-02 +2.415690E+00 +5.835559E+00 +1.486373E-01 +2.209304E-02 +2.051037E+00 +4.206752E+00 +1.196066E-01 +1.430573E-02 +1.593732E+00 +2.539980E+00 +1.126392E-01 +1.268759E-02 +7.994865E-01 +6.391786E-01 +5.806144E-02 +3.371131E-03 +tally 4: +1.341763E-01 +1.800329E-02 +0.000000E+00 +0.000000E+00 +1.420749E-01 +2.018527E-02 +2.633206E-01 +6.933776E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.633206E-01 +6.933776E-02 +1.420749E-01 +2.018527E-02 +2.628684E-01 +6.909981E-02 +3.590428E-01 +1.289117E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.590428E-01 +1.289117E-01 +2.628684E-01 +6.909981E-02 +3.851638E-01 +1.483512E-01 +4.595776E-01 +2.112116E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.595776E-01 +2.112116E-01 +3.851638E-01 +1.483512E-01 +4.679953E-01 +2.190196E-01 +4.970127E-01 +2.470216E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.970127E-01 +2.470216E-01 +4.679953E-01 +2.190196E-01 +4.909606E-01 +2.410423E-01 +4.838733E-01 +2.341333E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.838733E-01 +2.341333E-01 +4.909606E-01 +2.410423E-01 +5.152620E-01 +2.654949E-01 +4.788580E-01 +2.293049E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.788580E-01 +2.293049E-01 +5.152620E-01 +2.654949E-01 +4.266812E-01 +1.820568E-01 +3.401298E-01 +1.156883E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.401298E-01 +1.156883E-01 +4.266812E-01 +1.820568E-01 +3.724142E-01 +1.386923E-01 +2.559996E-01 +6.553579E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.559996E-01 +6.553579E-02 +3.724142E-01 +1.386923E-01 +2.892105E-01 +8.364271E-02 +1.556166E-01 +2.421652E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.556166E-01 +2.421652E-02 +2.892105E-01 +8.364271E-02 +1.497731E-01 +2.243198E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +7.459525E-01 +5.564451E-01 +1.149859E-01 +1.322177E-02 +1.356729E+00 +1.840713E+00 +1.801152E-01 +3.244149E-02 +2.047350E+00 +4.191641E+00 +2.627538E-01 +6.903954E-02 +2.609208E+00 +6.807967E+00 +3.360312E-01 +1.129170E-01 +2.909427E+00 +8.464765E+00 +4.095398E-01 +1.677229E-01 +2.805095E+00 +7.868559E+00 +3.957580E-01 +1.566244E-01 +2.415690E+00 +5.835559E+00 +3.196366E-01 +1.021676E-01 +2.049833E+00 +4.201813E+00 +3.162366E-01 +1.000056E-01 +1.593732E+00 +2.539980E+00 +2.169968E-01 +4.708761E-02 +7.994865E-01 +6.391786E-01 +1.002479E-01 +1.004964E-02 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +1.143785E+00 +1.163460E+00 +1.173453E+00 +1.171056E+00 +1.147214E+00 +1.122230E+00 +1.106385E+00 +1.124706E+00 +1.133207E+00 +1.134453E+00 +1.142355E+00 +1.132121E+00 +1.132479E+00 +1.153657E+00 +1.170183E+00 +1.184408E+00 +cmfd entropy +3.211758E+00 +3.206356E+00 +3.223933E+00 +3.222754E+00 +3.232110E+00 +3.242098E+00 +3.246062E+00 +3.238858E+00 +3.235577E+00 +3.234604E+00 +3.233582E+00 +3.236922E+00 +3.229563E+00 +3.225265E+00 +3.221498E+00 +3.219126E+00 +cmfd balance +8.26180E-03 +4.27338E-03 +2.22686E-03 +1.93026E-03 +1.96979E-03 +2.13756E-03 +2.01521E-03 +1.74538E-03 +2.09240E-03 +1.25495E-03 +1.49542E-03 +1.76968E-03 +1.99174E-03 +1.87753E-03 +1.24170E-03 +1.15645E-03 +cmfd dominance ratio +5.408E-01 +5.374E-01 +5.420E-01 +5.444E-01 +5.513E-01 +5.613E-01 +5.722E-01 +5.592E-01 +5.542E-01 +5.537E-01 +5.541E-01 +5.579E-01 +5.543E-01 +5.487E-01 +5.413E-01 +5.381E-01 +cmfd openmc source comparison +1.597982E-02 +1.276493E-02 +3.495229E-03 +8.157807E-03 +5.715330E-03 +7.433111E-03 +5.006211E-03 +1.766072E-03 +5.184425E-03 +3.864321E-03 +6.617152E-03 +8.841210E-03 +5.454747E-03 +5.652690E-03 +4.006767E-03 +4.167617E-03 +cmfd source +4.116746E-02 +7.798354E-02 +1.062881E-01 +1.333681E-01 +1.481008E-01 +1.370408E-01 +1.299726E-01 +9.886814E-02 +8.228698E-02 +4.492338E-02 diff --git a/tests/regression_tests/cmfd_feed_ref_d/settings.xml b/tests/regression_tests/cmfd_feed_ref_d/settings.xml new file mode 100644 index 0000000000..24b0b6ab50 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/settings.xml @@ -0,0 +1,26 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_ref_d/tallies.xml b/tests/regression_tests/cmfd_feed_ref_d/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_ref_d/test.py b/tests/regression_tests/cmfd_feed_ref_d/test.py new file mode 100644 index 0000000000..d033ab8ddf --- /dev/null +++ b/tests/regression_tests/cmfd_feed_ref_d/test.py @@ -0,0 +1,27 @@ +from tests.testing_harness import CMFDTestHarness +from openmc import cmfd + + +def test_cmfd_feed_rolling_window(): + """Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + + # Initialize and run CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 10 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.window_type = 'expanding' + cmfd_run.ref_d = [0.542] + cmfd_run.run() + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness.main() diff --git a/tests/regression_tests/cmfd_feed_rolling_window/__init__.py b/tests/regression_tests/cmfd_feed_rolling_window/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml b/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/regression_tests/cmfd_feed_rolling_window/materials.xml b/tests/regression_tests/cmfd_feed_rolling_window/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat b/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat new file mode 100644 index 0000000000..5180526835 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/results_true.dat @@ -0,0 +1,488 @@ +k-combined: +1.172120E+00 9.761693E-03 +tally 1: +1.131066E+01 +1.287315E+01 +2.035268E+01 +4.162146E+01 +2.839961E+01 +8.098764E+01 +3.405187E+01 +1.164951E+02 +3.712390E+01 +1.380899E+02 +3.687780E+01 +1.363090E+02 +3.380903E+01 +1.147695E+02 +2.903657E+01 +8.489183E+01 +2.121421E+01 +4.519765E+01 +1.112628E+01 +1.245629E+01 +tally 2: +1.114845E+00 +1.242879E+00 +8.000235E-01 +6.400375E-01 +1.852141E+00 +3.430427E+00 +1.324803E+00 +1.755104E+00 +2.585263E+00 +6.683587E+00 +1.840969E+00 +3.389167E+00 +3.737263E+00 +1.396714E+01 +2.674345E+00 +7.152122E+00 +3.989056E+00 +1.591257E+01 +2.843382E+00 +8.084820E+00 +3.951710E+00 +1.561601E+01 +2.838216E+00 +8.055468E+00 +3.734018E+00 +1.394289E+01 +2.676071E+00 +7.161356E+00 +3.241209E+00 +1.050543E+01 +2.315684E+00 +5.362392E+00 +2.459493E+00 +6.049108E+00 +1.734344E+00 +3.007949E+00 +1.079306E+00 +1.164902E+00 +7.536940E-01 +5.680547E-01 +tally 3: +7.728275E-01 +5.972624E-01 +5.550061E-02 +3.080318E-03 +1.272034E+00 +1.618070E+00 +7.284456E-02 +5.306329E-03 +1.779821E+00 +3.167762E+00 +1.110012E-01 +1.232127E-02 +2.586020E+00 +6.687502E+00 +1.618768E-01 +2.620410E-02 +2.726552E+00 +7.434084E+00 +1.965647E-01 +3.863767E-02 +2.741747E+00 +7.517178E+00 +1.907834E-01 +3.639829E-02 +2.564084E+00 +6.574527E+00 +1.503142E-01 +2.259435E-02 +2.233122E+00 +4.986832E+00 +1.456891E-01 +2.122532E-02 +1.682929E+00 +2.832250E+00 +9.828234E-02 +9.659418E-03 +7.232788E-01 +5.231322E-01 +6.128193E-02 +3.755475E-03 +tally 4: +1.326662E-01 +1.760031E-02 +0.000000E+00 +0.000000E+00 +1.369313E-01 +1.875018E-02 +2.569758E-01 +6.603657E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.569758E-01 +6.603657E-02 +1.369313E-01 +1.875018E-02 +2.494513E-01 +6.222596E-02 +3.551002E-01 +1.260962E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.551002E-01 +1.260962E-01 +2.494513E-01 +6.222596E-02 +3.610027E-01 +1.303230E-01 +4.252566E-01 +1.808432E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.252566E-01 +1.808432E-01 +3.610027E-01 +1.303230E-01 +4.673502E-01 +2.184162E-01 +5.073957E-01 +2.574504E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.073957E-01 +2.574504E-01 +4.673502E-01 +2.184162E-01 +4.818855E-01 +2.322136E-01 +4.949753E-01 +2.450006E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.949753E-01 +2.450006E-01 +4.818855E-01 +2.322136E-01 +5.176611E-01 +2.679730E-01 +4.756654E-01 +2.262576E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.756654E-01 +2.262576E-01 +5.176611E-01 +2.679730E-01 +4.591191E-01 +2.107904E-01 +3.930251E-01 +1.544688E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.930251E-01 +1.544688E-01 +4.591191E-01 +2.107904E-01 +4.201532E-01 +1.765287E-01 +3.188250E-01 +1.016494E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.188250E-01 +1.016494E-01 +4.201532E-01 +1.765287E-01 +2.898817E-01 +8.403142E-02 +1.509185E-01 +2.277639E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.509185E-01 +2.277639E-02 +2.898817E-01 +8.403142E-02 +1.482175E-01 +2.196844E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +7.719619E-01 +5.959252E-01 +8.168322E-02 +6.672148E-03 +1.272034E+00 +1.618070E+00 +1.458271E-01 +2.126554E-02 +1.779821E+00 +3.167762E+00 +2.705917E-01 +7.321987E-02 +2.584069E+00 +6.677414E+00 +3.176497E-01 +1.009013E-01 +2.726552E+00 +7.434084E+00 +3.562714E-01 +1.269293E-01 +2.740788E+00 +7.511919E+00 +3.690107E-01 +1.361689E-01 +2.563145E+00 +6.569715E+00 +3.296244E-01 +1.086523E-01 +2.233122E+00 +4.986832E+00 +3.106254E-01 +9.648813E-02 +1.682929E+00 +2.832250E+00 +2.709632E-01 +7.342104E-02 +7.232788E-01 +5.231322E-01 +9.343486E-02 +8.730073E-03 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +1.143597E+00 +1.163387E+00 +1.162391E+00 +1.163351E+00 +1.145721E+00 +1.134785E+00 +1.119048E+00 +1.116124E+00 +1.109085E+00 +1.126581E+00 +1.158559E+00 +1.163719E+00 +1.162162E+00 +1.160856E+00 +1.166709E+00 +1.167223E+00 +cmfd entropy +3.212002E+00 +3.206393E+00 +3.222109E+00 +3.221996E+00 +3.229978E+00 +3.234615E+00 +3.246512E+00 +3.244634E+00 +3.244312E+00 +3.236922E+00 +3.232693E+00 +3.221849E+00 +3.215716E+00 +3.215968E+00 +3.203758E+00 +3.201798E+00 +cmfd balance +8.26180E-03 +4.27338E-03 +2.62159E-03 +2.40301E-03 +2.08484E-03 +1.58351E-03 +1.59196E-03 +1.87591E-03 +1.94451E-03 +1.91803E-03 +1.90044E-03 +1.87968E-03 +2.55363E-03 +2.39932E-03 +2.25515E-03 +1.53613E-03 +cmfd dominance ratio +5.404E-01 +5.406E-01 +5.432E-01 +5.454E-01 +5.507E-01 +5.578E-01 +5.679E-01 +5.671E-01 +5.690E-01 +5.637E-01 +5.575E-01 +5.474E-01 +5.480E-01 +5.467E-01 +5.374E-01 +5.321E-01 +cmfd openmc source comparison +1.575499E-02 +1.293688E-02 +5.920734E-03 +9.746850E-03 +7.183896E-03 +7.693485E-03 +4.158805E-03 +2.962505E-03 +4.415044E-03 +2.304495E-03 +7.921580E-03 +8.609203E-03 +8.945198E-03 +8.054204E-03 +1.164189E-02 +5.945645E-03 +cmfd source +4.077779E-02 +6.659143E-02 +1.017198E-01 +1.172269E-01 +1.458410E-01 +1.559801E-01 +1.337001E-01 +1.137262E-01 +8.397376E-02 +4.046290E-02 diff --git a/tests/regression_tests/cmfd_feed_rolling_window/settings.xml b/tests/regression_tests/cmfd_feed_rolling_window/settings.xml new file mode 100644 index 0000000000..24b0b6ab50 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/settings.xml @@ -0,0 +1,26 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml b/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_rolling_window/test.py b/tests/regression_tests/cmfd_feed_rolling_window/test.py new file mode 100644 index 0000000000..802b5b7003 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rolling_window/test.py @@ -0,0 +1,27 @@ +from tests.testing_harness import CMFDTestHarness +from openmc import cmfd + + +def test_cmfd_feed_rolling_window(): + """Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + + # Initialize and run CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 10 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.window_type = 'rolling' + cmfd_run.window_size = 5 + cmfd_run.run() + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness.main() diff --git a/tests/regression_tests/cmfd_nofeed/test.py b/tests/regression_tests/cmfd_nofeed/test.py index 36f6930268..7d0895c2fb 100644 --- a/tests/regression_tests/cmfd_nofeed/test.py +++ b/tests/regression_tests/cmfd_nofeed/test.py @@ -7,15 +7,15 @@ def test_cmfd_nofeed(): """Test 1 group CMFD solver without CMFD feedback""" # Initialize and set CMFD mesh cmfd_mesh = cmfd.CMFDMesh() - cmfd_mesh.lower_left = -10.0, -1.0, -1.0 - cmfd_mesh.upper_right = 10.0, 1.0, 1.0 - cmfd_mesh.dimension = 10, 1, 1 - cmfd_mesh.albedo = 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) # Initialize and run CMFDRun object cmfd_run = cmfd.CMFDRun() cmfd_run.mesh = cmfd_mesh - cmfd_run.begin = 5 + cmfd_run.tally_begin = 5 cmfd_run.display = {'dominance': True} cmfd_run.feedback = False cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] diff --git a/tests/regression_tests/cmfd_restart/__init__.py b/tests/regression_tests/cmfd_restart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/cmfd_restart/geometry.xml b/tests/regression_tests/cmfd_restart/geometry.xml new file mode 100644 index 0000000000..73ea679c4c --- /dev/null +++ b/tests/regression_tests/cmfd_restart/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/regression_tests/cmfd_restart/materials.xml b/tests/regression_tests/cmfd_restart/materials.xml new file mode 100644 index 0000000000..70580e3a8d --- /dev/null +++ b/tests/regression_tests/cmfd_restart/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_restart/results_true.dat b/tests/regression_tests/cmfd_restart/results_true.dat new file mode 100644 index 0000000000..4a2c6eea25 --- /dev/null +++ b/tests/regression_tests/cmfd_restart/results_true.dat @@ -0,0 +1,488 @@ +k-combined: +1.169891E+00 6.289489E-03 +tally 1: +1.173922E+01 +1.385461E+01 +2.164076E+01 +4.699368E+01 +2.906462E+01 +8.464937E+01 +3.382312E+01 +1.147095E+02 +3.632006E+01 +1.323878E+02 +3.655413E+01 +1.341064E+02 +3.347757E+01 +1.124264E+02 +2.931336E+01 +8.607239E+01 +2.182947E+01 +4.789565E+01 +1.147668E+01 +1.325716E+01 +tally 2: +2.298190E+01 +2.667071E+01 +1.600292E+01 +1.293670E+01 +4.268506E+01 +9.161216E+01 +3.022909E+01 +4.598915E+01 +5.680399E+01 +1.623879E+02 +4.033805E+01 +8.196263E+01 +6.814742E+01 +2.331778E+02 +4.851618E+01 +1.182330E+02 +7.392923E+01 +2.740255E+02 +5.253586E+01 +1.384152E+02 +7.332860E+01 +2.698608E+02 +5.227405E+01 +1.371810E+02 +6.830172E+01 +2.340687E+02 +4.867159E+01 +1.188724E+02 +5.885634E+01 +1.736180E+02 +4.170434E+01 +8.719622E+01 +4.371848E+01 +9.592893E+01 +3.106403E+01 +4.844308E+01 +2.338413E+01 +2.752467E+01 +1.636713E+01 +1.347770E+01 +tally 3: +1.538752E+01 +1.196478E+01 +1.079685E+00 +6.010786E-02 +2.911906E+01 +4.269070E+01 +1.822657E+00 +1.671850E-01 +3.885421E+01 +7.608218E+01 +2.541516E+00 +3.262451E-01 +4.673300E+01 +1.097036E+02 +2.885307E+00 +4.214444E-01 +5.059247E+01 +1.283984E+02 +3.222796E+00 +5.237329E-01 +5.034856E+01 +1.272538E+02 +3.230225E+00 +5.273424E-01 +4.688476E+01 +1.103152E+02 +2.941287E+00 +4.363749E-01 +4.013746E+01 +8.077506E+01 +2.634234E+00 +3.520270E-01 +2.996887E+01 +4.509953E+01 +1.946504E+00 +1.919104E-01 +1.575260E+01 +1.248707E+01 +1.020705E+00 +5.413569E-02 +tally 4: +3.049469E+00 +4.677325E-01 +0.000000E+00 +0.000000E+00 +2.770358E+00 +3.879191E-01 +5.514939E+00 +1.528899E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.514939E+00 +1.528899E+00 +2.770358E+00 +3.879191E-01 +5.032131E+00 +1.275040E+00 +7.294002E+00 +2.675589E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.294002E+00 +2.675589E+00 +5.032131E+00 +1.275040E+00 +7.036008E+00 +2.490718E+00 +8.668860E+00 +3.776102E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.668860E+00 +3.776102E+00 +7.036008E+00 +2.490718E+00 +8.352414E+00 +3.501945E+00 +9.345868E+00 +4.380719E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.345868E+00 +4.380719E+00 +8.352414E+00 +3.501945E+00 +9.093766E+00 +4.158282E+00 +9.223771E+00 +4.270120E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.223771E+00 +4.270120E+00 +9.093766E+00 +4.158282E+00 +9.219150E+00 +4.264346E+00 +8.530966E+00 +3.651778E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.530966E+00 +3.651778E+00 +9.219150E+00 +4.264346E+00 +8.690373E+00 +3.785262E+00 +7.204424E+00 +2.604203E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.204424E+00 +2.604203E+00 +8.690373E+00 +3.785262E+00 +7.513640E+00 +2.833028E+00 +5.326721E+00 +1.426975E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.326721E+00 +1.426975E+00 +7.513640E+00 +2.833028E+00 +5.662215E+00 +1.607757E+00 +2.848381E+00 +4.093396E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +2.848381E+00 +4.093396E-01 +5.662215E+00 +1.607757E+00 +3.025812E+00 +4.597241E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +1.538652E+01 +1.196332E+01 +2.252427E+00 +2.605738E-01 +2.911344E+01 +4.267319E+01 +3.873926E+00 +7.615035E-01 +3.884516E+01 +7.604619E+01 +5.280610E+00 +1.414008E+00 +4.672391E+01 +1.096625E+02 +6.261805E+00 +1.983205E+00 +5.058447E+01 +1.283588E+02 +6.733810E+00 +2.278242E+00 +5.033589E+01 +1.271898E+02 +6.714658E+00 +2.273652E+00 +4.687563E+01 +1.102719E+02 +6.215002E+00 +1.956978E+00 +4.013134E+01 +8.075062E+01 +5.253064E+00 +1.396224E+00 +2.996497E+01 +4.508839E+01 +3.818076E+00 +7.509442E-01 +1.574994E+01 +1.248291E+01 +2.219928E+00 +2.515492E-01 +cmfd indices +1.000000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +1.170416E+00 +1.172966E+00 +1.165537E+00 +1.170979E+00 +1.161922E+00 +1.157523E+00 +1.158873E+00 +1.162877E+00 +1.167101E+00 +1.168130E+00 +1.170570E+00 +1.168115E+00 +1.174081E+00 +1.169458E+00 +1.167848E+00 +1.165116E+00 +cmfd entropy +3.203643E+00 +3.207943E+00 +3.213367E+00 +3.214360E+00 +3.219634E+00 +3.222232E+00 +3.221744E+00 +3.224544E+00 +3.225990E+00 +3.227769E+00 +3.227417E+00 +3.230728E+00 +3.231662E+00 +3.233316E+00 +3.233193E+00 +3.232564E+00 +cmfd balance +4.00906E-03 +4.43177E-03 +3.15267E-03 +3.51038E-03 +2.05209E-03 +2.06865E-03 +1.50243E-03 +1.58983E-03 +1.56602E-03 +1.17001E-03 +9.50759E-04 +9.07259E-04 +1.00900E-03 +1.06470E-03 +1.16361E-03 +9.75631E-04 +cmfd dominance ratio +5.397E-01 +5.425E-01 +5.481E-01 +5.473E-01 +5.503E-01 +5.502E-01 +5.483E-01 +5.520E-01 +5.505E-01 +3.216E-01 +5.373E-01 +5.517E-01 +5.508E-01 +5.524E-01 +5.524E-01 +5.523E-01 +cmfd openmc source comparison +6.959834E-03 +5.655657E-03 +3.886186E-03 +4.035116E-03 +3.043277E-03 +5.455474E-03 +4.515310E-03 +2.439840E-03 +2.114032E-03 +2.673132E-03 +2.431749E-03 +4.330928E-03 +3.404647E-03 +3.680298E-03 +3.309620E-03 +3.705541E-03 +cmfd source +4.697085E-02 +7.920706E-02 +1.107968E-01 +1.250932E-01 +1.383930E-01 +1.380648E-01 +1.246874E-01 +1.113705E-01 +8.203754E-02 +4.337882E-02 diff --git a/tests/regression_tests/cmfd_restart/settings.xml b/tests/regression_tests/cmfd_restart/settings.xml new file mode 100644 index 0000000000..ba5495911f --- /dev/null +++ b/tests/regression_tests/cmfd_restart/settings.xml @@ -0,0 +1,28 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + + + diff --git a/tests/regression_tests/cmfd_restart/tallies.xml b/tests/regression_tests/cmfd_restart/tallies.xml new file mode 100644 index 0000000000..c869711147 --- /dev/null +++ b/tests/regression_tests/cmfd_restart/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_restart/test.py b/tests/regression_tests/cmfd_restart/test.py new file mode 100644 index 0000000000..7ed92d024c --- /dev/null +++ b/tests/regression_tests/cmfd_restart/test.py @@ -0,0 +1,72 @@ +import glob +import os +import copy + +from tests.testing_harness import CMFDTestHarness +from openmc import cmfd +import numpy as np + + +class CMFDRestartTestHarness(CMFDTestHarness): + def __init__(self, final_sp, restart_sp, cmfd_run1, cmfd_run2): + super().__init__(final_sp, cmfd_run1) + self._cmfd_restart_run = cmfd_run2 + self._restart_sp = restart_sp + + def execute_test(self): + try: + # Compare results from first CMFD run + self._test_output_created() + results = self._get_results() + results += self._cmfdrun_results + self._write_results(results) + self._compare_results() + + # Run CMFD from restart file + statepoint = glob.glob(os.path.join(os.getcwd(), self._restart_sp)) + assert len(statepoint) == 1 + statepoint = statepoint[0] + self._cmfd_restart_run.run(args=['-r', statepoint]) + + # Compare results from second CMFD run + self._test_output_created() + self._create_cmfd_result_str(self._cmfd_restart_run) + results = self._get_results() + results += self._cmfdrun_results + self._write_results(results) + self._compare_results() + finally: + self._cleanup() + + +def test_cmfd_restart(): + """Test 1 group CMFD solver with restart run""" + # Initialize and set CMFD mesh, create a copy for second run + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.lower_left = (-10.0, -1.0, -1.0) + cmfd_mesh.upper_right = (10.0, 1.0, 1.0) + cmfd_mesh.dimension = (10, 1, 1) + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + cmfd_mesh2 = copy.deepcopy(cmfd_mesh) + + # Initialize and run first CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.feedback_begin = 5 + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Initialize second CMFDRun object which will be run from restart file + cmfd_run2 = cmfd.CMFDRun() + cmfd_run2.mesh = cmfd_mesh2 + cmfd_run2.tally_begin = 5 + cmfd_run2.feedback_begin = 5 + cmfd_run2.feedback = True + cmfd_run2.gauss_seidel_tolerance = [1.e-15, 1.e-20] + + # Initialize and run CMFD restart test harness + harness = CMFDRestartTestHarness('statepoint.20.h5', 'statepoint.15.h5', + cmfd_run, cmfd_run2) + harness.main() diff --git a/tests/regression_tests/dagmc/legacy/__init__.py b/tests/regression_tests/dagmc/legacy/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/dagmc/dagmc.h5m b/tests/regression_tests/dagmc/legacy/dagmc.h5m similarity index 100% rename from tests/regression_tests/dagmc/dagmc.h5m rename to tests/regression_tests/dagmc/legacy/dagmc.h5m diff --git a/tests/regression_tests/dagmc/inputs_true.dat b/tests/regression_tests/dagmc/legacy/inputs_true.dat similarity index 100% rename from tests/regression_tests/dagmc/inputs_true.dat rename to tests/regression_tests/dagmc/legacy/inputs_true.dat diff --git a/tests/regression_tests/dagmc/results_true.dat b/tests/regression_tests/dagmc/legacy/results_true.dat similarity index 100% rename from tests/regression_tests/dagmc/results_true.dat rename to tests/regression_tests/dagmc/legacy/results_true.dat diff --git a/tests/regression_tests/dagmc/test.py b/tests/regression_tests/dagmc/legacy/test.py similarity index 100% rename from tests/regression_tests/dagmc/test.py rename to tests/regression_tests/dagmc/legacy/test.py diff --git a/tests/regression_tests/dagmc/refl/__init__.py b/tests/regression_tests/dagmc/refl/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/dagmc/refl/dagmc.h5m b/tests/regression_tests/dagmc/refl/dagmc.h5m new file mode 100644 index 0000000000..ac788d2deb Binary files /dev/null and b/tests/regression_tests/dagmc/refl/dagmc.h5m differ diff --git a/tests/regression_tests/uwuw/inputs_true.dat b/tests/regression_tests/dagmc/refl/inputs_true.dat similarity index 100% rename from tests/regression_tests/uwuw/inputs_true.dat rename to tests/regression_tests/dagmc/refl/inputs_true.dat diff --git a/tests/regression_tests/dagmc/refl/results_true.dat b/tests/regression_tests/dagmc/refl/results_true.dat new file mode 100644 index 0000000000..ed67e2f93b --- /dev/null +++ b/tests/regression_tests/dagmc/refl/results_true.dat @@ -0,0 +1,5 @@ +k-combined: +2.053871E+00 7.718195E-03 +tally 1: +1.171003E+01 +2.864565E+01 diff --git a/tests/regression_tests/dagmc/refl/test.py b/tests/regression_tests/dagmc/refl/test.py new file mode 100644 index 0000000000..93104f1d0d --- /dev/null +++ b/tests/regression_tests/dagmc/refl/test.py @@ -0,0 +1,40 @@ +import openmc +import openmc.capi +from openmc.stats import Box + +import pytest +from tests.testing_harness import PyAPITestHarness + +pytestmark = pytest.mark.skipif( + not openmc.capi._dagmc_enabled(), + reason="DAGMC CAD geometry is not enabled.") + +class UWUWTest(PyAPITestHarness): + + def _build_inputs(self): + model = openmc.model.Model() + + # settings + model.settings.batches = 5 + model.settings.inactive = 0 + model.settings.particles = 100 + + source = openmc.Source(space=Box([-4, -4, -4], + [ 4, 4, 4])) + model.settings.source = source + + model.settings.dagmc = True + + model.settings.export_to_xml() + + # tally + tally = openmc.Tally() + tally.scores = ['total'] + tally.filters = [openmc.CellFilter(1)] + model.tallies = [tally] + + model.tallies.export_to_xml() + +def test_refl(): + harness = UWUWTest('statepoint.5.h5') + harness.main() diff --git a/tests/regression_tests/dagmc/uwuw/__init__.py b/tests/regression_tests/dagmc/uwuw/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/uwuw/dagmc.h5m b/tests/regression_tests/dagmc/uwuw/dagmc.h5m similarity index 100% rename from tests/regression_tests/uwuw/dagmc.h5m rename to tests/regression_tests/dagmc/uwuw/dagmc.h5m diff --git a/tests/regression_tests/dagmc/uwuw/inputs_true.dat b/tests/regression_tests/dagmc/uwuw/inputs_true.dat new file mode 100644 index 0000000000..87ceb944d8 --- /dev/null +++ b/tests/regression_tests/dagmc/uwuw/inputs_true.dat @@ -0,0 +1,23 @@ + + + eigenvalue + 100 + 5 + 0 + + + -4 -4 -4 4 4 4 + + + true + + + + + 1 + + + 1 + total + + diff --git a/tests/regression_tests/dagmc/uwuw/results_true.dat b/tests/regression_tests/dagmc/uwuw/results_true.dat new file mode 120000 index 0000000000..f09f75fa58 --- /dev/null +++ b/tests/regression_tests/dagmc/uwuw/results_true.dat @@ -0,0 +1 @@ +../legacy/results_true.dat \ No newline at end of file diff --git a/tests/regression_tests/uwuw/test.py b/tests/regression_tests/dagmc/uwuw/test.py similarity index 96% rename from tests/regression_tests/uwuw/test.py rename to tests/regression_tests/dagmc/uwuw/test.py index f6713c2e4f..885b83766f 100644 --- a/tests/regression_tests/uwuw/test.py +++ b/tests/regression_tests/dagmc/uwuw/test.py @@ -1,7 +1,6 @@ import openmc import openmc.capi from openmc.stats import Box -from openmc.material import Materials import pytest from tests.testing_harness import PyAPITestHarness diff --git a/tests/regression_tests/filter_distribcell/case-1/results_true.dat b/tests/regression_tests/filter_distribcell/case-1/results_true.dat index 2d9835b1ab..80036a7c47 100644 --- a/tests/regression_tests/filter_distribcell/case-1/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-1/results_true.dat @@ -1,5 +1,5 @@ k-combined: -0.000000E+00 0.000000E+00 +5.497140E-02 INF tally 1: 1.548980E-02 2.399339E-04 diff --git a/tests/regression_tests/filter_distribcell/case-2/results_true.dat b/tests/regression_tests/filter_distribcell/case-2/results_true.dat index 57e40f4962..bbf38b3d1c 100644 --- a/tests/regression_tests/filter_distribcell/case-2/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-2/results_true.dat @@ -1,5 +1,5 @@ k-combined: -0.000000E+00 0.000000E+00 +2.149726E-02 INF tally 1: 7.588170E-03 5.758032E-05 diff --git a/tests/regression_tests/filter_distribcell/case-3/results_true.dat b/tests/regression_tests/filter_distribcell/case-3/results_true.dat index 0a4dd9dc76..be197b5e9f 100644 --- a/tests/regression_tests/filter_distribcell/case-3/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-3/results_true.dat @@ -1 +1 @@ -11755ecac8355b5e79384f5d72974e618b6f95500c0a5718c01bb340c5d1c8ceafc33de65b9e94b7e9d78f16ec209f8a0fdf6a531eab5430657688d0125db7ef \ No newline at end of file +2ee0162762999f71ad2178936509fd9e054928023a9e0e90c078cede3ecf6583267069486adf15f1b02188333d1bfe1fc41b341bc00aab53feddd1395441f1f8 \ No newline at end of file diff --git a/tests/regression_tests/filter_distribcell/case-4/results_true.dat b/tests/regression_tests/filter_distribcell/case-4/results_true.dat index 078e74bed1..43a837e983 100644 --- a/tests/regression_tests/filter_distribcell/case-4/results_true.dat +++ b/tests/regression_tests/filter_distribcell/case-4/results_true.dat @@ -1,5 +1,5 @@ k-combined: -0.000000E+00 0.000000E+00 +1.121246E-01 INF tally 1: 2.265319E-02 5.131668E-04 diff --git a/tests/regression_tests/filter_mesh/inputs_true.dat b/tests/regression_tests/filter_mesh/inputs_true.dat index 511b298482..6755a64c82 100644 --- a/tests/regression_tests/filter_mesh/inputs_true.dat +++ b/tests/regression_tests/filter_mesh/inputs_true.dat @@ -309,45 +309,56 @@ - + 17 -182.07 182.07 - + 17 17 -182.07 -182.07 182.07 182.07 - + 17 17 17 -182.07 -182.07 -183.0 182.07 182.07 183.0 + + -182.07 -160.65 -139.23 -117.81 -96.39 -74.97 -53.55000000000001 -32.129999999999995 -10.710000000000008 10.70999999999998 32.129999999999995 53.54999999999998 74.96999999999997 96.38999999999999 117.81 139.22999999999996 160.64999999999998 182.07 + -182.07 -160.65 -139.23 -117.81 -96.39 -74.97 -53.55000000000001 -32.129999999999995 -10.710000000000008 10.70999999999998 32.129999999999995 53.54999999999998 74.96999999999997 96.38999999999999 117.81 139.22999999999996 160.64999999999998 182.07 + 1.0 1.683624003879018 2.8345897864376153 4.772383405596668 8.034899257376447 13.52774925846868 22.77564337001445 38.34561988154435 64.55960607618856 108.69410247084474 182.99999999999991 + 1 - + 1 2 - + 2 3 - + 3 + + 4 + + + 4 + 1 total - 4 + 5 current @@ -355,7 +366,7 @@ total - 5 + 6 current @@ -363,7 +374,15 @@ total - 6 + 7 + current + + + 4 + total + + + 8 current diff --git a/tests/regression_tests/filter_mesh/results_true.dat b/tests/regression_tests/filter_mesh/results_true.dat index 227d0d608a..05efffe2fb 100644 --- a/tests/regression_tests/filter_mesh/results_true.dat +++ b/tests/regression_tests/filter_mesh/results_true.dat @@ -1 +1 @@ -cc306dfd3e3669827cb2f3bdc38005c0616f6faecb9277f3b0f869fdecf826ae662c65d67a402705956f100d2e13c7c11332364fa41ccc2e215ab100709b627f \ No newline at end of file +35f04a6f062ef64116ef4eb0e9b803cd44cff7e185e2b53c9174afad8a26ca1a436ca9b800d6a228e006a9129f4536d7dce289d7a11cd56c6949d71d6a201b31 \ No newline at end of file diff --git a/tests/regression_tests/filter_mesh/test.py b/tests/regression_tests/filter_mesh/test.py index 8a7f7a6fca..ed2e9b35b4 100644 --- a/tests/regression_tests/filter_mesh/test.py +++ b/tests/regression_tests/filter_mesh/test.py @@ -1,3 +1,5 @@ +import numpy as np + import openmc from tests.testing_harness import HashedPyAPITestHarness @@ -8,31 +10,35 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): super().__init__(*args, **kwargs) # Initialize Meshes - mesh_1d = openmc.Mesh(mesh_id=1) - mesh_1d.type = 'regular' + mesh_1d = openmc.RegularMesh(mesh_id=1) mesh_1d.dimension = [17] mesh_1d.lower_left = [-182.07] mesh_1d.upper_right = [182.07] - mesh_2d = openmc.Mesh(mesh_id=2) - mesh_2d.type = 'regular' + mesh_2d = openmc.RegularMesh(mesh_id=2) mesh_2d.dimension = [17, 17] mesh_2d.lower_left = [-182.07, -182.07] mesh_2d.upper_right = [182.07, 182.07] - mesh_3d = openmc.Mesh(mesh_id=3) - mesh_3d.type = 'regular' + mesh_3d = openmc.RegularMesh(mesh_id=3) mesh_3d.dimension = [17, 17, 17] mesh_3d.lower_left = [-182.07, -182.07, -183.00] mesh_3d.upper_right = [182.07, 182.07, 183.00] + recti_mesh = openmc.RectilinearMesh(mesh_id=4) + recti_mesh.x_grid = np.linspace(-182.07, 182.07, 18) + recti_mesh.y_grid = np.linspace(-182.07, 182.07, 18) + recti_mesh.z_grid = np.logspace(0, np.log10(183), 11) + # Initialize the filters mesh_1d_filter = openmc.MeshFilter(mesh_1d) mesh_2d_filter = openmc.MeshFilter(mesh_2d) mesh_3d_filter = openmc.MeshFilter(mesh_3d) + recti_mesh_filter = openmc.MeshFilter(recti_mesh) meshsurf_1d_filter = openmc.MeshSurfaceFilter(mesh_1d) meshsurf_2d_filter = openmc.MeshSurfaceFilter(mesh_2d) meshsurf_3d_filter = openmc.MeshSurfaceFilter(mesh_3d) + recti_meshsurf_filter = openmc.MeshSurfaceFilter(recti_mesh) # Initialized the tallies tally = openmc.Tally(name='tally 1') @@ -65,6 +71,16 @@ class FilterMeshTestHarness(HashedPyAPITestHarness): tally.scores = ['current'] self._model.tallies.append(tally) + tally = openmc.Tally(name='tally 7') + tally.filters = [recti_mesh_filter] + tally.scores = ['total'] + self._model.tallies.append(tally) + + tally = openmc.Tally(name='tally 8') + tally.filters = [recti_meshsurf_filter] + tally.scores = ['current'] + self._model.tallies.append(tally) + def test_filter_mesh(): harness = FilterMeshTestHarness('statepoint.10.h5') diff --git a/tests/regression_tests/lattice_hex_coincident/inputs_true.dat b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat index 8d73f63741..0388a4f555 100644 --- a/tests/regression_tests/lattice_hex_coincident/inputs_true.dat +++ b/tests/regression_tests/lattice_hex_coincident/inputs_true.dat @@ -39,7 +39,7 @@ - + @@ -67,8 +67,8 @@ eigenvalue 1000 - 10 - 5 + 5 + 2 -0.9899494936611666 -0.9899494936611666 0.0 0.9899494936611666 0.9899494936611666 10.0 diff --git a/tests/regression_tests/lattice_hex_coincident/results_true.dat b/tests/regression_tests/lattice_hex_coincident/results_true.dat index ba047f6740..1f95edaf8d 100644 --- a/tests/regression_tests/lattice_hex_coincident/results_true.dat +++ b/tests/regression_tests/lattice_hex_coincident/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.741370E+00 1.384609E-03 +1.910374E+00 2.685991E-02 diff --git a/tests/regression_tests/lattice_hex_coincident/test.py b/tests/regression_tests/lattice_hex_coincident/test.py index 65786e2ecd..eaea74b193 100644 --- a/tests/regression_tests/lattice_hex_coincident/test.py +++ b/tests/regression_tests/lattice_hex_coincident/test.py @@ -1,4 +1,5 @@ -import numpy as np +from math import sqrt + import openmc from tests.testing_harness import PyAPITestHarness @@ -13,7 +14,7 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): materials.append(fuel_mat) matrix = openmc.Material() - matrix.set_density('atom/b-cm', 8.7742E-02) + matrix.set_density('atom/b-cm', 1.7742E-02) matrix.add_element('C', 1.0, 'ao') matrix.add_s_alpha_beta('c_Graphite') materials.append(matrix) @@ -94,7 +95,7 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): coolant_univ.add_cells(coolant_channel) half_width = assembly_pitch # cm - edge_length = (2./np.sqrt(3.0)) * half_width + edge_length = (2./sqrt(3.0)) * half_width inf_mat = openmc.Cell() inf_mat.fill = matrix @@ -132,19 +133,19 @@ class HexLatticeCoincidentTestHarness(PyAPITestHarness): settings.run_mode = 'eigenvalue' source = openmc.Source() - corner_dist = np.sqrt(2) * pin_rad + corner_dist = sqrt(2) * pin_rad ll = [-corner_dist, -corner_dist, 0.0] ur = [corner_dist, corner_dist, 10.0] source.space = openmc.stats.Box(ll, ur) source.strength = 1.0 settings.source = source settings.output = {'summary' : False} - settings.batches = 10 - settings.inactive = 5 + settings.batches = 5 + settings.inactive = 2 settings.particles = 1000 settings.seed = 22 settings.export_to_xml() def test_lattice_hex_coincident_surf(): - harness = HexLatticeCoincidentTestHarness('statepoint.10.h5') + harness = HexLatticeCoincidentTestHarness('statepoint.5.h5') harness.main() diff --git a/tests/regression_tests/lattice_hex_x/__init__.py b/tests/regression_tests/lattice_hex_x/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/lattice_hex_x/inputs_true.dat b/tests/regression_tests/lattice_hex_x/inputs_true.dat new file mode 100644 index 0000000000..b528a97b7c --- /dev/null +++ b/tests/regression_tests/lattice_hex_x/inputs_true.dat @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + 1.235 5.0 + 4 +
0.0 0.0 5.0
+ + 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 1 3 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 3 1 1 1 1 3 1 1 1 3 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 +1 1 1 1 1 3 1 1 1 1 2 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 3 1 1 1 3 1 1 1 1 3 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 3 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 1 3 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 3 1 1 1 1 3 1 1 1 3 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 +1 1 1 1 1 3 1 1 1 1 2 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 3 1 1 1 3 1 1 1 1 3 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 3 1 1 1 1 3 1 1 1 1 1 + 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 1 + 1 1 1 1 1 1 1 1 1 1 1 +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 1000 + 10 + 5 + + + -13.62546635287517 -13.62546635287517 0.0 13.62546635287517 13.62546635287517 10.0 + + + 22 + diff --git a/tests/regression_tests/lattice_hex_x/results_true.dat b/tests/regression_tests/lattice_hex_x/results_true.dat new file mode 100644 index 0000000000..a742aa0d54 --- /dev/null +++ b/tests/regression_tests/lattice_hex_x/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +1.355663E+00 2.896562E-02 diff --git a/tests/regression_tests/lattice_hex_x/test.py b/tests/regression_tests/lattice_hex_x/test.py new file mode 100644 index 0000000000..538f790dac --- /dev/null +++ b/tests/regression_tests/lattice_hex_x/test.py @@ -0,0 +1,206 @@ +from tests.testing_harness import PyAPITestHarness +import openmc +import numpy as np + + +class HexLatticeOXTestHarness(PyAPITestHarness): + + def _build_inputs(self): + materials = openmc.Materials() + + fuel_mat = openmc.Material(material_id=1, name="UO2") + fuel_mat.set_density('sum') + fuel_mat.add_nuclide('U235', 0.87370e-03) + fuel_mat.add_nuclide('U238', 1.87440e-02) + fuel_mat.add_nuclide('O16', 3.92350e-02) + materials.append(fuel_mat) + + coolant = openmc.Material(material_id=2, name="borated H2O") + coolant.set_density('sum') + coolant.add_nuclide('H1', 0.06694) + coolant.add_nuclide('O16', 0.03347) + coolant.add_nuclide('B10', 6.6262e-6) + coolant.add_nuclide('B11', 2.6839e-5) + materials.append(coolant) + + absorber = openmc.Material(material_id=3, name="pellet B4C") + absorber.set_density('sum') + absorber.add_nuclide('C0', 0.01966) + absorber.add_nuclide('B11', 4.7344e-6) + absorber.add_nuclide('B10', 1.9177e-5) + materials.append(absorber) + + zirc = openmc.Material(material_id=4, name="Zirc4") + zirc.set_density('sum') + zirc.add_element('Zr', 4.23e-2) + materials.append(zirc) + + materials.export_to_xml() + + # Geometry # + + pin_rad = 0.7 # cm + assembly_pitch = 1.235 # cm + hexagonal_pitch = 23.6 # cm + length = 10.0 # cm + + # Fuel pin surfaces + + cylfuelin = openmc.ZCylinder(surface_id=1, r=0.386) + cylfuelout = openmc.ZCylinder(surface_id=2, r=0.4582) + + # Fuel cells + + infcell = openmc.Cell(cell_id=1) + infcell.region = -cylfuelin + infcell.fill = fuel_mat + + clfcell = openmc.Cell(cell_id=2) + clfcell.region = -cylfuelout & +cylfuelin + clfcell.fill = zirc + + outfcell = openmc.Cell(cell_id=3) + outfcell.region = +cylfuelout + outfcell.fill = coolant + + # Fuel universe + + fuel_ch_univ = openmc.Universe(universe_id=1, name="Fuel channel", + cells=[infcell, clfcell, outfcell]) + + # Central tube surfaces + + cyltubein = openmc.ZCylinder(surface_id=3, r=0.45) + cyltubeout = openmc.ZCylinder(surface_id=4, r=0.5177) + + # Central tube cells + + inctcell = openmc.Cell(cell_id=4) + inctcell.region = -cyltubein + inctcell.fill = coolant + + clctcell = openmc.Cell(cell_id=5) + clctcell.region = -cyltubeout & +cyltubein + clctcell.fill = zirc + + outctcell = openmc.Cell(cell_id=6) + outctcell.region = +cyltubeout + outctcell.fill = coolant + + # Central tubel universe + + tube_ch_univ = openmc.Universe(universe_id=2, + name="Central tube channel", + cells=[inctcell, clctcell, outctcell]) + + # Absorber tube surfaces + + cylabsin = openmc.ZCylinder(surface_id=5, r=0.35) + cylabsout = openmc.ZCylinder(surface_id=6, r=0.41) + cylabsclin = openmc.ZCylinder(surface_id=7, r=0.545) + cylabsclout = openmc.ZCylinder(surface_id=8, r=0.6323) + + # Absorber tube cells + + inabscell = openmc.Cell(cell_id=7) + inabscell.region = -cylabsin + inabscell.fill = absorber + + clabscell = openmc.Cell(cell_id=8) + clabscell.region = -cylabsout & +cylabsin + clabscell.fill = zirc + + interabscell = openmc.Cell(cell_id=9) + interabscell.region = -cylabsclin & +cylabsout + interabscell.fill = coolant + + clatcell = openmc.Cell(cell_id=10) + clatcell.region = -cylabsclout & +cylabsclin + clatcell.fill = zirc + + outabscell = openmc.Cell(cell_id=11) + outabscell.region = +cylabsclout + outabscell.fill = coolant + + # Absorber tube universe + + abs_ch_univ = openmc.Universe(universe_id=3, + name="Central tube channel", + cells=[inabscell, clabscell, + interabscell, + clatcell, outabscell]) + # Assembly surfaces + + edge_length = (1./np.sqrt(3.0)) * hexagonal_pitch + fuel_bottom = openmc.ZPlane(surface_id=9, z0=0.0, + boundary_type='reflective') + fuel_top = openmc.ZPlane(surface_id=10, z0=length, + boundary_type='reflective') + + # a hex surface for the core to go inside of + + hexprism = openmc.model.get_hexagonal_prism(edge_length=edge_length, + origin=(0.0, 0.0), + boundary_type='reflective', + orientation='x') + region = hexprism & +fuel_bottom & -fuel_top + + inf_mat = openmc.Cell(cell_id=12) + inf_mat.fill = coolant + inf_mat_univ = openmc.Universe(universe_id=4, cells=[inf_mat]) + + # Fill lattice by channels + + nring = 11 + universes = [] + for ring in range(nring - 1, -1, -1): + arr = [] + arr.append(fuel_ch_univ) + for cell in range(ring * 6 - 1): + arr.append(fuel_ch_univ) + universes.append(arr) + universes[-1] = [tube_ch_univ] + channels = [(7, 2), (7, 5), (7, 8), (7, 11), (7, 14), (7, 17), (5, 0), + (4, 3), (5, 5), (4, 9), (5, 10), (4, 15), (5, 15), + (4, 21), (5, 20), (4, 27), (5, 25), (4, 33)] + for i, j in channels: + universes[i][j] = abs_ch_univ + lattice = openmc.HexLattice(name="regular fuel assembly") + lattice.orientation = "x" + lattice.center = (0., 0., length/2.0) + lattice.pitch = (assembly_pitch, length/2.0) + lattice.universes = 2*[universes] + lattice.outer = inf_mat_univ + + assembly_cell = openmc.Cell(cell_id=13, + name="container assembly cell") + assembly_cell.region = region + assembly_cell.fill = lattice + + root_univ = openmc.Universe(universe_id=5, name="root universe", + cells=[assembly_cell]) + + geom = openmc.Geometry(root_univ) + geom.export_to_xml() + + # Settings # + + settings = openmc.Settings() + settings.run_mode = 'eigenvalue' + + source = openmc.Source() + ll = [-edge_length, -edge_length, 0.0] + ur = [edge_length, edge_length, 10.0] + source.space = openmc.stats.Box(ll, ur) + source.strength = 1.0 + settings.source = source + settings.batches = 10 + settings.inactive = 5 + settings.particles = 1000 + settings.seed = 22 + settings.export_to_xml() + + +def test_lattice_hex_ox_surf(): + harness = HexLatticeOXTestHarness('statepoint.10.h5') + harness.main() diff --git a/tests/regression_tests/mg_tallies/inputs_true.dat b/tests/regression_tests/mg_tallies/inputs_true.dat index b75573b0a7..5629587222 100644 --- a/tests/regression_tests/mg_tallies/inputs_true.dat +++ b/tests/regression_tests/mg_tallies/inputs_true.dat @@ -33,7 +33,7 @@
- + 10 1 1 0.0 0.0 0.0 929.45 1000 1000 diff --git a/tests/regression_tests/mg_tallies/test.py b/tests/regression_tests/mg_tallies/test.py index b0a8987108..e1c19e6b7e 100644 --- a/tests/regression_tests/mg_tallies/test.py +++ b/tests/regression_tests/mg_tallies/test.py @@ -62,8 +62,7 @@ def test_mg_tallies(): model = slab_mg() # Instantiate a tally mesh - mesh = openmc.Mesh(mesh_id=1) - mesh.type = 'regular' + mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [10, 1, 1] mesh.lower_left = [0.0, 0.0, 0.0] mesh.upper_right = [929.45, 1000, 1000] diff --git a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat index 299da0713c..4f138b24f6 100644 --- a/tests/regression_tests/mgxs_library_mesh/inputs_true.dat +++ b/tests/regression_tests/mgxs_library_mesh/inputs_true.dat @@ -309,7 +309,7 @@ - + 2 2 -100.0 -100.0 100.0 100.0 diff --git a/tests/regression_tests/mgxs_library_mesh/test.py b/tests/regression_tests/mgxs_library_mesh/test.py index a9d24da934..b72fdf2f1e 100644 --- a/tests/regression_tests/mgxs_library_mesh/test.py +++ b/tests/regression_tests/mgxs_library_mesh/test.py @@ -27,8 +27,7 @@ class MGXSTestHarness(PyAPITestHarness): self.mgxs_lib.domain_type = 'mesh' # Instantiate a tally mesh - mesh = openmc.Mesh(mesh_id=1) - mesh.type = 'regular' + mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [2, 2] mesh.lower_left = [-100., -100.] mesh.width = [100., 100.] diff --git a/tests/regression_tests/plot/plots.xml b/tests/regression_tests/plot/plots.xml index ecfe69125b..ce63da1442 100644 --- a/tests/regression_tests/plot/plots.xml +++ b/tests/regression_tests/plot/plots.xml @@ -14,6 +14,7 @@ 25 25 200 200 + diff --git a/tests/regression_tests/plot/results_true.dat b/tests/regression_tests/plot/results_true.dat index 18251e8d0c..db6beb9cdb 100644 --- a/tests/regression_tests/plot/results_true.dat +++ b/tests/regression_tests/plot/results_true.dat @@ -1 +1 @@ -6b3eb36488b995b42233e6b7c0164cf6c92a1823b3c91985b97fd076f86c0aad93fbdb74e70026f5f12c61a6aee52a09588171a7fd839511ee7d9efc3952beb2 \ No newline at end of file +fd61f6a5de632f06a39e2a938480ba3dc314810655e684291e4b255bb8c142a16f10fec414c587ecd727fa559d3760d6e5b43f53fe86e90238a69d9c5698db8e \ No newline at end of file diff --git a/tests/regression_tests/plot/tallies.xml b/tests/regression_tests/plot/tallies.xml new file mode 100644 index 0000000000..b7e678ca0f --- /dev/null +++ b/tests/regression_tests/plot/tallies.xml @@ -0,0 +1,20 @@ + + + + + -10 10 + -10 10 + -10 0 5 7.5 8.75 10 + + + + mesh + 2 + + + + 1 + total + + + diff --git a/tests/regression_tests/plot_overlaps/__init__.py b/tests/regression_tests/plot_overlaps/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/regression_tests/plot_overlaps/geometry.xml b/tests/regression_tests/plot_overlaps/geometry.xml new file mode 100644 index 0000000000..7a9f1fb41f --- /dev/null +++ b/tests/regression_tests/plot_overlaps/geometry.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/tests/regression_tests/plot_overlaps/materials.xml b/tests/regression_tests/plot_overlaps/materials.xml new file mode 100644 index 0000000000..90b3542675 --- /dev/null +++ b/tests/regression_tests/plot_overlaps/materials.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/tests/regression_tests/plot_overlaps/plots.xml b/tests/regression_tests/plot_overlaps/plots.xml new file mode 100644 index 0000000000..28064f58fb --- /dev/null +++ b/tests/regression_tests/plot_overlaps/plots.xml @@ -0,0 +1,35 @@ + + + + + 0. 0. 0. + 25 25 + 200 200 + + + true + + + + 0. 0. 0. + 25 25 + 200 200 + + true + 255 211 0 + + + + 0. 0. 0. + 25 25 + 200 200 + 0 0 0 + + + + 100 100 10 + 0. 0. 0. + 20 20 10 + + + diff --git a/tests/regression_tests/plot_overlaps/results_true.dat b/tests/regression_tests/plot_overlaps/results_true.dat new file mode 100644 index 0000000000..a2afd4c35a --- /dev/null +++ b/tests/regression_tests/plot_overlaps/results_true.dat @@ -0,0 +1 @@ +e67f7737b49af347a617bfdeebebc3288bd85050f33c9208403f3dfb4625a42f63f3396ecb2517ed9214c3e4e68c9038a8f9a7b3e27a414565c5580827dbb6e6 \ No newline at end of file diff --git a/tests/regression_tests/plot_overlaps/settings.xml b/tests/regression_tests/plot_overlaps/settings.xml new file mode 100644 index 0000000000..adf256d2d4 --- /dev/null +++ b/tests/regression_tests/plot_overlaps/settings.xml @@ -0,0 +1,13 @@ + + + + plot + + + 5 4 3 + -10 -10 -10 + 10 10 10 + + 1 + + diff --git a/tests/regression_tests/plot_overlaps/test.py b/tests/regression_tests/plot_overlaps/test.py new file mode 100644 index 0000000000..d8c0943cf5 --- /dev/null +++ b/tests/regression_tests/plot_overlaps/test.py @@ -0,0 +1,61 @@ +import glob +import hashlib +import os + +import h5py +import openmc + +from tests.testing_harness import TestHarness +from tests.regression_tests import config + + +class PlotTestHarness(TestHarness): + """Specialized TestHarness for running OpenMC plotting tests.""" + def __init__(self, plot_names): + super().__init__(None) + self._plot_names = plot_names + + def _run_openmc(self): + openmc.plot_geometry(openmc_exec=config['exe']) + + def _test_output_created(self): + """Make sure *.ppm has been created.""" + for fname in self._plot_names: + assert os.path.exists(fname), 'Plot output file does not exist.' + + def _cleanup(self): + super()._cleanup() + for fname in self._plot_names: + if os.path.exists(fname): + os.remove(fname) + + def _get_results(self): + """Return a string hash of the plot files.""" + outstr = bytes() + + for fname in self._plot_names: + if fname.endswith('.ppm'): + # Add PPM output to results + with open(fname, 'rb') as fh: + outstr += fh.read() + elif fname.endswith('.h5'): + # Add voxel data to results + with h5py.File(fname, 'r') as fh: + outstr += fh.attrs['filetype'] + outstr += fh.attrs['num_voxels'].tostring() + outstr += fh.attrs['lower_left'].tostring() + outstr += fh.attrs['voxel_width'].tostring() + outstr += fh['data'].value.tostring() + + # Hash the information and return. + sha512 = hashlib.sha512() + sha512.update(outstr) + outstr = sha512.hexdigest() + + return outstr + + +def test_plot_overlap(): + harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm', + 'plot_4.h5')) + harness.main() diff --git a/tests/regression_tests/salphabeta/inputs_true.dat b/tests/regression_tests/salphabeta/inputs_true.dat index ede96d3769..29d8065cf1 100644 --- a/tests/regression_tests/salphabeta/inputs_true.dat +++ b/tests/regression_tests/salphabeta/inputs_true.dat @@ -49,7 +49,7 @@ eigenvalue - 1000 + 400 5 0 diff --git a/tests/regression_tests/salphabeta/results_true.dat b/tests/regression_tests/salphabeta/results_true.dat index e1121bce50..708e6e7269 100644 --- a/tests/regression_tests/salphabeta/results_true.dat +++ b/tests/regression_tests/salphabeta/results_true.dat @@ -1,2 +1,2 @@ k-combined: -8.403447E-01 2.461538E-02 +8.474822E-01 1.767966E-02 diff --git a/tests/regression_tests/salphabeta/test.py b/tests/regression_tests/salphabeta/test.py index e69ada5ba3..ab5305126e 100644 --- a/tests/regression_tests/salphabeta/test.py +++ b/tests/regression_tests/salphabeta/test.py @@ -66,7 +66,7 @@ def make_model(): # Settings model.settings.batches = 5 model.settings.inactive = 0 - model.settings.particles = 1000 + model.settings.particles = 400 model.settings.source = openmc.Source(space=openmc.stats.Box( [-4, -4, -4], [4, 4, 4])) diff --git a/tests/regression_tests/sourcepoint_batch/results_true.dat b/tests/regression_tests/sourcepoint_batch/results_true.dat index 8b8dd98f2b..a9843df530 100644 --- a/tests/regression_tests/sourcepoint_batch/results_true.dat +++ b/tests/regression_tests/sourcepoint_batch/results_true.dat @@ -1,3 +1,3 @@ k-combined: -0.000000E+00 0.000000E+00 +2.976389E-01 3.770725E-03 1.892327E+00 -3.385257E+00 6.702632E-01 diff --git a/tests/regression_tests/tallies/inputs_true.dat b/tests/regression_tests/tallies/inputs_true.dat index 3ec8d8d4ce..9409ca4a71 100644 --- a/tests/regression_tests/tallies/inputs_true.dat +++ b/tests/regression_tests/tallies/inputs_true.dat @@ -309,7 +309,7 @@ - + 2 2 -182.07 -182.07 182.07 182.07 diff --git a/tests/regression_tests/tallies/test.py b/tests/regression_tests/tallies/test.py index 543d0acd21..d4857b3ec7 100644 --- a/tests/regression_tests/tallies/test.py +++ b/tests/regression_tests/tallies/test.py @@ -1,6 +1,6 @@ from openmc.filter import * from openmc.filter_expansion import * -from openmc import Mesh, Tally +from openmc import RegularMesh, Tally from tests.testing_harness import HashedPyAPITestHarness @@ -28,7 +28,7 @@ def test_tallies(): azimuthal_tally2.scores = ['flux'] azimuthal_tally2.estimator = 'analog' - mesh_2x2 = Mesh(mesh_id=1) + mesh_2x2 = RegularMesh(mesh_id=1) mesh_2x2.lower_left = [-182.07, -182.07] mesh_2x2.upper_right = [182.07, 182.07] mesh_2x2.dimension = [2, 2] diff --git a/tests/regression_tests/tally_arithmetic/inputs_true.dat b/tests/regression_tests/tally_arithmetic/inputs_true.dat index 3605005add..842b0df623 100644 --- a/tests/regression_tests/tally_arithmetic/inputs_true.dat +++ b/tests/regression_tests/tally_arithmetic/inputs_true.dat @@ -309,7 +309,7 @@ - + 2 2 2 -160.0 -160.0 -183.0 160.0 160.0 183.0 diff --git a/tests/regression_tests/tally_arithmetic/test.py b/tests/regression_tests/tally_arithmetic/test.py index 3adf0c5bef..236f5cc384 100644 --- a/tests/regression_tests/tally_arithmetic/test.py +++ b/tests/regression_tests/tally_arithmetic/test.py @@ -10,8 +10,7 @@ class TallyArithmeticTestHarness(PyAPITestHarness): super().__init__(*args, **kwargs) # Initialize Mesh - mesh = openmc.Mesh(mesh_id=1) - mesh.type = 'regular' + mesh = openmc.RegularMesh(mesh_id=1) mesh.dimension = [2, 2, 2] mesh.lower_left = [-160.0, -160.0, -183.0] mesh.upper_right = [160.0, 160.0, 183.0] diff --git a/tests/regression_tests/tally_slice_merge/inputs_true.dat b/tests/regression_tests/tally_slice_merge/inputs_true.dat index ccd3655f9e..6f421b1d95 100644 --- a/tests/regression_tests/tally_slice_merge/inputs_true.dat +++ b/tests/regression_tests/tally_slice_merge/inputs_true.dat @@ -310,7 +310,7 @@ - + 2 2 -50.0 -50.0 50.0 50.0 diff --git a/tests/regression_tests/tally_slice_merge/test.py b/tests/regression_tests/tally_slice_merge/test.py index 20ab57a077..090e314483 100644 --- a/tests/regression_tests/tally_slice_merge/test.py +++ b/tests/regression_tests/tally_slice_merge/test.py @@ -24,8 +24,7 @@ class TallySliceMergeTestHarness(PyAPITestHarness): cell_27 = openmc.CellFilter(27) distribcell_filter = openmc.DistribcellFilter(21) - mesh = openmc.Mesh(name='mesh') - mesh.type = 'regular' + mesh = openmc.RegularMesh(name='mesh') mesh.dimension = [2, 2] mesh.lower_left = [-50., -50.] mesh.upper_right = [+50., +50.] diff --git a/tests/regression_tests/triso/results_true.dat b/tests/regression_tests/triso/results_true.dat index eb06a771c9..c8e6832345 100644 --- a/tests/regression_tests/triso/results_true.dat +++ b/tests/regression_tests/triso/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.707485E+00 9.795497E-02 +1.701412E+00 3.180881E-02 diff --git a/tests/regression_tests/uwuw/results_true.dat b/tests/regression_tests/uwuw/results_true.dat deleted file mode 120000 index 3711895aba..0000000000 --- a/tests/regression_tests/uwuw/results_true.dat +++ /dev/null @@ -1 +0,0 @@ -../dagmc/results_true.dat \ No newline at end of file diff --git a/tests/regression_tests/void/results_true.dat b/tests/regression_tests/void/results_true.dat index 48be2778a4..c0e8184b6f 100644 --- a/tests/regression_tests/void/results_true.dat +++ b/tests/regression_tests/void/results_true.dat @@ -1,2 +1,2 @@ k-combined: -1.062505E+00 2.674375E-02 +9.612556E-01 1.990135E-02 diff --git a/tests/unit_tests/test_capi.py b/tests/unit_tests/test_capi.py index 07fef85166..53ad09e906 100644 --- a/tests/unit_tests/test_capi.py +++ b/tests/unit_tests/test_capi.py @@ -120,6 +120,16 @@ def test_material(capi_init): m.set_density(rho) assert sum(m.densities) == pytest.approx(rho) + m.set_density(0.1, 'g/cm3') + assert m.density == pytest.approx(0.1) + + +def test_material_add_nuclide(capi_init): + m = openmc.capi.materials[3] + m.add_nuclide('Xe135', 1e-12) + assert m.nuclides[-1] == 'Xe135' + assert m.densities[-1] == 1e-12 + def test_new_material(capi_init): with pytest.raises(exc.AllocationError): @@ -132,7 +142,7 @@ def test_new_material(capi_init): def test_nuclide_mapping(capi_init): nucs = openmc.capi.nuclides assert isinstance(nucs, Mapping) - assert len(nucs) == 12 + assert len(nucs) == 13 for name, nuc in nucs.items(): assert isinstance(nuc, openmc.capi.Nuclide) assert name == nuc.name @@ -333,11 +343,11 @@ def test_find_material(capi_init): def test_mesh(capi_init): - mesh = openmc.capi.Mesh() + mesh = openmc.capi.RegularMesh() mesh.dimension = (2, 3, 4) assert mesh.dimension == (2, 3, 4) with pytest.raises(exc.AllocationError): - mesh2 = openmc.capi.Mesh(mesh.id) + mesh2 = openmc.capi.RegularMesh(mesh.id) # Make sure each combination of parameters works ll = (0., 0., 0.) @@ -357,7 +367,7 @@ def test_mesh(capi_init): assert isinstance(meshes, Mapping) assert len(meshes) == 1 for mesh_id, mesh in meshes.items(): - assert isinstance(mesh, openmc.capi.Mesh) + assert isinstance(mesh, openmc.capi.RegularMesh) assert mesh_id == mesh.id mf = openmc.capi.MeshFilter(mesh) diff --git a/tests/unit_tests/test_data_misc.py b/tests/unit_tests/test_data_misc.py index ea20ba53c8..2585e9315f 100644 --- a/tests/unit_tests/test_data_misc.py +++ b/tests/unit_tests/test_data_misc.py @@ -2,6 +2,7 @@ from collections.abc import Mapping import os +from pathlib import Path import numpy as np import pytest @@ -33,6 +34,30 @@ def test_data_library(tmpdir): assert new_lib.libraries[-1]['type'] == 'thermal' +def test_depletion_chain_data_library(run_in_tmpdir): + dep_lib = openmc.data.DataLibrary.from_xml() + prev_len = len(dep_lib.libraries) + chain_path = Path(__file__).parents[1] / "chain_simple.xml" + dep_lib.register_file(chain_path) + assert len(dep_lib.libraries) == prev_len + 1 + # Inspect + dep_dict = dep_lib.libraries[-1] + assert dep_dict['materials'] == [] + assert dep_dict['type'] == 'depletion_chain' + assert dep_dict['path'] == str(chain_path) + + out_path = "cross_section_chain.xml" + dep_lib.export_to_xml(out_path) + + dep_import = openmc.data.DataLibrary.from_xml(out_path) + for lib in reversed(dep_import.libraries): + if lib['type'] == 'depletion_chain': + break + else: + raise IndexError("depletion_chain not found in exported DataLibrary") + assert os.path.exists(lib['path']) + + def test_linearize(): """Test linearization of a continuous function.""" x, y = openmc.data.linearize([-1., 1.], lambda x: 1 - x*x) diff --git a/tests/unit_tests/test_deplete_operator.py b/tests/unit_tests/test_deplete_operator.py new file mode 100644 index 0000000000..757b51d923 --- /dev/null +++ b/tests/unit_tests/test_deplete_operator.py @@ -0,0 +1,70 @@ +"""Basic unit tests for openmc.deplete.Operator instantiation + +Modifies and resets environment variable OPENMC_CROSS_SECTIONS +to a custom file with new depletion_chain node +""" + +from os import environ +from unittest import mock +from pathlib import Path + +import pytest +from openmc.deplete.abc import TransportOperator +from openmc.deplete.chain import Chain + +BARE_XS_FILE = "bare_cross_sections.xml" +CHAIN_PATH = Path(__file__).parents[1] / "chain_simple.xml" + + +@pytest.fixture() +def bare_xs(run_in_tmpdir): + """Create a very basic cross_sections file, return simple Chain. + + """ + + bare_xs_contents = """ + + + +""".format(CHAIN_PATH) + + with open(BARE_XS_FILE, "w") as out: + out.write(bare_xs_contents) + + yield + + +class BareDepleteOperator(TransportOperator): + """Very basic class for testing the initialization.""" + + # declare abstract methods so object can be created + def __call__(self, *args, **kwargs): + pass + + def initial_condition(self): + pass + + def get_results_info(self): + pass + + +@mock.patch.dict(environ, {"OPENMC_CROSS_SECTIONS": BARE_XS_FILE}) +def test_operator_init(bare_xs): + """The test will set and unset environment variable OPENMC_CROSS_SECTIONS + to point towards a temporary dummy file. This file will be removed + at the end of the test, and only contains a + depletion_chain node.""" + # force operator to read from OPENMC_CROSS_SECTIONS + bare_op = BareDepleteOperator(chain_file=None) + act_chain = bare_op.chain + ref_chain = Chain.from_xml(CHAIN_PATH) + assert len(act_chain) == len(ref_chain) + for name in ref_chain.nuclide_dict: + # compare openmc.deplete.Nuclide objects + ref_nuc = ref_chain[name] + act_nuc = act_chain[name] + for prop in [ + 'name', 'half_life', 'decay_energy', 'reactions', + 'decay_modes', 'yield_data', 'yield_energies', + ]: + assert getattr(act_nuc, prop) == getattr(ref_nuc, prop), prop diff --git a/tests/unit_tests/test_lattice.py b/tests/unit_tests/test_lattice.py index fe86e2a784..c4a3517769 100644 --- a/tests/unit_tests/test_lattice.py +++ b/tests/unit_tests/test_lattice.py @@ -202,6 +202,13 @@ def test_get_universe(rlat2, rlat3, hlat2, hlat3): assert hlat2.get_universe((1, 0)) == u1 assert hlat2.get_universe((-2, 2)) == u1 + hlat2.orientation = 'x' + assert hlat2.get_universe((2, 0)) == u2 + assert hlat2.get_universe((1, 0)) == u2 + assert hlat2.get_universe((1, 1)) == u1 + assert hlat2.get_universe((-1, 1)) == u1 + hlat2.orientation = 'y' + u1, u2, u3, outer = hlat3.univs assert hlat3.get_universe((0, 0, 0)) == u2 assert hlat3.get_universe((0, 0, 1)) == u3 @@ -342,3 +349,5 @@ def test_show_indices(): for i in range(1, 11): lines = openmc.HexLattice.show_indices(i).split('\n') assert len(lines) == 4*i - 3 + lines_x = openmc.HexLattice.show_indices(i, 'x').split('\n') + assert len(lines) == 4*i - 3 diff --git a/tests/unit_tests/test_mesh_from_lattice.py b/tests/unit_tests/test_mesh_from_lattice.py index feac873260..0a01387cde 100644 --- a/tests/unit_tests/test_mesh_from_lattice.py +++ b/tests/unit_tests/test_mesh_from_lattice.py @@ -90,12 +90,12 @@ def test_mesh2d(rlat2): shape = np.array(rlat2.shape) width = shape*rlat2.pitch - mesh1 = openmc.Mesh.from_rect_lattice(rlat2) + mesh1 = openmc.RegularMesh.from_rect_lattice(rlat2) assert np.array_equal(mesh1.dimension, (3, 3)) assert np.array_equal(mesh1.lower_left, rlat2.lower_left) assert np.array_equal(mesh1.upper_right, rlat2.lower_left + width) - mesh2 = openmc.Mesh.from_rect_lattice(rlat2, division=3) + mesh2 = openmc.RegularMesh.from_rect_lattice(rlat2, division=3) assert np.array_equal(mesh2.dimension, (9, 9)) assert np.array_equal(mesh2.lower_left, rlat2.lower_left) assert np.array_equal(mesh2.upper_right, rlat2.lower_left + width) @@ -105,12 +105,12 @@ def test_mesh3d(rlat3): shape = np.array(rlat3.shape) width = shape*rlat3.pitch - mesh1 = openmc.Mesh.from_rect_lattice(rlat3) + mesh1 = openmc.RegularMesh.from_rect_lattice(rlat3) assert np.array_equal(mesh1.dimension, (3, 3, 2)) assert np.array_equal(mesh1.lower_left, rlat3.lower_left) assert np.array_equal(mesh1.upper_right, rlat3.lower_left + width) - mesh2 = openmc.Mesh.from_rect_lattice(rlat3, division=3) + mesh2 = openmc.RegularMesh.from_rect_lattice(rlat3, division=3) assert np.array_equal(mesh2.dimension, (9, 9, 6)) assert np.array_equal(mesh2.lower_left, rlat3.lower_left) assert np.array_equal(mesh2.upper_right, rlat3.lower_left + width) diff --git a/tests/unit_tests/test_plots.py b/tests/unit_tests/test_plots.py index 840e7e635f..f2d3e10148 100644 --- a/tests/unit_tests/test_plots.py +++ b/tests/unit_tests/test_plots.py @@ -24,6 +24,10 @@ def myplot(): plot.mask_background = (255, 255, 255) plot.mask_background = 'white' + plot.overlap_color = (255, 211, 0) + plot.overlap_color = 'yellow' + plot.show_overlaps = True + plot.level = 1 plot.meshlines = { 'type': 'tally', diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index e42f6240f5..9c33f6a2d4 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -19,12 +19,13 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.confidence_intervals = True - s.cross_sections = '/path/to/cross_sections.xml' s.ptables = True s.seed = 17 s.survival_biasing = True - s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy': 1.0e-5} - mesh = openmc.Mesh() + s.cutoff = {'weight': 0.25, 'weight_avg': 0.5, 'energy_neutron': 1.0e-5, + 'energy_photon': 1000.0, 'energy_electron': 1.0e-5, + 'energy_positron': 1.0e-5} + mesh = openmc.RegularMesh() mesh.lower_left = (-10., -10., -10.) mesh.upper_right = (10., 10., 10.) mesh.dimension = (5, 5, 5) @@ -47,6 +48,59 @@ def test_export_to_xml(run_in_tmpdir): upper_right = (10., 10., 10.)) s.create_fission_neutrons = True s.log_grid_bins = 2000 + s.photon_transport = False + s.electron_treatment = 'led' + s.dagmc = False # Make sure exporting XML works s.export_to_xml() + + # Generate settings from XML + s = openmc.Settings.from_xml() + assert s.run_mode == 'fixed source' + assert s.batches == 1000 + assert s.generations_per_batch == 10 + assert s.inactive == 100 + assert s.particles == 1000000 + assert s.keff_trigger == {'type': 'std_dev', 'threshold': 0.001} + assert s.energy_mode == 'continuous-energy' + assert s.max_order == 5 + assert isinstance(s.source[0], openmc.Source) + assert isinstance(s.source[0].space, openmc.stats.Point) + assert s.output == {'summary': True, 'tallies': False, 'path': 'here'} + assert s.verbosity == 7 + assert s.sourcepoint == {'batches': [50, 150, 500, 1000], 'separate': True, + 'write': True, 'overwrite': True} + assert s.statepoint == {'batches': [50, 150, 500, 1000]} + assert s.confidence_intervals + assert s.ptables + assert s.seed == 17 + assert s.survival_biasing + assert s.cutoff == {'weight': 0.25, 'weight_avg': 0.5, + 'energy_neutron': 1.0e-5, 'energy_photon': 1000.0, + 'energy_electron': 1.0e-5, 'energy_positron': 1.0e-5} + assert isinstance(s.entropy_mesh, openmc.RegularMesh) + assert s.entropy_mesh.lower_left == [-10., -10., -10.] + assert s.entropy_mesh.upper_right == [10., 10., 10.] + assert s.entropy_mesh.dimension == [5, 5, 5] + assert s.trigger_active + assert s.trigger_max_batches == 10000 + assert s.trigger_batch_interval == 50 + assert not s.no_reduce + assert s.tabular_legendre == {'enable': True, 'num_points': 50} + assert s.temperature == {'default': 293.6, 'method': 'interpolation', + 'multipole': True, 'range': [200., 1000.]} + assert s.trace == [10, 1, 20] + assert s.track == [1, 1, 1, 2, 1, 1] + assert isinstance(s.ufs_mesh, openmc.RegularMesh) + assert s.ufs_mesh.lower_left == [-10., -10., -10.] + assert s.ufs_mesh.upper_right == [10., 10., 10.] + assert s.ufs_mesh.dimension == [5, 5, 5] + assert s.resonance_scattering == {'enable': True, 'method': 'rvs', + 'energy_min': 1.0, 'energy_max': 1000.0, + 'nuclides': ['U235', 'U238', 'Pu239']} + assert s.create_fission_neutrons + assert s.log_grid_bins == 2000 + assert not s.photon_transport + assert s.electron_treatment == 'led' + assert not s.dagmc diff --git a/tests/unit_tests/test_source.py b/tests/unit_tests/test_source.py index 3c963d0527..1c70e159d2 100644 --- a/tests/unit_tests/test_source.py +++ b/tests/unit_tests/test_source.py @@ -11,7 +11,6 @@ def test_source(): assert src.space == space assert src.angle == angle assert src.energy == energy - assert src.strength == 1.0 elem = src.to_xml_element() assert 'strength' in elem.attrib @@ -19,6 +18,13 @@ def test_source(): assert elem.find('angle') is not None assert elem.find('energy') is not None + src = openmc.Source.from_xml_element(elem) + assert isinstance(src.angle, openmc.stats.Isotropic) + assert src.space.xyz == [0.0, 0.0, 0.0] + assert src.energy.x == [1.0e6] + assert src.energy.p == [1.0] + assert src.strength == 1.0 + def test_source_file(): filename = 'source.h5' diff --git a/tests/unit_tests/test_stats.py b/tests/unit_tests/test_stats.py index 553f2410e4..e159584782 100644 --- a/tests/unit_tests/test_stats.py +++ b/tests/unit_tests/test_stats.py @@ -10,10 +10,15 @@ def test_discrete(): x = [0.0, 1.0, 10.0] p = [0.3, 0.2, 0.5] d = openmc.stats.Discrete(x, p) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Discrete.from_xml_element(elem) assert d.x == x assert d.p == p assert len(d) == len(x) - d.to_xml_element('distribution') + + d = openmc.stats.Univariate.from_xml_element(elem) + assert isinstance(d, openmc.stats.Discrete) # Single point d2 = openmc.stats.Discrete(1e6, 1.0) @@ -25,6 +30,9 @@ def test_discrete(): def test_uniform(): a, b = 10.0, 20.0 d = openmc.stats.Uniform(a, b) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Uniform.from_xml_element(elem) assert d.a == a assert d.b == b assert len(d) == 2 @@ -34,35 +42,39 @@ def test_uniform(): assert t.p == [1/(b-a), 1/(b-a)] assert t.interpolation == 'histogram' - d.to_xml_element('distribution') - def test_maxwell(): theta = 1.2895e6 d = openmc.stats.Maxwell(theta) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Maxwell.from_xml_element(elem) assert d.theta == theta assert len(d) == 1 - d.to_xml_element('distribution') def test_watt(): a, b = 0.965e6, 2.29e-6 d = openmc.stats.Watt(a, b) + elem = d.to_xml_element('distribution') + + d = openmc.stats.Watt.from_xml_element(elem) assert d.a == a assert d.b == b assert len(d) == 2 - d.to_xml_element('distribution') def test_tabular(): x = [0.0, 5.0, 7.0] p = [0.1, 0.2, 0.05] d = openmc.stats.Tabular(x, p, 'linear-linear') + elem = d.to_xml_element('distribution') + + d = openmc.stats.Tabular.from_xml_element(elem) assert d.x == x assert d.p == p assert d.interpolation == 'linear-linear' assert len(d) == len(x) - d.to_xml_element('distribution') def test_legendre(): @@ -115,6 +127,15 @@ def test_polar_azimuthal(): assert elem.find('mu') is not None assert elem.find('phi') is not None + d = openmc.stats.PolarAzimuthal.from_xml_element(elem) + assert d.mu.x == [1.] + assert d.mu.p == [1.] + assert d.phi.x == [0.] + assert d.phi.p == [1.] + + d = openmc.stats.UnitSphere.from_xml_element(elem) + assert isinstance(d, openmc.stats.PolarAzimuthal) + def test_isotropic(): d = openmc.stats.Isotropic() @@ -122,24 +143,25 @@ def test_isotropic(): assert elem.tag == 'angle' assert elem.attrib['type'] == 'isotropic' + d = openmc.stats.Isotropic.from_xml_element(elem) + assert isinstance(d, openmc.stats.Isotropic) + def test_monodirectional(): d = openmc.stats.Monodirectional((1., 0., 0.)) - assert d.reference_uvw == pytest.approx((1., 0., 0.)) - elem = d.to_xml_element() assert elem.tag == 'angle' assert elem.attrib['type'] == 'monodirectional' + d = openmc.stats.Monodirectional.from_xml_element(elem) + assert d.reference_uvw == pytest.approx((1., 0., 0.)) + def test_cartesian(): x = openmc.stats.Uniform(-10., 10.) y = openmc.stats.Uniform(-10., 10.) z = openmc.stats.Uniform(0., 20.) d = openmc.stats.CartesianIndependent(x, y, z) - assert d.x == x - assert d.y == y - assert d.z == z elem = d.to_xml_element() assert elem.tag == 'space' @@ -147,55 +169,75 @@ def test_cartesian(): assert elem.find('x') is not None assert elem.find('y') is not None + d = openmc.stats.CartesianIndependent.from_xml_element(elem) + assert d.x == x + assert d.y == y + assert d.z == z + + d = openmc.stats.Spatial.from_xml_element(elem) + assert isinstance(d, openmc.stats.CartesianIndependent) + def test_box(): lower_left = (-10., -10., -10.) upper_right = (10., 10., 10.) d = openmc.stats.Box(lower_left, upper_right) - assert d.lower_left == pytest.approx(lower_left) - assert d.upper_right == pytest.approx(upper_right) - assert not d.only_fissionable elem = d.to_xml_element() assert elem.tag == 'space' assert elem.attrib['type'] == 'box' assert elem.find('parameters') is not None + d = openmc.stats.Box.from_xml_element(elem) + assert d.lower_left == pytest.approx(lower_left) + assert d.upper_right == pytest.approx(upper_right) + assert not d.only_fissionable + # only fissionable parameter d2 = openmc.stats.Box(lower_left, upper_right, True) assert d2.only_fissionable elem = d2.to_xml_element() assert elem.attrib['type'] == 'fission' + d = openmc.stats.Spatial.from_xml_element(elem) + assert isinstance(d, openmc.stats.Box) def test_point(): p = (-4., 2., 10.) d = openmc.stats.Point(p) - assert d.xyz == pytest.approx(p) elem = d.to_xml_element() assert elem.tag == 'space' assert elem.attrib['type'] == 'point' assert elem.find('parameters') is not None + d = openmc.stats.Point.from_xml_element(elem) + assert d.xyz == pytest.approx(p) + def test_normal(): mean = 10.0 std_dev = 2.0 d = openmc.stats.Normal(mean,std_dev) + + elem = d.to_xml_element('distribution') + assert elem.attrib['type'] == 'normal' + + d = openmc.stats.Normal.from_xml_element(elem) assert d.mean_value == pytest.approx(mean) assert d.std_dev == pytest.approx(std_dev) assert len(d) == 2 - elem = d.to_xml_element('distribution') - assert elem.attrib['type'] == 'normal' def test_muir(): mean = 10.0 mass = 5.0 temp = 20000. d = openmc.stats.Muir(mean,mass,temp) + + elem = d.to_xml_element('energy') + assert elem.attrib['type'] == 'muir' + + d = openmc.stats.Muir.from_xml_element(elem) assert d.e0 == pytest.approx(mean) assert d.m_rat == pytest.approx(mass) assert d.kt == pytest.approx(temp) assert len(d) == 3 - elem = d.to_xml_element('energy') - assert elem.attrib['type'] == 'muir' diff --git a/tools/ci/travis-install.py b/tools/ci/travis-install.py index 9c12387fed..9a9b06dae1 100644 --- a/tools/ci/travis-install.py +++ b/tools/ci/travis-install.py @@ -48,6 +48,9 @@ def install(omp=False, mpi=False, phdf5=False, dagmc=False): if dagmc: cmake_cmd.append('-Ddagmc=ON') + # Build in coverage mode for coverage testing + cmake_cmd.append('-Dcoverage=on') + # Build and install cmake_cmd.append('..') print(' '.join(cmake_cmd)) diff --git a/tools/ci/travis-install.sh b/tools/ci/travis-install.sh index 2bca50e4b4..4e32f31cc0 100755 --- a/tools/ci/travis-install.sh +++ b/tools/ci/travis-install.sh @@ -25,5 +25,8 @@ python tools/ci/travis-install.py # Install Python API in editable mode pip install -e .[test,vtk] -# For uploading to coveralls +# For coverage testing of the C++ source files +pip install cpp-coveralls + +# For coverage testing of the Python source files pip install coveralls diff --git a/vendor/gsl/include/gsl/gsl b/vendor/gsl/include/gsl/gsl new file mode 100644 index 0000000000..be3f2ccdb2 --- /dev/null +++ b/vendor/gsl/include/gsl/gsl @@ -0,0 +1,27 @@ +// +// gsl-lite is based on GSL: Guidelines Support Library. +// For more information see https://github.com/martinmoene/gsl-lite +// +// Copyright (c) 2015 Martin Moene +// Copyright (c) 2015 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// 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. + +// mimic MS include hierarchy + +#pragma once + +#ifndef GSL_GSL_H_INCLUDED +#define GSL_GSL_H_INCLUDED + +#include "gsl-lite.hpp" + +#endif // GSL_GSL_H_INCLUDED diff --git a/vendor/gsl/include/gsl/gsl-lite.hpp b/vendor/gsl/include/gsl/gsl-lite.hpp new file mode 100644 index 0000000000..825087cabd --- /dev/null +++ b/vendor/gsl/include/gsl/gsl-lite.hpp @@ -0,0 +1,2857 @@ +// +// gsl-lite is based on GSL: Guidelines Support Library. +// For more information see https://github.com/martinmoene/gsl-lite +// +// Copyright (c) 2015-2018 Martin Moene +// Copyright (c) 2015-2018 Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// 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. + +#pragma once + +#ifndef GSL_GSL_LITE_HPP_INCLUDED +#define GSL_GSL_LITE_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define gsl_lite_MAJOR 0 +#define gsl_lite_MINOR 34 +#define gsl_lite_PATCH 0 + +#define gsl_lite_VERSION gsl_STRINGIFY(gsl_lite_MAJOR) "." gsl_STRINGIFY(gsl_lite_MINOR) "." gsl_STRINGIFY(gsl_lite_PATCH) + +// gsl-lite backward compatibility: + +#ifdef gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR +# define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR +# pragma message ("gsl_CONFIG_ALLOWS_SPAN_CONTAINER_CTOR is deprecated since gsl-lite 0.7.0; replace with gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR, or consider span(with_container, cont).") +#endif + +// M-GSL compatibility: + +#if defined( GSL_THROW_ON_CONTRACT_VIOLATION ) +# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS 1 +#endif + +#if defined( GSL_TERMINATE_ON_CONTRACT_VIOLATION ) +# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS 0 +#endif + +#if defined( GSL_UNENFORCED_ON_CONTRACT_VIOLATION ) +# define gsl_CONFIG_CONTRACT_LEVEL_OFF 1 +#endif + +// Configuration: Features + +#ifndef gsl_FEATURE_WITH_CONTAINER_TO_STD +# define gsl_FEATURE_WITH_CONTAINER_TO_STD 99 +#endif + +#ifndef gsl_FEATURE_MAKE_SPAN_TO_STD +# define gsl_FEATURE_MAKE_SPAN_TO_STD 99 +#endif + +#ifndef gsl_FEATURE_BYTE_SPAN_TO_STD +# define gsl_FEATURE_BYTE_SPAN_TO_STD 99 +#endif + +#ifndef gsl_FEATURE_IMPLICIT_MACRO +# define gsl_FEATURE_IMPLICIT_MACRO 1 +#endif + +#ifndef gsl_FEATURE_OWNER_MACRO +# define gsl_FEATURE_OWNER_MACRO 1 +#endif + +#ifndef gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD +# define gsl_FEATURE_EXPERIMENTAL_RETURN_GUARD 0 +#endif + +// Configuration: Other + +#ifndef gsl_CONFIG_DEPRECATE_TO_LEVEL +# define gsl_CONFIG_DEPRECATE_TO_LEVEL 0 +#endif + +#ifndef gsl_CONFIG_SPAN_INDEX_TYPE +# define gsl_CONFIG_SPAN_INDEX_TYPE size_t +#endif + +#ifndef gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR +# define gsl_CONFIG_NOT_NULL_EXPLICIT_CTOR 0 +#endif + +#ifndef gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF +# define gsl_CONFIG_NOT_NULL_GET_BY_CONST_REF 0 +#endif + +#ifndef gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS +# define gsl_CONFIG_CONFIRMS_COMPILATION_ERRORS 0 +#endif + +#ifndef gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON +# define gsl_CONFIG_ALLOWS_NONSTRICT_SPAN_COMPARISON 1 +#endif + +#ifndef gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR +# define gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR 0 +#endif + +#if defined( gsl_CONFIG_CONTRACT_LEVEL_ON ) +# define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( gsl_CONFIG_CONTRACT_LEVEL_OFF ) +# define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( gsl_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( gsl_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x10 +#else +# define gsl_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#endif + +#if !defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS ) && \ + !defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES ) +# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#elif defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS ) && \ + !defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES ) +# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 1 +#elif !defined( gsl_CONFIG_CONTRACT_VIOLATION_THROWS ) && \ + defined( gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES ) +# define gsl_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#else +# error only one of gsl_CONFIG_CONTRACT_VIOLATION_THROWS and gsl_CONFIG_CONTRACT_VIOLATION_TERMINATES may be defined. +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef gsl_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define gsl_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define gsl_CPLUSPLUS __cplusplus +# endif +#endif + +#define gsl_CPP98_OR_GREATER ( gsl_CPLUSPLUS >= 199711L ) +#define gsl_CPP11_OR_GREATER ( gsl_CPLUSPLUS >= 201103L ) +#define gsl_CPP14_OR_GREATER ( gsl_CPLUSPLUS >= 201402L ) +#define gsl_CPP17_OR_GREATER ( gsl_CPLUSPLUS >= 201703L ) +#define gsl_CPP20_OR_GREATER ( gsl_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define gsl_CPLUSPLUS_V ( gsl_CPLUSPLUS / 100 - (gsl_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +// half-open range [lo..hi): +#define gsl_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define gsl_COMPILER_MSVC_VER (_MSC_VER ) +# define gsl_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define gsl_COMPILER_MSVC_VER 0 +# define gsl_COMPILER_MSVC_VERSION 0 +#endif + +#define gsl_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define gsl_COMPILER_CLANG_VERSION gsl_COMPILER_VERSION( __clang_major__, __clang_minor__, __clang_patchlevel__ ) +#else +# define gsl_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define gsl_COMPILER_GNUC_VERSION gsl_COMPILER_VERSION( __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ ) +#else +# define gsl_COMPILER_GNUC_VERSION 0 +#endif + +// Method enabling (C++98, VC120 (VS2013) cannot use __VA_ARGS__) + +#define gsl_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if::type = 0 > + +#define gsl_REQUIRES_T(VA) \ + , typename = typename std::enable_if< (VA), gsl::detail::enabler >::type + +#define gsl_REQUIRES_R(R, VA) \ + typename std::enable_if::type + +#define gsl_REQUIRES_A(VA) \ + , typename std::enable_if::type = nullptr + +// Compiler non-strict aliasing: + +#if defined(__clang__) || defined(__GNUC__) +# define gsl_may_alias __attribute__((__may_alias__)) +#else +# define gsl_may_alias +#endif + +// Presence of gsl, language and library features: + +#define gsl_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= gsl_CPLUSPLUS_V ) + +#define gsl_DEPRECATE_TO_LEVEL( level ) ( level <= gsl_CONFIG_DEPRECATE_TO_LEVEL ) +#define gsl_FEATURE_TO_STD( feature ) ( gsl_IN_STD( gsl_FEATURE( feature##_TO_STD ) ) ) +#define gsl_FEATURE( feature ) ( gsl_FEATURE_##feature ) +#define gsl_CONFIG( feature ) ( gsl_CONFIG_##feature ) +#define gsl_HAVE( feature ) ( gsl_HAVE_##feature ) + +// Presence of wide character support: + +#ifdef __DJGPP__ +# define gsl_HAVE_WCHAR 0 +#else +# define gsl_HAVE_WCHAR 1 +#endif + +// Presence of language & library features: + +#ifdef _HAS_CPP0X +# define gsl_HAS_CPP0X _HAS_CPP0X +#else +# define gsl_HAS_CPP0X 0 +#endif + +#define gsl_CPP11_100 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1600) +#define gsl_CPP11_110 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1700) +#define gsl_CPP11_120 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1800) +#define gsl_CPP11_140 (gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) + +#define gsl_CPP14_000 (gsl_CPP14_OR_GREATER) +#define gsl_CPP14_120 (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1800) +#define gsl_CPP14_140 (gsl_CPP14_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) + +#define gsl_CPP17_000 (gsl_CPP17_OR_GREATER) +#define gsl_CPP17_140 (gsl_CPP17_OR_GREATER || gsl_COMPILER_MSVC_VER >= 1900) + +#define gsl_CPP11_140_CPP0X_90 (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VER >= 1500 && gsl_HAS_CPP0X)) +#define gsl_CPP11_140_CPP0X_100 (gsl_CPP11_140 || (gsl_COMPILER_MSVC_VER >= 1600 && gsl_HAS_CPP0X)) + +// Presence of C++11 language features: + +#define gsl_HAVE_AUTO gsl_CPP11_100 +#define gsl_HAVE_NULLPTR gsl_CPP11_100 +#define gsl_HAVE_RVALUE_REFERENCE gsl_CPP11_100 + +#define gsl_HAVE_ENUM_CLASS gsl_CPP11_110 + +#define gsl_HAVE_ALIAS_TEMPLATE gsl_CPP11_120 +#define gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG gsl_CPP11_120 +#define gsl_HAVE_EXPLICIT gsl_CPP11_120 +#define gsl_HAVE_INITIALIZER_LIST gsl_CPP11_120 + +#define gsl_HAVE_CONSTEXPR_11 gsl_CPP11_140 +#define gsl_HAVE_IS_DEFAULT gsl_CPP11_140 +#define gsl_HAVE_IS_DELETE gsl_CPP11_140 +#define gsl_HAVE_NOEXCEPT gsl_CPP11_140 + +#if gsl_CPP11_OR_GREATER +// see above +#endif + +// Presence of C++14 language features: + +#define gsl_HAVE_CONSTEXPR_14 gsl_CPP14_000 +#define gsl_HAVE_DECLTYPE_AUTO gsl_CPP14_140 + +// Presence of C++17 language features: +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#define gsl_HAVE_ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE gsl_CPP17_000 +#define gsl_HAVE_DEDUCTION_GUIDES (gsl_CPP17_000 && ! gsl_BETWEEN( gsl_COMPILER_MSVC_VERSION, 1, 999 ) ) + +// Presence of C++ library features: + +#define gsl_HAVE_ADDRESSOF gsl_CPP17_000 +#define gsl_HAVE_ARRAY gsl_CPP11_110 +#define gsl_HAVE_TYPE_TRAITS gsl_CPP11_110 +#define gsl_HAVE_TR1_TYPE_TRAITS gsl_CPP11_110 + +#define gsl_HAVE_CONTAINER_DATA_METHOD gsl_CPP11_140_CPP0X_90 +#define gsl_HAVE_STD_DATA gsl_CPP17_000 + +#define gsl_HAVE_SIZED_TYPES gsl_CPP11_140 + +#define gsl_HAVE_MAKE_SHARED gsl_CPP11_140_CPP0X_100 +#define gsl_HAVE_SHARED_PTR gsl_CPP11_140_CPP0X_100 +#define gsl_HAVE_UNIQUE_PTR gsl_CPP11_140_CPP0X_100 + +#define gsl_HAVE_MAKE_UNIQUE gsl_CPP14_120 + +#define gsl_HAVE_UNCAUGHT_EXCEPTIONS gsl_CPP17_140 + +#define gsl_HAVE_ADD_CONST gsl_HAVE_TYPE_TRAITS +#define gsl_HAVE_INTEGRAL_CONSTANT gsl_HAVE_TYPE_TRAITS +#define gsl_HAVE_REMOVE_CONST gsl_HAVE_TYPE_TRAITS +#define gsl_HAVE_REMOVE_REFERENCE gsl_HAVE_TYPE_TRAITS + +#define gsl_HAVE_TR1_ADD_CONST gsl_HAVE_TR1_TYPE_TRAITS +#define gsl_HAVE_TR1_INTEGRAL_CONSTANT gsl_HAVE_TR1_TYPE_TRAITS +#define gsl_HAVE_TR1_REMOVE_CONST gsl_HAVE_TR1_TYPE_TRAITS +#define gsl_HAVE_TR1_REMOVE_REFERENCE gsl_HAVE_TR1_TYPE_TRAITS + +// C++ feature usage: + +#if gsl_HAVE( ADDRESSOF ) +# define gsl_ADDRESSOF(x) std::addressof(x) +#else +# define gsl_ADDRESSOF(x) (&x) +#endif + +#if gsl_HAVE( CONSTEXPR_11 ) +# define gsl_constexpr constexpr +#else +# define gsl_constexpr /*constexpr*/ +#endif + +#if gsl_HAVE( CONSTEXPR_14 ) +# define gsl_constexpr14 constexpr +#else +# define gsl_constexpr14 /*constexpr*/ +#endif + +#if gsl_HAVE( EXPLICIT ) +# define gsl_explicit explicit +#else +# define gsl_explicit /*explicit*/ +#endif + +#if gsl_FEATURE( IMPLICIT_MACRO ) +# define implicit /*implicit*/ +#endif + +#if gsl_HAVE( IS_DELETE ) +# define gsl_is_delete = delete +#else +# define gsl_is_delete +#endif + +#if gsl_HAVE( IS_DELETE ) +# define gsl_is_delete_access public +#else +# define gsl_is_delete_access private +#endif + +#if !gsl_HAVE( NOEXCEPT ) || gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) +# define gsl_noexcept /*noexcept*/ +#else +# define gsl_noexcept noexcept +#endif + +#if gsl_HAVE( NULLPTR ) +# define gsl_nullptr nullptr +#else +# define gsl_nullptr NULL +#endif + +#define gsl_DIMENSION_OF( a ) ( sizeof(a) / sizeof(0[a]) ) + +// Other features: + +#define gsl_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR \ + ( gsl_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG && gsl_HAVE_CONTAINER_DATA_METHOD ) + +// Note: !defined(__NVCC__) doesn't work with nvcc here: +#define gsl_HAVE_UNCONSTRAINED_SPAN_CONTAINER_CTOR \ + ( gsl_CONFIG_ALLOWS_UNCONSTRAINED_SPAN_CONTAINER_CTOR && (__NVCC__== 0) ) + +// GSL API (e.g. for CUDA platform): + +#ifndef gsl_api +# ifdef __CUDACC__ +# define gsl_api __host__ __device__ +# else +# define gsl_api /*gsl_api*/ +# endif +#endif + +// Additional includes: + +#if gsl_HAVE( ARRAY ) +# include +#endif + +#if gsl_HAVE( TYPE_TRAITS ) +# include +#elif gsl_HAVE( TR1_TYPE_TRAITS ) +# include +#endif + +#if gsl_HAVE( SIZED_TYPES ) +# include +#endif + +// MSVC warning suppression macros: + +#if gsl_COMPILER_MSVC_VERSION >= 140 +# define gsl_SUPPRESS_MSGSL_WARNING(expr) [[gsl::suppress(expr)]] +# define gsl_SUPPRESS_MSVC_WARNING(code, descr) __pragma(warning(suppress: code) ) +# define gsl_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define gsl_RESTORE_MSVC_WARNINGS() __pragma(warning(pop )) +#else +# define gsl_SUPPRESS_MSGSL_WARNING(expr) +# define gsl_SUPPRESS_MSVC_WARNING(code, descr) +# define gsl_DISABLE_MSVC_WARNINGS(codes) +# define gsl_RESTORE_MSVC_WARNINGS() +#endif + +// Suppress the following MSVC GSL warnings: +// - C26410: gsl::r.32: the parameter 'ptr' is a reference to const unique pointer, use const T* or const T& instead +// - C26415: gsl::r.30: smart pointer parameter 'ptr' is used only to access contained pointer. Use T* or T& instead +// - C26418: gsl::r.36: shared pointer parameter 'ptr' is not copied or moved. Use T* or T& instead +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narow +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26482, gsl::b.2 : only index into arrays using constant expressions +// - C26490: gsl::t.1 : don't use reinterpret_cast + +gsl_DISABLE_MSVC_WARNINGS( 26410 26415 26418 26472 26439 26440 26473 26481 26482 26490 ) + +namespace gsl { + +// forward declare span<>: + +template< class T > +class span; + +// C++11 emulation: + +namespace std11 { + +#if gsl_HAVE( ADD_CONST ) + +using std::add_const; + +#elif gsl_HAVE( TR1_ADD_CONST ) + +using std::tr1::add_const; + +#else + +template< class T > struct add_const { typedef const T type; }; + +#endif // gsl_HAVE( ADD_CONST ) + +#if gsl_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#elif gsl_HAVE( TR1_REMOVE_CONST ) + +using std::tr1::remove_cv; +using std::tr1::remove_const; +using std::tr1::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename remove_volatile::type>::type type; +}; + +#endif // gsl_HAVE( REMOVE_CONST ) + +#if gsl_HAVE( INTEGRAL_CONSTANT ) + +using std::integral_constant; +using std::true_type; +using std::false_type; + +#elif gsl_HAVE( TR1_INTEGRAL_CONSTANT ) + +using std::tr1::integral_constant; +using std::tr1::true_type; +using std::tr1::false_type; + +#else + +template< int v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< true > true_type; +typedef integral_constant< false > false_type; + +#endif + +} // namespace std11 + +namespace detail { + +/// for nsel_REQUIRES_T + +/*enum*/ class enabler{}; + +#if gsl_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std11::false_type{}; + +template< class T> +struct is_span_oracle< span > : std11::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std11::remove_cv::type >{}; + +template< class Q > +struct is_std_array_oracle : std11::false_type{}; + +#if gsl_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array > : std11::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std11::remove_cv::type >{}; + +template< class Q > +struct is_array : std11::false_type {}; + +template< class T > +struct is_array : std11::true_type {}; + +template< class T, std::size_t N > +struct is_array : std11::true_type {}; + +#endif // gsl_HAVE( TYPE_TRAITS ) + +} // namespace detail + +// +// GSL.util: utilities +// + +// index type for all container indexes/subscripts/sizes +typedef gsl_CONFIG_SPAN_INDEX_TYPE index; // p0122r3 uses std::ptrdiff_t + +// +// GSL.owner: ownership pointers +// +#if gsl_HAVE( SHARED_PTR ) + using std::unique_ptr; + using std::shared_ptr; + using std::make_shared; +# if gsl_HAVE( MAKE_UNIQUE ) + using std::make_unique; +# endif +#endif + +#if gsl_HAVE( ALIAS_TEMPLATE ) +# if gsl_HAVE( TYPE_TRAITS ) + template< class T + gsl_REQUIRES_T( std::is_pointer::value ) + > + using owner = T; +# else + template< class T > using owner = T; +# endif +#else + template< class T > struct owner { typedef T type; }; +#endif + +#define gsl_HAVE_OWNER_TEMPLATE gsl_HAVE_ALIAS_TEMPLATE + +#if gsl_FEATURE( OWNER_MACRO ) +# if gsl_HAVE( OWNER_TEMPLATE ) +# define Owner(t) ::gsl::owner +# else +# define Owner(t) ::gsl::owner::type +# endif +#endif + +// +// GSL.assert: assertions +// + +#define gsl_ELIDE_CONTRACT_EXPECTS ( 0 == ( gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define gsl_ELIDE_CONTRACT_ENSURES ( 0 == ( gsl_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if gsl_ELIDE_CONTRACT_EXPECTS +# define Expects( x ) /* Expects elided */ +#elif gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) +# define Expects( x ) ::gsl::fail_fast_assert( (x), "GSL: Precondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__) ); +#else +# define Expects( x ) ::gsl::fail_fast_assert( (x) ) +#endif + +#if gsl_ELIDE_CONTRACT_EXPECTS +# define gsl_EXPECTS_UNUSED_PARAM( x ) /* Make param unnamed if Expects elided */ +#else +# define gsl_EXPECTS_UNUSED_PARAM( x ) x +#endif + +#if gsl_ELIDE_CONTRACT_ENSURES +# define Ensures( x ) /* Ensures elided */ +#elif gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) +# define Ensures( x ) ::gsl::fail_fast_assert( (x), "GSL: Postcondition failure at " __FILE__ ":" gsl_STRINGIFY(__LINE__) ); +#else +# define Ensures( x ) ::gsl::fail_fast_assert( (x) ) +#endif + +#define gsl_STRINGIFY( x ) gsl_STRINGIFY_( x ) +#define gsl_STRINGIFY_( x ) #x + +struct fail_fast : public std::logic_error +{ + gsl_api explicit fail_fast( char const * const message ) + : std::logic_error( message ) {} +}; + +// workaround for gcc 5 throw/terminate constexpr bug: + +#if gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 430, 600 ) && gsl_HAVE( CONSTEXPR_14 ) + +# if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +gsl_api inline gsl_constexpr14 auto fail_fast_assert( bool cond, char const * const message ) -> void +{ + !cond ? throw fail_fast( message ) : 0; +} + +# else + +gsl_api inline gsl_constexpr14 auto fail_fast_assert( bool cond ) -> void +{ + struct F { static gsl_constexpr14 void f(){}; }; + + !cond ? std::terminate() : F::f(); +} + +# endif + +#else // workaround + +# if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +gsl_api inline gsl_constexpr14 void fail_fast_assert( bool cond, char const * const message ) +{ + if ( !cond ) + throw fail_fast( message ); +} + +# else + +gsl_api inline gsl_constexpr14 void fail_fast_assert( bool cond ) gsl_noexcept +{ + if ( !cond ) + std::terminate(); +} + +# endif +#endif // workaround + +// +// GSL.util: utilities +// + +#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) + +// Add uncaught_exceptions for pre-2017 MSVC, GCC and Clang +// Return unsigned char to save stack space, uncaught_exceptions can only increase by 1 in a scope + +namespace detail { + +inline unsigned char to_uchar( unsigned x ) gsl_noexcept +{ + return static_cast( x ); +} + +} // namespace detail + +namespace std11 { + +#if gsl_HAVE( UNCAUGHT_EXCEPTIONS ) + +inline unsigned char uncaught_exceptions() gsl_noexcept +{ + return detail::to_uchar( std::uncaught_exceptions() ); +} + +#elif gsl_COMPILER_MSVC_VERSION + +extern "C" char * __cdecl _getptd(); +inline unsigned char uncaught_exceptions() gsl_noexcept +{ + return detail::to_uchar( *reinterpret_cast(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90) ) ); +} + +#elif gsl_COMPILER_CLANG_VERSION || gsl_COMPILER_GNUC_VERSION + +extern "C" char * __cxa_get_globals(); +inline unsigned char uncaught_exceptions() gsl_noexcept +{ + return detail::to_uchar( *reinterpret_cast(__cxa_get_globals() + sizeof(void*) ) ); +} +#endif +} // namespace std11 +#endif + +#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110 + +template< class F > +class final_action +{ +public: + gsl_api explicit final_action( F action ) gsl_noexcept + : action_( std::move( action ) ) + , invoke_( true ) + {} + + gsl_api final_action( final_action && other ) gsl_noexcept + : action_( std::move( other.action_ ) ) + , invoke_( other.invoke_ ) + { + other.invoke_ = false; + } + + gsl_api virtual ~final_action() gsl_noexcept + { + if ( invoke_ ) + action_(); + } + +gsl_is_delete_access: + gsl_api final_action( final_action const & ) gsl_is_delete; + gsl_api final_action & operator=( final_action const & ) gsl_is_delete; + gsl_api final_action & operator=( final_action && ) gsl_is_delete; + +protected: + gsl_api void dismiss() gsl_noexcept + { + invoke_ = false; + } + +private: + F action_; + bool invoke_; +}; + +template< class F > +gsl_api inline final_action finally( F const & action ) gsl_noexcept +{ + return final_action( action ); +} + +template< class F > +gsl_api inline final_action finally( F && action ) gsl_noexcept +{ + return final_action( std::forward( action ) ); +} + +#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) + +template< class F > +class final_action_return : public final_action +{ +public: + gsl_api explicit final_action_return( F && action ) gsl_noexcept + : final_action( std::move( action ) ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api final_action_return( final_action_return && other ) gsl_noexcept + : final_action( std::move( other ) ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api ~final_action_return() override + { + if ( std11::uncaught_exceptions() != exception_count ) + this->dismiss(); + } + +gsl_is_delete_access: + gsl_api final_action_return( final_action_return const & ) gsl_is_delete; + gsl_api final_action_return & operator=( final_action_return const & ) gsl_is_delete; + +private: + unsigned char exception_count; +}; + +template< class F > +gsl_api inline final_action_return on_return( F const & action ) gsl_noexcept +{ + return final_action_return( action ); +} + +template< class F > +gsl_api inline final_action_return on_return( F && action ) gsl_noexcept +{ + return final_action_return( std::forward( action ) ); +} + +template< class F > +class final_action_error : public final_action +{ +public: + gsl_api explicit final_action_error( F && action ) gsl_noexcept + : final_action( std::move( action ) ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api final_action_error( final_action_error && other ) gsl_noexcept + : final_action( std::move( other ) ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api ~final_action_error() override + { + if ( std11::uncaught_exceptions() == exception_count ) + this->dismiss(); + } + +gsl_is_delete_access: + gsl_api final_action_error( final_action_error const & ) gsl_is_delete; + gsl_api final_action_error & operator=( final_action_error const & ) gsl_is_delete; + +private: + unsigned char exception_count; +}; + +template< class F > +gsl_api inline final_action_error on_error( F const & action ) gsl_noexcept +{ + return final_action_error( action ); +} + +template< class F > +gsl_api inline final_action_error on_error( F && action ) gsl_noexcept +{ + return final_action_error( std::forward( action ) ); +} + +#endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) + +#else // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 110 + +class final_action +{ +public: + typedef void (*Action)(); + + gsl_api final_action( Action action ) + : action_( action ) + , invoke_( true ) + {} + + gsl_api final_action( final_action const & other ) + : action_( other.action_ ) + , invoke_( other.invoke_ ) + { + other.invoke_ = false; + } + + gsl_api virtual ~final_action() + { + if ( invoke_ ) + action_(); + } + +protected: + gsl_api void dismiss() + { + invoke_ = false; + } + +private: + gsl_api final_action & operator=( final_action const & ); + +private: + Action action_; + mutable bool invoke_; +}; + +template< class F > +gsl_api inline final_action finally( F const & f ) +{ + return final_action(( f )); +} + +#if gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) + +class final_action_return : public final_action +{ +public: + gsl_api explicit final_action_return( Action action ) + : final_action( action ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api ~final_action_return() + { + if ( std11::uncaught_exceptions() != exception_count ) + this->dismiss(); + } + +private: + gsl_api final_action_return & operator=( final_action_return const & ); + +private: + unsigned char exception_count; +}; + +template< class F > +gsl_api inline final_action_return on_return( F const & action ) +{ + return final_action_return( action ); +} + +class final_action_error : public final_action +{ +public: + gsl_api explicit final_action_error( Action action ) + : final_action( action ) + , exception_count( std11::uncaught_exceptions() ) + {} + + gsl_api ~final_action_error() + { + if ( std11::uncaught_exceptions() == exception_count ) + this->dismiss(); + } + +private: + gsl_api final_action_error & operator=( final_action_error const & ); + +private: + unsigned char exception_count; +}; + +template< class F > +gsl_api inline final_action_error on_error( F const & action ) +{ + return final_action_error( action ); +} + +#endif // gsl_FEATURE( EXPERIMENTAL_RETURN_GUARD ) + +#endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION == 110 + +#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 + +template< class T, class U > +gsl_api inline gsl_constexpr T narrow_cast( U && u ) gsl_noexcept +{ + return static_cast( std::forward( u ) ); +} + +#else + +template< class T, class U > +gsl_api inline T narrow_cast( U u ) gsl_noexcept +{ + return static_cast( u ); +} + +#endif // gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 + +struct narrowing_error : public std::exception {}; + +#if gsl_HAVE( TYPE_TRAITS ) + +namespace detail +{ + template< class T, class U > + struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> + {}; +} +#endif + +template< class T, class U > +gsl_api inline T narrow( U u ) +{ + T t = narrow_cast( u ); + + if ( static_cast( t ) != u ) + { +#if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + throw narrowing_error(); +#else + std::terminate(); +#endif + } + +#if gsl_HAVE( TYPE_TRAITS ) +# if gsl_COMPILER_MSVC_VERSION + // Suppress MSVC level 4 warning C4127 (conditional expression is constant) + if ( 0, ! detail::is_same_signedness::value && ( ( t < T() ) != ( u < U() ) ) ) +# else + if ( ! detail::is_same_signedness::value && ( ( t < T() ) != ( u < U() ) ) ) +# endif +#else + // Don't assume T() works: + if ( ( t < 0 ) != ( u < 0 ) ) +#endif + { +#if gsl_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + throw narrowing_error(); +#else + std::terminate(); +#endif + } + return t; +} + +// +// at() - Bounds-checked way of accessing static arrays, std::array, std::vector. +// + +template< class T, size_t N > +gsl_api inline gsl_constexpr14 T & at( T(&arr)[N], size_t index ) +{ + Expects( index < N ); + return arr[index]; +} + +#if gsl_HAVE( ARRAY ) + +template< class T, size_t N > +gsl_api inline gsl_constexpr14 T & at( std::array & arr, size_t index ) +{ + Expects( index < N ); + return arr[index]; +} +#endif + +template< class Container > +gsl_api inline gsl_constexpr14 typename Container::value_type & at( Container & cont, size_t index ) +{ + Expects( index < cont.size() ); + return cont[index]; +} + +#if gsl_HAVE( INITIALIZER_LIST ) + +template< class T > +gsl_api inline const gsl_constexpr14 T & at( std::initializer_list cont, size_t index ) +{ + Expects( index < cont.size() ); + return *( cont.begin() + index ); +} +#endif + +template< class T > +gsl_api inline gsl_constexpr T & at( span s, size_t index ) +{ + return s.at( index ); +} + +// +// GSL.views: views +// + +// +// not_null<> - Wrap any indirection and enforce non-null. +// +template< class T > +class not_null +{ +#if gsl_CONFIG( NOT_NULL_EXPLICIT_CTOR ) +# define gsl_not_null_explicit explicit +#else +# define gsl_not_null_explicit /*explicit*/ +#endif + +#if gsl_CONFIG( NOT_NULL_GET_BY_CONST_REF ) + typedef T const & get_result_t; +#else + typedef T get_result_t; +#endif + +public: +#if gsl_HAVE( TYPE_TRAITS ) + static_assert( std::is_assignable::value, "T cannot be assigned nullptr." ); +#endif + + template< class U +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_constructible::value )) +#endif + > + gsl_api gsl_constexpr14 gsl_not_null_explicit +#if gsl_HAVE( RVALUE_REFERENCE ) + not_null( U && u ) + : ptr_( std::forward( u ) ) +#else + not_null( U const & u ) + : ptr_( u ) +#endif + { + Expects( ptr_ != gsl_nullptr ); + } +#undef gsl_not_null_explicit + +#if gsl_HAVE( IS_DEFAULT ) + gsl_api ~not_null() = default; + gsl_api gsl_constexpr not_null( not_null && other ) = default; + gsl_api gsl_constexpr not_null( not_null const & other ) = default; + gsl_api not_null & operator=( not_null && other ) = default; + gsl_api not_null & operator=( not_null const & other ) = default; +#else + gsl_api ~not_null() {}; + gsl_api gsl_constexpr not_null( not_null const & other ) : ptr_ ( other.ptr_ ) {} + gsl_api not_null & operator=( not_null const & other ) { ptr_ = other.ptr_; return *this; } +# if gsl_HAVE( RVALUE_REFERENCE ) + gsl_api gsl_constexpr not_null( not_null && other ) : ptr_( std::move( other.get() ) ) {} + gsl_api not_null & operator=( not_null && other ) { ptr_ = std::move( other.get() ); return *this; } +# endif +#endif + + template< class U +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::value )) +#endif + > + gsl_api gsl_constexpr not_null( not_null const & other ) + : ptr_( other.get() ) + {} + + gsl_api gsl_constexpr14 get_result_t get() const + { + // Without cheating and changing ptr_ from the outside, this check is superfluous: + Ensures( ptr_ != gsl_nullptr ); + return ptr_; + } + + gsl_api gsl_constexpr operator get_result_t () const { return get(); } + gsl_api gsl_constexpr get_result_t operator->() const { return get(); } + +#if gsl_HAVE( DECLTYPE_AUTO ) + gsl_api gsl_constexpr decltype(auto) operator*() const { return *get(); } +#endif + +gsl_is_delete_access: + // prevent compilation when initialized with a nullptr or literal 0: +#if gsl_HAVE( NULLPTR ) + gsl_api not_null( std::nullptr_t ) gsl_is_delete; + gsl_api not_null & operator=( std::nullptr_t ) gsl_is_delete; +#else + gsl_api not_null( int ) gsl_is_delete; + gsl_api not_null & operator=( int ) gsl_is_delete; +#endif + + // unwanted operators...pointers only point to single objects! + gsl_api not_null & operator++() gsl_is_delete; + gsl_api not_null & operator--() gsl_is_delete; + gsl_api not_null operator++( int ) gsl_is_delete; + gsl_api not_null operator--( int ) gsl_is_delete; + gsl_api not_null & operator+ ( size_t ) gsl_is_delete; + gsl_api not_null & operator+=( size_t ) gsl_is_delete; + gsl_api not_null & operator- ( size_t ) gsl_is_delete; + gsl_api not_null & operator-=( size_t ) gsl_is_delete; + gsl_api not_null & operator+=( std::ptrdiff_t ) gsl_is_delete; + gsl_api not_null & operator-=( std::ptrdiff_t ) gsl_is_delete; + gsl_api void operator[]( std::ptrdiff_t ) const gsl_is_delete; + +private: + T ptr_; +}; + +// not_null with implicit constructor, allowing copy-initialization: + +template< class T > +class not_null_ic : public not_null +{ +public: + template< class U +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_constructible::value )) +#endif + > + gsl_api gsl_constexpr14 +#if gsl_HAVE( RVALUE_REFERENCE ) + not_null_ic( U && u ) + : not_null( std::forward( u ) ) +#else + not_null_ic( U const & u ) + : not_null( u ) +#endif + {} +}; + +// more not_null unwanted operators + +template< class T, class U > +std::ptrdiff_t operator-( not_null const &, not_null const & ) gsl_is_delete; + +template< class T > +not_null operator-( not_null const &, std::ptrdiff_t ) gsl_is_delete; + +template< class T > +not_null operator+( not_null const &, std::ptrdiff_t ) gsl_is_delete; + +template< class T > +not_null operator+( std::ptrdiff_t, not_null const & ) gsl_is_delete; + +// not_null comparisons + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator==( not_null const & l, not_null const & r ) +{ + return l.get() == r.get(); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator< ( not_null const & l, not_null const & r ) +{ + return l.get() < r.get(); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator!=( not_null const & l, not_null const & r ) +{ + return !( l == r ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator<=( not_null const & l, not_null const & r ) +{ + return !( r < l ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator> ( not_null const & l, not_null const & r ) +{ + return ( r < l ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator>=( not_null const & l, not_null const & r ) +{ + return !( l < r ); +} + +// +// Byte-specific type. +// +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + enum class gsl_may_alias byte : unsigned char {}; +#else + struct gsl_may_alias byte { typedef unsigned char type; type v; }; +#endif + +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) +# define gsl_ENABLE_IF_INTEGRAL_T(T) \ + gsl_REQUIRES_T(( std::is_integral::value )) +#else +# define gsl_ENABLE_IF_INTEGRAL_T(T) +#endif + +template< class T > +gsl_api inline gsl_constexpr byte to_byte( T v ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return static_cast( v ); +#elif gsl_HAVE( CONSTEXPR_11 ) + return { static_cast( v ) }; +#else + byte b = { static_cast( v ) }; return b; +#endif +} + +template< class IntegerType gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) > +gsl_api inline gsl_constexpr IntegerType to_integer( byte b ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return static_cast::type>( b ); +#else + return b.v; +#endif +} + +gsl_api inline gsl_constexpr unsigned char to_uchar( byte b ) gsl_noexcept +{ + return to_integer( b ); +} + +gsl_api inline gsl_constexpr unsigned char to_uchar( int i ) gsl_noexcept +{ + return static_cast( i ); +} + +#if ! gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + +gsl_api inline gsl_constexpr bool operator==( byte l, byte r ) gsl_noexcept +{ + return l.v == r.v; +} + +gsl_api inline gsl_constexpr bool operator!=( byte l, byte r ) gsl_noexcept +{ + return !( l == r ); +} + +gsl_api inline gsl_constexpr bool operator< ( byte l, byte r ) gsl_noexcept +{ + return l.v < r.v; +} + +gsl_api inline gsl_constexpr bool operator<=( byte l, byte r ) gsl_noexcept +{ + return !( r < l ); +} + +gsl_api inline gsl_constexpr bool operator> ( byte l, byte r ) gsl_noexcept +{ + return ( r < l ); +} + +gsl_api inline gsl_constexpr bool operator>=( byte l, byte r ) gsl_noexcept +{ + return !( l < r ); +} +#endif + +template< class IntegerType gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) > +gsl_api inline gsl_constexpr14 byte & operator<<=( byte & b, IntegerType shift ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return b = to_byte( to_uchar( b ) << shift ); +#else + b.v = to_uchar( b.v << shift ); return b; +#endif +} + +template< class IntegerType gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) > +gsl_api inline gsl_constexpr byte operator<<( byte b, IntegerType shift ) gsl_noexcept +{ + return to_byte( to_uchar( b ) << shift ); +} + +template< class IntegerType gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) > +gsl_api inline gsl_constexpr14 byte & operator>>=( byte & b, IntegerType shift ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return b = to_byte( to_uchar( b ) >> shift ); +#else + b.v = to_uchar( b.v >> shift ); return b; +#endif +} + +template< class IntegerType gsl_ENABLE_IF_INTEGRAL_T( IntegerType ) > +gsl_api inline gsl_constexpr byte operator>>( byte b, IntegerType shift ) gsl_noexcept +{ + return to_byte( to_uchar( b ) >> shift ); +} + +gsl_api inline gsl_constexpr14 byte & operator|=( byte & l, byte r ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return l = to_byte( to_uchar( l ) | to_uchar( r ) ); +#else + l.v = to_uchar( l ) | to_uchar( r ); return l; +#endif +} + +gsl_api inline gsl_constexpr byte operator|( byte l, byte r ) gsl_noexcept +{ + return to_byte( to_uchar( l ) | to_uchar( r ) ); +} + +gsl_api inline gsl_constexpr14 byte & operator&=( byte & l, byte r ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return l = to_byte( to_uchar( l ) & to_uchar( r ) ); +#else + l.v = to_uchar( l ) & to_uchar( r ); return l; +#endif +} + +gsl_api inline gsl_constexpr byte operator&( byte l, byte r ) gsl_noexcept +{ + return to_byte( to_uchar( l ) & to_uchar( r ) ); +} + +gsl_api inline gsl_constexpr14 byte & operator^=( byte & l, byte r ) gsl_noexcept +{ +#if gsl_HAVE( ENUM_CLASS_CONSTRUCTION_FROM_UNDERLYING_TYPE ) + return l = to_byte( to_uchar( l ) ^ to_uchar (r ) ); +#else + l.v = to_uchar( l ) ^ to_uchar (r ); return l; +#endif +} + +gsl_api inline gsl_constexpr byte operator^( byte l, byte r ) gsl_noexcept +{ + return to_byte( to_uchar( l ) ^ to_uchar( r ) ); +} + +gsl_api inline gsl_constexpr byte operator~( byte b ) gsl_noexcept +{ + return to_byte( ~to_uchar( b ) ); +} + +#if gsl_FEATURE_TO_STD( WITH_CONTAINER ) + +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): + +struct with_container_t { gsl_constexpr with_container_t() gsl_noexcept {} }; +const gsl_constexpr with_container_t with_container; + +#endif + +#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +namespace detail { + +// Can construct from containers that: + +template< class Container, class ElementType + gsl_REQUIRES_T(( + ! detail::is_span< Container >::value + && ! detail::is_array< Container >::value + && ! detail::is_std_array< Container >::value + && std::is_convertible().data())>::type(*)[], ElementType(*)[] >::value + )) +#if gsl_HAVE( STD_DATA ) + // data(cont) and size(cont) well-formed: + , class = decltype( std::data( std::declval() ) ) + , class = decltype( std::size( std::declval() ) ) +#endif +> +struct can_construct_span_from : std11::true_type{}; + +} // namespace detail + +#endif + +// +// span<> - A 1D view of contiguous T's, replace (*,len). +// +template< class T > +class span +{ + template< class U > friend class span; + +public: + typedef index index_type; + + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; + + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef typename std::iterator_traits< iterator >::difference_type difference_type; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + gsl_api gsl_constexpr14 span() gsl_noexcept + : first_( gsl_nullptr ) + , last_ ( gsl_nullptr ) + { + Expects( size() == 0 ); + } + +#if ! gsl_DEPRECATE_TO_LEVEL( 5 ) + +#if gsl_HAVE( NULLPTR ) + gsl_api gsl_constexpr14 span( std::nullptr_t, index_type gsl_EXPECTS_UNUSED_PARAM( size_in ) ) + : first_( nullptr ) + , last_ ( nullptr ) + { + Expects( size_in == 0 ); + } +#endif + +#if gsl_HAVE( IS_DELETE ) + gsl_api gsl_constexpr span( reference data_in ) + : span( &data_in, 1 ) + {} + + gsl_api gsl_constexpr span( element_type && ) = delete; +#endif + +#endif // deprecate + + gsl_api gsl_constexpr14 span( pointer data_in, index_type size_in ) + : first_( data_in ) + , last_ ( data_in + size_in ) + { + Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) ); + } + + gsl_api gsl_constexpr14 span( pointer first_in, pointer last_in ) + : first_( first_in ) + , last_ ( last_in ) + { + Expects( first_in <= last_in ); + } + +#if ! gsl_DEPRECATE_TO_LEVEL( 5 ) + + template< class U > + gsl_api gsl_constexpr14 span( U * & data_in, index_type size_in ) + : first_( data_in ) + , last_ ( data_in + size_in ) + { + Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) ); + } + + template< class U > + gsl_api gsl_constexpr14 span( U * const & data_in, index_type size_in ) + : first_( data_in ) + , last_ ( data_in + size_in ) + { + Expects( size_in == 0 || ( size_in > 0 && data_in != gsl_nullptr ) ); + } + +#endif // deprecate + +#if ! gsl_DEPRECATE_TO_LEVEL( 5 ) + template< class U, size_t N > + gsl_api gsl_constexpr span( U (&arr)[N] ) gsl_noexcept + : first_( gsl_ADDRESSOF( arr[0] ) ) + , last_ ( gsl_ADDRESSOF( arr[0] ) + N ) + {} +#else + template< size_t N +# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::value )) +# endif + > + gsl_api gsl_constexpr span( element_type (&arr)[N] ) gsl_noexcept + : first_( gsl_ADDRESSOF( arr[0] ) ) + , last_ ( gsl_ADDRESSOF( arr[0] ) + N ) + {} +#endif // deprecate + +#if gsl_HAVE( ARRAY ) +#if ! gsl_DEPRECATE_TO_LEVEL( 5 ) + + template< class U, size_t N > + gsl_api gsl_constexpr span( std::array< U, N > & arr ) + : first_( arr.data() ) + , last_ ( arr.data() + N ) + {} + + template< class U, size_t N > + gsl_api gsl_constexpr span( std::array< U, N > const & arr ) + : first_( arr.data() ) + , last_ ( arr.data() + N ) + {} + +#else + + template< size_t N +# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::value )) +# endif + > + gsl_api gsl_constexpr span( std::array< value_type, N > & arr ) + : first_( arr.data() ) + , last_ ( arr.data() + N ) + {} + + template< size_t N +# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::value )) +# endif + > + gsl_api gsl_constexpr span( std::array< value_type, N > const & arr ) + : first_( arr.data() ) + , last_ ( arr.data() + N ) + {} + +#endif // deprecate +#endif // gsl_HAVE( ARRAY ) + +#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + gsl_REQUIRES_T(( detail::can_construct_span_from< Container, element_type >::value )) + > + gsl_api gsl_constexpr span( Container & cont ) + : first_( cont.data() ) + , last_ ( cont.data() + cont.size() ) + {} + + template< class Container + gsl_REQUIRES_T(( + std::is_const< element_type >::value + && detail::can_construct_span_from< Container, element_type >::value + )) + > + gsl_api gsl_constexpr span( Container const & cont ) + : first_( cont.data() ) + , last_ ( cont.data() + cont.size() ) + {} + +#elif gsl_HAVE( UNCONSTRAINED_SPAN_CONTAINER_CTOR ) + + template< class Container > + gsl_api gsl_constexpr span( Container & cont ) + : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) ) + , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() ) + {} + + template< class Container > + gsl_api gsl_constexpr span( Container const & cont ) + : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) ) + , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() ) + {} + +#endif + +#if gsl_FEATURE_TO_STD( WITH_CONTAINER ) + + template< class Container > + gsl_api gsl_constexpr span( with_container_t, Container & cont ) + : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) ) + , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() ) + {} + + template< class Container > + gsl_api gsl_constexpr span( with_container_t, Container const & cont ) + : first_( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) ) + , last_ ( cont.size() == 0 ? gsl_nullptr : gsl_ADDRESSOF( cont[0] ) + cont.size() ) + {} + +#endif + +#if ! gsl_DEPRECATE_TO_LEVEL( 4 ) + // constructor taking shared_ptr deprecated since 0.29.0 + +#if gsl_HAVE( SHARED_PTR ) + gsl_api gsl_constexpr span( shared_ptr const & ptr ) + : first_( ptr.get() ) + , last_ ( ptr.get() ? ptr.get() + 1 : gsl_nullptr ) + {} +#endif + + // constructors taking unique_ptr deprecated since 0.29.0 + +#if gsl_HAVE( UNIQUE_PTR ) +# if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< class ArrayElementType = typename std::add_pointer::type > +# else + template< class ArrayElementType > +# endif + gsl_api gsl_constexpr span( unique_ptr const & ptr, index_type count ) + : first_( ptr.get() ) + , last_ ( ptr.get() + count ) + {} + + gsl_api gsl_constexpr span( unique_ptr const & ptr ) + : first_( ptr.get() ) + , last_ ( ptr.get() ? ptr.get() + 1 : gsl_nullptr ) + {} +#endif + +#endif // deprecate shared_ptr, unique_ptr + +#if gsl_HAVE( IS_DEFAULT ) && ! gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 430, 600) + gsl_api gsl_constexpr span( span && ) gsl_noexcept = default; + gsl_api gsl_constexpr span( span const & ) = default; +#else + gsl_api gsl_constexpr span( span const & other ) + : first_( other.begin() ) + , last_ ( other.end() ) + {} +#endif + +#if gsl_HAVE( IS_DEFAULT ) + ~span() = default; +#else + ~span() {} +#endif + +#if gsl_HAVE( IS_DEFAULT ) + gsl_api gsl_constexpr14 span & operator=( span && ) gsl_noexcept = default; + gsl_api gsl_constexpr14 span & operator=( span const & ) gsl_noexcept = default; +#else + gsl_api span & operator=( span other ) gsl_noexcept + { + other.swap( *this ); + return *this; + } +#endif + + template< class U +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::value )) +#endif + > + gsl_api gsl_constexpr span( span const & other ) + : first_( other.begin() ) + , last_ ( other.end() ) + {} + +#if 0 + // Converting from other span ? + template< class U > operator=(); +#endif + + // 26.7.3.3 Subviews [span.sub] + + gsl_api gsl_constexpr14 span first( index_type count ) const gsl_noexcept + { + Expects( 0 <= count && count <= this->size() ); + return span( this->data(), count ); + } + + gsl_api gsl_constexpr14 span last( index_type count ) const gsl_noexcept + { + Expects( 0 <= count && count <= this->size() ); + return span( this->data() + this->size() - count, count ); + } + + gsl_api gsl_constexpr14 span subspan( index_type offset ) const gsl_noexcept + { + Expects( 0 <= offset && offset <= this->size() ); + return span( this->data() + offset, this->size() - offset ); + } + + gsl_api gsl_constexpr14 span subspan( index_type offset, index_type count ) const gsl_noexcept + { + Expects( + 0 <= offset && offset <= this->size() && + 0 <= count && count <= this->size() - offset ); + return span( this->data() + offset, count ); + } + + // 26.7.3.4 Observers [span.obs] + + gsl_api gsl_constexpr index_type size() const gsl_noexcept + { + return narrow_cast( last_ - first_ ); + } + + gsl_api gsl_constexpr std::ptrdiff_t ssize() const gsl_noexcept + { + return narrow_cast( last_ - first_ ); + } + + gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept + { + return size() * narrow_cast( sizeof( element_type ) ); + } + + gsl_api gsl_constexpr bool empty() const gsl_noexcept + { + return size() == 0; + } + + // 26.7.3.5 Element access [span.elem] + + gsl_api gsl_constexpr reference operator[]( index_type index ) const + { + return at( index ); + } + + gsl_api gsl_constexpr reference operator()( index_type index ) const + { + return at( index ); + } + + gsl_api gsl_constexpr14 reference at( index_type index ) const + { + Expects( index < size() ); + return first_[ index ]; + } + + gsl_api gsl_constexpr pointer data() const gsl_noexcept + { + return first_; + } + + // 26.7.3.6 Iterator support [span.iterators] + + gsl_api gsl_constexpr iterator begin() const gsl_noexcept + { + return iterator( first_ ); + } + + gsl_api gsl_constexpr iterator end() const gsl_noexcept + { + return iterator( last_ ); + } + + gsl_api gsl_constexpr const_iterator cbegin() const gsl_noexcept + { +#if gsl_CPP11_OR_GREATER + return { begin() }; +#else + return const_iterator( begin() ); +#endif + } + + gsl_api gsl_constexpr const_iterator cend() const gsl_noexcept + { +#if gsl_CPP11_OR_GREATER + return { end() }; +#else + return const_iterator( end() ); +#endif + } + + gsl_api gsl_constexpr reverse_iterator rbegin() const gsl_noexcept + { + return reverse_iterator( end() ); + } + + gsl_api gsl_constexpr reverse_iterator rend() const gsl_noexcept + { + return reverse_iterator( begin() ); + } + + gsl_api gsl_constexpr const_reverse_iterator crbegin() const gsl_noexcept + { + return const_reverse_iterator( cend() ); + } + + gsl_api gsl_constexpr const_reverse_iterator crend() const gsl_noexcept + { + return const_reverse_iterator( cbegin() ); + } + + gsl_api void swap( span & other ) gsl_noexcept + { + using std::swap; + swap( first_, other.first_ ); + swap( last_ , other.last_ ); + } + +#if ! gsl_DEPRECATE_TO_LEVEL( 3 ) + // member length() deprecated since 0.29.0 + + gsl_api gsl_constexpr index_type length() const gsl_noexcept + { + return size(); + } + + // member length_bytes() deprecated since 0.29.0 + + gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept + { + return size_bytes(); + } +#endif + +#if ! gsl_DEPRECATE_TO_LEVEL( 2 ) + // member as_bytes(), as_writeable_bytes deprecated since 0.17.0 + + gsl_api span< const byte > as_bytes() const gsl_noexcept + { + return span< const byte >( reinterpret_cast( data() ), size_bytes() ); // NOLINT + } + + gsl_api span< byte > as_writeable_bytes() const gsl_noexcept + { + return span< byte >( reinterpret_cast( data() ), size_bytes() ); // NOLINT + } + +#endif + + template< class U > + gsl_api span< U > as_span() const gsl_noexcept + { + Expects( ( this->size_bytes() % sizeof(U) ) == 0 ); + return span< U >( reinterpret_cast( this->data() ), this->size_bytes() / sizeof( U ) ); // NOLINT + } + +private: + pointer first_; + pointer last_; +}; + +// class template argument deduction guides: + +#if gsl_HAVE( DEDUCTION_GUIDES ) // gsl_CPP17_OR_GREATER + +template< class T, size_t N > +span( T (&)[N] ) -> span; + +template< class T, size_t N > +span( std::array & ) -> span; + +template< class T, size_t N > +span( std::array const & ) -> span; + +template< class Container > +span( Container& ) -> span; + +template< class Container > +span( Container const & ) -> span; + +#endif // gsl_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator==( span const & l, span const & r ) +{ + return l.size() == r.size() + && (l.begin() == r.begin() || std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator< ( span const & l, span const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +#else + +template< class T > +gsl_api inline gsl_constexpr bool operator==( span const & l, span const & r ) +{ + return l.size() == r.size() + && (l.begin() == r.begin() || std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T > +gsl_api inline gsl_constexpr bool operator< ( span const & l, span const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} +#endif + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator!=( span const & l, span const & r ) +{ + return !( l == r ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator<=( span const & l, span const & r ) +{ + return !( r < l ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator> ( span const & l, span const & r ) +{ + return ( r < l ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr bool operator>=( span const & l, span const & r ) +{ + return !( l < r ); +} + +// span algorithms + +template< class T > +gsl_api inline gsl_constexpr std::size_t size( span const & spn ) +{ + return static_cast( spn.size() ); +} + +template< class T > +gsl_api inline gsl_constexpr std::ptrdiff_t ssize( span const & spn ) +{ + return spn.ssize(); +} + +namespace detail { + +template< class II, class N, class OI > +gsl_api inline OI copy_n( II first, N count, OI result ) +{ + if ( count > 0 ) + { + *result++ = *first; + for ( N i = 1; i < count; ++i ) + { + *result++ = *++first; + } + } + return result; +} +} + +template< class T, class U > +gsl_api inline void copy( span src, span dest ) +{ +#if gsl_CPP14_OR_GREATER // gsl_HAVE( TYPE_TRAITS ) (circumvent Travis clang 3.4) + static_assert( std::is_assignable::value, "Cannot assign elements of source span to elements of destination span" ); +#endif + Expects( dest.size() >= src.size() ); + detail::copy_n( src.data(), src.size(), dest.data() ); +} + +// span creator functions (see ctors) + +template< class T > +gsl_api inline span< const byte > as_bytes( span spn ) gsl_noexcept +{ + return span< const byte >( reinterpret_cast( spn.data() ), spn.size_bytes() ); // NOLINT +} + +template< class T> +gsl_api inline span< byte > as_writeable_bytes( span spn ) gsl_noexcept +{ + return span< byte >( reinterpret_cast( spn.data() ), spn.size_bytes() ); // NOLINT +} + +#if gsl_FEATURE_TO_STD( MAKE_SPAN ) + +template< class T > +gsl_api inline gsl_constexpr span +make_span( T * ptr, typename span::index_type count ) +{ + return span( ptr, count ); +} + +template< class T > +gsl_api inline gsl_constexpr span +make_span( T * first, T * last ) +{ + return span( first, last ); +} + +template< class T, size_t N > +gsl_api inline gsl_constexpr span +make_span( T (&arr)[N] ) +{ + return span( gsl_ADDRESSOF( arr[0] ), N ); +} + +#if gsl_HAVE( ARRAY ) + +template< class T, size_t N > +gsl_api inline gsl_constexpr span +make_span( std::array & arr ) +{ + return span( arr ); +} + +template< class T, size_t N > +gsl_api inline gsl_constexpr span +make_span( std::array const & arr ) +{ + return span( arr ); +} +#endif + +#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && gsl_HAVE( AUTO ) + +template< class Container, class = decltype(std::declval().data()) > +gsl_api inline gsl_constexpr auto +make_span( Container & cont ) -> span< typename Container::value_type > +{ + return span< typename Container::value_type >( cont ); +} + +template< class Container, class = decltype(std::declval().data()) > +gsl_api inline gsl_constexpr auto +make_span( Container const & cont ) -> span< const typename Container::value_type > +{ + return span< const typename Container::value_type >( cont ); +} + +#else + +template< class T > +gsl_api inline span +make_span( std::vector & cont ) +{ + return span( with_container, cont ); +} + +template< class T > +gsl_api inline span +make_span( std::vector const & cont ) +{ + return span( with_container, cont ); +} +#endif + +#if gsl_FEATURE_TO_STD( WITH_CONTAINER ) + +template< class Container > +gsl_api inline gsl_constexpr span +make_span( with_container_t, Container & cont ) gsl_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +gsl_api inline gsl_constexpr span +make_span( with_container_t, Container const & cont ) gsl_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // gsl_FEATURE_TO_STD( WITH_CONTAINER ) + +template< class Ptr > +gsl_api inline span +make_span( Ptr & ptr ) +{ + return span( ptr ); +} + +template< class Ptr > +gsl_api inline span +make_span( Ptr & ptr, typename span::index_type count ) +{ + return span( ptr, count); +} + +#endif // gsl_FEATURE_TO_STD( MAKE_SPAN ) + +#if gsl_FEATURE_TO_STD( BYTE_SPAN ) + +template< class T > +gsl_api inline gsl_constexpr span +byte_span( T & t ) gsl_noexcept +{ + return span( reinterpret_cast( &t ), sizeof(T) ); +} + +template< class T > +gsl_api inline gsl_constexpr span +byte_span( T const & t ) gsl_noexcept +{ + return span( reinterpret_cast( &t ), sizeof(T) ); +} + +#endif // gsl_FEATURE_TO_STD( BYTE_SPAN ) + +// +// basic_string_span: +// + +template< class T > +class basic_string_span; + +namespace detail { + +template< class T > +struct is_basic_string_span_oracle : std11::false_type {}; + +template< class T > +struct is_basic_string_span_oracle< basic_string_span > : std11::true_type {}; + +template< class T > +struct is_basic_string_span : is_basic_string_span_oracle< typename std11::remove_cv::type > {}; + +template< class T > +gsl_api inline gsl_constexpr14 std::size_t string_length( T * ptr, std::size_t max ) +{ + if ( ptr == gsl_nullptr || max <= 0 ) + return 0; + + std::size_t len = 0; + while ( len < max && ptr[len] ) // NOLINT + ++len; + + return len; +} + +} // namespace detail + +// +// basic_string_span<> - A view of contiguous characters, replace (*,len). +// +template< class T > +class basic_string_span +{ +public: + typedef T element_type; + typedef span span_type; + + typedef typename span_type::index_type index_type; + typedef typename span_type::difference_type difference_type; + + typedef typename span_type::pointer pointer ; + typedef typename span_type::reference reference ; + + typedef typename span_type::iterator iterator ; + typedef typename span_type::const_iterator const_iterator ; + typedef typename span_type::reverse_iterator reverse_iterator; + typedef typename span_type::const_reverse_iterator const_reverse_iterator; + + // construction: + +#if gsl_HAVE( IS_DEFAULT ) + gsl_api gsl_constexpr basic_string_span() gsl_noexcept = default; +#else + gsl_api gsl_constexpr basic_string_span() gsl_noexcept {} +#endif + +#if gsl_HAVE( NULLPTR ) + gsl_api gsl_constexpr basic_string_span( std::nullptr_t ptr ) gsl_noexcept + : span_( ptr, index_type( 0 ) ) + {} +#endif + + gsl_api gsl_constexpr basic_string_span( pointer ptr ) + : span_( remove_z( ptr, (std::numeric_limits::max)() ) ) + {} + + gsl_api gsl_constexpr basic_string_span( pointer ptr, index_type count ) + : span_( ptr, count ) + {} + + gsl_api gsl_constexpr basic_string_span( pointer firstElem, pointer lastElem ) + : span_( firstElem, lastElem ) + {} + + template< std::size_t N > + gsl_api gsl_constexpr basic_string_span( element_type (&arr)[N] ) + : span_( remove_z( gsl_ADDRESSOF( arr[0] ), N ) ) + {} + +#if gsl_HAVE( ARRAY ) + + template< std::size_t N > + gsl_api gsl_constexpr basic_string_span( std::array< typename std11::remove_const::type, N> & arr ) + : span_( remove_z( arr ) ) + {} + + template< std::size_t N > + gsl_api gsl_constexpr basic_string_span( std::array< typename std11::remove_const::type, N> const & arr ) + : span_( remove_z( arr ) ) + {} + +#endif + +#if gsl_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + + // Exclude: array, [basic_string,] basic_string_span + + template< class Container + gsl_REQUIRES_T(( + ! detail::is_std_array< Container >::value + && ! detail::is_basic_string_span< Container >::value + && std::is_convertible< typename Container::pointer, pointer >::value + && std::is_convertible< typename Container::pointer, decltype(std::declval().data()) >::value + )) + > + gsl_api gsl_constexpr basic_string_span( Container & cont ) + : span_( ( cont ) ) + {} + + // Exclude: array, [basic_string,] basic_string_span + + template< class Container + gsl_REQUIRES_T(( + ! detail::is_std_array< Container >::value + && ! detail::is_basic_string_span< Container >::value + && std::is_convertible< typename Container::pointer, pointer >::value + && std::is_convertible< typename Container::pointer, decltype(std::declval().data()) >::value + )) + > + gsl_api gsl_constexpr basic_string_span( Container const & cont ) + : span_( ( cont ) ) + {} + +#elif gsl_HAVE( UNCONSTRAINED_SPAN_CONTAINER_CTOR ) + + template< class Container > + gsl_api gsl_constexpr basic_string_span( Container & cont ) + : span_( cont ) + {} + + template< class Container > + gsl_api gsl_constexpr basic_string_span( Container const & cont ) + : span_( cont ) + {} + +#else + + template< class U > + gsl_api gsl_constexpr basic_string_span( span const & rhs ) + : span_( rhs ) + {} + +#endif + +#if gsl_FEATURE_TO_STD( WITH_CONTAINER ) + + template< class Container > + gsl_api gsl_constexpr basic_string_span( with_container_t, Container & cont ) + : span_( with_container, cont ) + {} +#endif + +#if gsl_HAVE( IS_DEFAULT ) +# if gsl_BETWEEN( gsl_COMPILER_GNUC_VERSION, 440, 600 ) + gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) = default; + + gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) = default; +# else + gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) gsl_noexcept = default; + + gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) gsl_noexcept = default; +# endif +#endif + + template< class U +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + gsl_REQUIRES_T(( std::is_convertible::pointer, pointer>::value )) +#endif + > + gsl_api gsl_constexpr basic_string_span( basic_string_span const & rhs ) + : span_( reinterpret_cast( rhs.data() ), rhs.length() ) // NOLINT + {} + +#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 + template< class U + gsl_REQUIRES_T(( std::is_convertible::pointer, pointer>::value )) + > + gsl_api gsl_constexpr basic_string_span( basic_string_span && rhs ) + : span_( reinterpret_cast( rhs.data() ), rhs.length() ) // NOLINT + {} +#endif + + template< class CharTraits, class Allocator > + gsl_api gsl_constexpr basic_string_span( + std::basic_string< typename std11::remove_const::type, CharTraits, Allocator > & str ) + : span_( gsl_ADDRESSOF( str[0] ), str.length() ) + {} + + template< class CharTraits, class Allocator > + gsl_api gsl_constexpr basic_string_span( + std::basic_string< typename std11::remove_const::type, CharTraits, Allocator > const & str ) + : span_( gsl_ADDRESSOF( str[0] ), str.length() ) + {} + + // destruction, assignment: + +#if gsl_HAVE( IS_DEFAULT ) + gsl_api ~basic_string_span() gsl_noexcept = default; + + gsl_api basic_string_span & operator=( basic_string_span const & rhs ) gsl_noexcept = default; + + gsl_api basic_string_span & operator=( basic_string_span && rhs ) gsl_noexcept = default; +#endif + + // sub span: + + gsl_api gsl_constexpr basic_string_span first( index_type count ) const + { + return span_.first( count ); + } + + gsl_api gsl_constexpr basic_string_span last( index_type count ) const + { + return span_.last( count ); + } + + gsl_api gsl_constexpr basic_string_span subspan( index_type offset ) const + { + return span_.subspan( offset ); + } + + gsl_api gsl_constexpr basic_string_span subspan( index_type offset, index_type count ) const + { + return span_.subspan( offset, count ); + } + + // observers: + + gsl_api gsl_constexpr index_type length() const gsl_noexcept + { + return span_.size(); + } + + gsl_api gsl_constexpr index_type size() const gsl_noexcept + { + return span_.size(); + } + + gsl_api gsl_constexpr index_type length_bytes() const gsl_noexcept + { + return span_.size_bytes(); + } + + gsl_api gsl_constexpr index_type size_bytes() const gsl_noexcept + { + return span_.size_bytes(); + } + + gsl_api gsl_constexpr bool empty() const gsl_noexcept + { + return size() == 0; + } + + gsl_api gsl_constexpr reference operator[]( index_type idx ) const + { + return span_[idx]; + } + + gsl_api gsl_constexpr reference operator()( index_type idx ) const + { + return span_[idx]; + } + + gsl_api gsl_constexpr pointer data() const gsl_noexcept + { + return span_.data(); + } + + gsl_api iterator begin() const gsl_noexcept + { + return span_.begin(); + } + + gsl_api iterator end() const gsl_noexcept + { + return span_.end(); + } + + gsl_api reverse_iterator rbegin() const gsl_noexcept + { + return span_.rbegin(); + } + + gsl_api reverse_iterator rend() const gsl_noexcept + { + return span_.rend(); + } + + // const version not in p0123r2: + + gsl_api const_iterator cbegin() const gsl_noexcept + { + return span_.cbegin(); + } + + gsl_api const_iterator cend() const gsl_noexcept + { + return span_.cend(); + } + + gsl_api const_reverse_iterator crbegin() const gsl_noexcept + { + return span_.crbegin(); + } + + gsl_api const_reverse_iterator crend() const gsl_noexcept + { + return span_.crend(); + } + +private: + gsl_api static gsl_constexpr14 span_type remove_z( pointer const & sz, std::size_t max ) + { + return span_type( sz, detail::string_length( sz, max ) ); + } + +#if gsl_HAVE( ARRAY ) + template< size_t N > + gsl_api static gsl_constexpr14 span_type remove_z( std::array::type, N> & arr ) + { + return remove_z( gsl_ADDRESSOF( arr[0] ), narrow_cast< std::size_t >( N ) ); + } + + template< size_t N > + gsl_api static gsl_constexpr14 span_type remove_z( std::array::type, N> const & arr ) + { + return remove_z( gsl_ADDRESSOF( arr[0] ), narrow_cast< std::size_t >( N ) ); + } +#endif + +private: + span_type span_; +}; + +// basic_string_span comparison functions: + +#if gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator==( basic_string_span const & l, U const & u ) gsl_noexcept +{ + const basic_string_span< typename std11::add_const::type > r( u ); + + return l.size() == r.size() + && std::equal( l.begin(), l.end(), r.begin() ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator<( basic_string_span const & l, U const & u ) gsl_noexcept +{ + const basic_string_span< typename std11::add_const::type > r( u ); + + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator==( U const & u, basic_string_span const & r ) gsl_noexcept +{ + const basic_string_span< typename std11::add_const::type > l( u ); + + return l.size() == r.size() + && std::equal( l.begin(), l.end(), r.begin() ); +} + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator<( U const & u, basic_string_span const & r ) gsl_noexcept +{ + const basic_string_span< typename std11::add_const::type > l( u ); + + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} +#endif + +#else //gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + +template< class T > +gsl_api inline gsl_constexpr14 bool operator==( basic_string_span const & l, basic_string_span const & r ) gsl_noexcept +{ + return l.size() == r.size() + && std::equal( l.begin(), l.end(), r.begin() ); +} + +template< class T > +gsl_api inline gsl_constexpr14 bool operator<( basic_string_span const & l, basic_string_span const & r ) gsl_noexcept +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +#endif // gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator!=( basic_string_span const & l, U const & r ) gsl_noexcept +{ + return !( l == r ); +} + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator<=( basic_string_span const & l, U const & r ) gsl_noexcept +{ +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) || ! gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + return !( r < l ); +#else + basic_string_span< typename std11::add_const::type > rr( r ); + return !( rr < l ); +#endif +} + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator>( basic_string_span const & l, U const & r ) gsl_noexcept +{ +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) || ! gsl_CONFIG( ALLOWS_NONSTRICT_SPAN_COMPARISON ) + return ( r < l ); +#else + basic_string_span< typename std11::add_const::type > rr( r ); + return ( rr < l ); +#endif +} + +template< class T, class U > +gsl_api inline gsl_constexpr14 bool operator>=( basic_string_span const & l, U const & r ) gsl_noexcept +{ + return !( l < r ); +} + +#if gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator!=( U const & l, basic_string_span const & r ) gsl_noexcept +{ + return !( l == r ); +} + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator<=( U const & l, basic_string_span const & r ) gsl_noexcept +{ + return !( r < l ); +} + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator>( U const & l, basic_string_span const & r ) gsl_noexcept +{ + return ( r < l ); +} + +template< class T, class U + gsl_REQUIRES_T(( !detail::is_basic_string_span::value )) +> +gsl_api inline gsl_constexpr14 bool operator>=( U const & l, basic_string_span const & r ) gsl_noexcept +{ + return !( l < r ); +} + +#endif // gsl_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +// convert basic_string_span to byte span: + +template< class T > +gsl_api inline span< const byte > as_bytes( basic_string_span spn ) gsl_noexcept +{ + return span< const byte >( reinterpret_cast( spn.data() ), spn.size_bytes() ); // NOLINT +} + +// +// String types: +// + +typedef char * zstring; +typedef const char * czstring; + +#if gsl_HAVE( WCHAR ) +typedef wchar_t * zwstring; +typedef const wchar_t * cwzstring; +#endif + +typedef basic_string_span< char > string_span; +typedef basic_string_span< char const > cstring_span; + +#if gsl_HAVE( WCHAR ) +typedef basic_string_span< wchar_t > wstring_span; +typedef basic_string_span< wchar_t const > cwstring_span; +#endif + +// to_string() allow (explicit) conversions from string_span to string + +#if 0 + +template< class T > +gsl_api inline std::basic_string< typename std::remove_const::type > to_string( basic_string_span spn ) +{ + std::string( spn.data(), spn.length() ); +} + +#else + +gsl_api inline std::string to_string( string_span const & spn ) +{ + return std::string( spn.data(), spn.length() ); +} + +gsl_api inline std::string to_string( cstring_span const & spn ) +{ + return std::string( spn.data(), spn.length() ); +} + +#if gsl_HAVE( WCHAR ) + +gsl_api inline std::wstring to_string( wstring_span const & spn ) +{ + return std::wstring( spn.data(), spn.length() ); +} + +gsl_api inline std::wstring to_string( cwstring_span const & spn ) +{ + return std::wstring( spn.data(), spn.length() ); +} + +#endif // gsl_HAVE( WCHAR ) +#endif // to_string() + +// +// Stream output for string_span types +// + +namespace detail { + +template< class Stream > +gsl_api void write_padding( Stream & os, std::streamsize n ) +{ + for ( std::streamsize i = 0; i < n; ++i ) + os.rdbuf()->sputc( os.fill() ); +} + +template< class Stream, class Span > +gsl_api Stream & write_to_stream( Stream & os, Span const & spn ) +{ + typename Stream::sentry sentry( os ); + + if ( !os ) + return os; + + const std::streamsize length = narrow( spn.length() ); + + // Whether, and how, to pad + const bool pad = ( length < os.width() ); + const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right; + + if ( left_pad ) + write_padding( os, os.width() - length ); + + // Write span characters + os.rdbuf()->sputn( spn.begin(), length ); + + if ( pad && !left_pad ) + write_padding( os, os.width() - length ); + + // Reset output stream width + os.width(0); + + return os; +} + +} // namespace detail + +template< typename Traits > +gsl_api std::basic_ostream< char, Traits > & operator<<( std::basic_ostream< char, Traits > & os, string_span const & spn ) +{ + return detail::write_to_stream( os, spn ); +} + +template< typename Traits > +gsl_api std::basic_ostream< char, Traits > & operator<<( std::basic_ostream< char, Traits > & os, cstring_span const & spn ) +{ + return detail::write_to_stream( os, spn ); +} + +#if gsl_HAVE( WCHAR ) + +template< typename Traits > +gsl_api std::basic_ostream< wchar_t, Traits > & operator<<( std::basic_ostream< wchar_t, Traits > & os, wstring_span const & spn ) +{ + return detail::write_to_stream( os, spn ); +} + +template< typename Traits > +gsl_api std::basic_ostream< wchar_t, Traits > & operator<<( std::basic_ostream< wchar_t, Traits > & os, cwstring_span const & spn ) +{ + return detail::write_to_stream( os, spn ); +} + +#endif // gsl_HAVE( WCHAR ) + +// +// ensure_sentinel() +// +// Provides a way to obtain a span from a contiguous sequence +// that ends with a (non-inclusive) sentinel value. +// +// Will fail-fast if sentinel cannot be found before max elements are examined. +// +namespace detail { + +template< class T, class SizeType, const T Sentinel > +gsl_api static span ensure_sentinel( T * seq, SizeType max = (std::numeric_limits::max)() ) +{ + typedef T * pointer; + + gsl_SUPPRESS_MSVC_WARNING( 26429, "f.23: symbol 'cur' is never tested for nullness, it can be marked as not_null" ) + + pointer cur = seq; + + while ( static_cast( cur - seq ) < max && *cur != Sentinel ) + ++cur; + + Expects( *cur == Sentinel ); + + return span( seq, narrow_cast< typename span::index_type >( cur - seq ) ); +} +} // namespace detail + +// +// ensure_z - creates a string_span for a czstring or cwzstring. +// Will fail fast if a null-terminator cannot be found before +// the limit of size_type. +// + +template< class T > +gsl_api inline span ensure_z( T * const & sz, size_t max = (std::numeric_limits::max)() ) +{ + return detail::ensure_sentinel( sz, max ); +} + +template< class T, size_t N > +gsl_api inline span ensure_z( T (&sz)[N] ) +{ + return ensure_z( gsl_ADDRESSOF( sz[0] ), N ); +} + +# if gsl_HAVE( TYPE_TRAITS ) + +template< class Container > +gsl_api inline span< typename std::remove_pointer::type > +ensure_z( Container & cont ) +{ + return ensure_z( cont.data(), cont.length() ); +} +# endif + +} // namespace gsl + +#if gsl_CPP11_OR_GREATER || gsl_COMPILER_MSVC_VERSION >= 120 + +namespace std { + +template<> +struct hash< gsl::byte > +{ +public: + std::size_t operator()( gsl::byte v ) const gsl_noexcept + { + return gsl::to_integer( v ); + } +}; + +} // namespace std + +#endif + +gsl_RESTORE_MSVC_WARNINGS() + +#endif // GSL_GSL_LITE_HPP_INCLUDED + +// end of file