Merge branch 'develop' into adding_libmesh_to_docker

This commit is contained in:
Jonathan Shimwell 2021-10-11 10:31:11 +01:00 committed by GitHub
commit 0a2016cece
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
89 changed files with 2037 additions and 463 deletions

View file

@ -20,7 +20,6 @@ env:
COVERALLS_PARALLEL: true
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
main:
runs-on: ubuntu-20.04
@ -61,10 +60,10 @@ jobs:
python-version: 3.8
omp: n
mpi: y
name: 'Python ${{ matrix.python-version }} (omp=${{ matrix.omp }},
name: "Python ${{ matrix.python-version }} (omp=${{ matrix.omp }},
mpi=${{ matrix.mpi }}, dagmc=${{ matrix.dagmc }},
libmesh=${{ matrix.libmesh }}, event=${{ matrix.event }}
vectfit=${{ matrix.vectfit }})'
vectfit=${{ matrix.vectfit }})"
env:
MPI: ${{ matrix.mpi }}
@ -78,24 +77,23 @@ jobs:
steps:
- uses: actions/checkout@v2
-
name: Set up Python ${{ matrix.python-version }}
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
-
name: Environment Variables
- name: Environment Variables
run: |
echo "DAGMC_ROOT=$HOME/DAGMC"
echo "OPENMC_CROSS_SECTIONS=$HOME/nndc_hdf5/cross_sections.xml" >> $GITHUB_ENV
echo "OPENMC_ENDF_DATA=$HOME/endf-b-vii.1" >> $GITHUB_ENV
-
name: Apt dependencies
- name: Apt dependencies
shell: bash
run: |
sudo apt -y update
sudo apt install -y libmpich-dev \
sudo apt install -y libpng-dev \
libmpich-dev \
libnetcdf-dev \
libpnetcdf-dev \
libhdf5-serial-dev \
@ -105,23 +103,21 @@ jobs:
sudo update-alternatives --set mpirun /usr/bin/mpirun.mpich
sudo update-alternatives --set mpi-x86_64-linux-gnu /usr/include/x86_64-linux-gnu/mpich
-
name: install
- name: install
shell: bash
run: |
echo "$HOME/NJOY2016/build" >> $GITHUB_PATH
$GITHUB_WORKSPACE/tools/ci/gha-install.sh
-
name: before
- name: before
shell: bash
run: $GITHUB_WORKSPACE/tools/ci/gha-before-script.sh
-
name: test
- name: test
shell: bash
run: $GITHUB_WORKSPACE/tools/ci/gha-script.sh
-
name: after_success
- name: after_success
shell: bash
run: |
cpp-coveralls -i src -i include --exclude-pattern "/usr/*" --dump cpp_cov.json
@ -131,8 +127,8 @@ jobs:
needs: main
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true

3
.gitignore vendored
View file

@ -64,6 +64,7 @@ scripts/*.tar.*
scripts/G4EMLOW*/
# Images
*.png
*.ppm
*.voxel
*.vti
@ -103,4 +104,4 @@ CMakeSettings.json
.vscode/
# Python pickle files
*.pkl
*.pkl

View file

@ -79,6 +79,11 @@ if(libmesh)
find_package(LIBMESH REQUIRED)
endif()
#===============================================================================
# libpng
#===============================================================================
find_package(PNG)
#===============================================================================
# HDF5 for binary output
#===============================================================================
@ -214,8 +219,6 @@ add_subdirectory(vendor/xtensor)
# GSL header-only library
#===============================================================================
set(GSL_LITE_OPT_INSTALL_COMPAT_HEADER ON CACHE BOOL
"Install MS-GSL compatibility header <gsl/gsl>")
add_subdirectory(vendor/gsl-lite)
# Make sure contract violations throw exceptions
@ -425,6 +428,11 @@ if(libmesh)
target_link_libraries(libopenmc PkgConfig::LIBMESH)
endif()
if (PNG_FOUND)
target_compile_definitions(libopenmc PRIVATE USE_LIBPNG)
target_link_libraries(libopenmc PNG::PNG)
endif()
#===============================================================================
# openmc executable
#===============================================================================

View file

@ -68,8 +68,8 @@ RUN apt-get update -y && \
apt-get install -y \
python3-pip python-is-python3 wget git build-essential cmake \
mpich libmpich-dev libhdf5-serial-dev libhdf5-mpich-dev \
imagemagick && \
apt-get autoremove -y
libpng-dev && \
apt-get autoremove
# Update system-provided pip
RUN pip install --upgrade pip

View file

@ -1,4 +1,5 @@
Copyright (c) 2011-2021 Massachusetts Institute of Technology and OpenMC contributors
Copyright (c) 2011-2021 Massachusetts Institute of Technology, UChicago Argonne
LLC, and OpenMC contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -16,6 +16,8 @@ if(@libmesh@)
pkg_check_modules(LIBMESH REQUIRED @LIBMESH_PC_FILE@>=1.6.0 IMPORTED_TARGET)
endif()
find_package(PNG)
if(NOT TARGET OpenMC::libopenmc)
include("${OpenMC_CMAKE_DIR}/OpenMCTargets.cmake")
endif()

View file

@ -62,7 +62,7 @@ master_doc = 'index'
# General information about the project.
project = 'OpenMC'
copyright = '2011-2021, Massachusetts Institute of Technology and OpenMC contributors'
copyright = '2011-2021, Massachusetts Institute of Technology, UChicago Argonne LLC, 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

View file

@ -406,3 +406,14 @@ Each ``<dagmc_universe>`` element can have the following attributes or sub-eleme
A required string indicating the file to be loaded representing the DAGMC universe.
*Default*: None
.. note:: A geometry.xml file containing only a DAGMC model for a file named `dagmc.h5m` (no CSG)
looks as follows
.. code-block:: xml
<?xml version='1.0' encoding='utf-8'?>
<geometry>
<dagmc_universe filename="dagmc.h5m" id="1" />
</geometry>

View file

@ -10,9 +10,9 @@ of the plots.xml is simply ``<plots>`` and any number output plots can be
defined with ``<plot>`` sub-elements. Two plot types are currently implemented
in openMC:
* ``slice`` 2D pixel plot along one of the major axes. Produces a PPM image
* ``slice`` 2D pixel plot along one of the major axes. Produces a PNG image
file.
* ``voxel`` 3D voxel data dump. Produces a binary file containing voxel xyz
* ``voxel`` 3D voxel data dump. Produces an HDF5 file containing voxel xyz
position and cell or material id.
@ -68,20 +68,14 @@ sub-elements:
:type:
Keyword for type of plot to be produced. Currently only "slice" and "voxel"
plots are implemented. The "slice" plot type creates 2D pixel maps saved in
the PPM file format. PPM files can be displayed in most viewers (e.g. the
default Gnome viewer, IrfanView, etc.). The "voxel" plot type produces a
binary datafile containing voxel grid positioning and the cell or material
(specified by the ``color`` tag) at the center of each voxel. These
datafiles can be processed into VTK files using the :ref:`scripts_voxel`
script provided with OpenMC, and subsequently viewed with a 3D viewer such
as VISIT or Paraview. See the :ref:`io_voxel` for information about the
datafile structure.
the PNG file format. The "voxel" plot type produces a binary datafile
containing voxel grid positioning and the cell or material (specified by the
``color`` tag) at the center of each voxel. Voxel plot files can be
processed into VTK files using the :ref:`scripts_voxel` script provided with
OpenMC and subsequently viewed with a 3D viewer such as VISIT or Paraview.
See the :ref:`io_voxel` for information about the datafile structure.
.. note:: Since the PPM format is saved without any kind of compression,
the resulting file sizes can be quite large. Saving the image in
the PNG format can often times reduce the file size by orders of
magnitude without any loss of image quality. Likewise,
high-resolution voxel files produced by OpenMC can be quite large,
.. note:: High-resolution voxel files produced by OpenMC can be quite large,
but the equivalent VTK files will be significantly smaller.
*Default*: "slice"
@ -94,11 +88,6 @@ attribute or sub-element:
directions for "slice" and "voxel" plots, respectively. Should be two or
three integers separated by spaces.
.. warning:: The ``pixels`` input determines the output file size. For the
PPM format, 10 million pixels will result in a file just under
30 MB in size. A 10 million voxel binary file will be around
40 MB.
.. warning:: If the aspect ratio defined in ``pixels`` does not match the
aspect ratio defined in ``width`` the plot may appear stretched
or squeezed.

View file

@ -619,16 +619,17 @@ variable and whose sub-elements/attributes are as follows:
:type:
The type of the distribution. Valid options are "uniform", "discrete",
"tabular", "maxwell", and "watt". The "uniform" option produces variates
sampled from a uniform distribution over a finite interval. The "discrete"
option produces random variates that can assume a finite number of values
(i.e., a distribution characterized by a probability mass function). The
"tabular" option produces random variates sampled from a tabulated
"tabular", "maxwell", "watt", and "mixture". The "uniform" option produces
variates sampled from a uniform distribution over a finite interval. The
"discrete" option produces random variates that can assume a finite number
of values (i.e., a distribution characterized by a probability mass function).
The "tabular" option produces random variates sampled from a tabulated
distribution where the density function is either a histogram or
linearly-interpolated between tabulated points. The "watt" option produces
random variates is sampled from a Watt fission spectrum (only used for
energies). The "maxwell" option produce variates sampled from a Maxwell
fission spectrum (only used for energies).
fission spectrum (only used for energies). The "mixture" option produces samples
from univariate sub-distributions with given probabilities.
*Default*: None
@ -651,12 +652,22 @@ variable and whose sub-elements/attributes are as follows:
.. note:: The above format should be used even when using the multi-group
:ref:`energy_mode`.
:interpolation:
For a "tabular" distribution, ``interpolation`` can be set to "histogram" or
"linear-linear" thereby specifying how tabular points are to be interpolated.
*Default*: histogram
:pair:
For a "mixture" distribution, this element provides a distribution and its corresponding probability.
:probability:
An attribute or ``pair`` that provides the probability of a univariate distribution within a "mixture" distribution.
:dist:
This sub-element of a ``pair`` element provides information on the corresponding univariate distribution.
-------------------------
``<state_point>`` Element
-------------------------

View file

@ -318,6 +318,10 @@ attributes/sub-elements:
:dimension:
The number of mesh cells in each direction. (For regular mesh only.)
:length_multiplier:
A multiplicative factor to apply to the mesh coordinates in all directions.
(For unstructured 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. (For regular mesh only.)

View file

@ -4,7 +4,8 @@
License Agreement
=================
Copyright © 2011-2021 Massachusetts Institute of Technology and OpenMC contributors
Copyright © 2011-2021 Massachusetts Institute of Technology, UChicago Argonne
LLC, and OpenMC contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -100,7 +100,7 @@ directly from the package manager:
.. code-block:: sh
sudo apt install g++ cmake libhdf5-dev
sudo apt install g++ cmake libhdf5-dev libpng-dev
After the packages have been installed, follow the instructions below for
building and installing OpenMC from source.

View file

@ -219,6 +219,15 @@ Prerequisites
.. admonition:: Optional
:class: note
* libpng_ official reference PNG library
OpenMC's built-in plotting capabilities use the libpng library to produce
compressed PNG files. In the absence of this library, OpenMC will fallback
to writing PPM files, which are uncompressed and only supported by select
image viewers. libpng can be installed on Ddebian derivates with::
sudo apt install libpng-dev
* An MPI implementation for distributed-memory parallel runs
To compile with support for parallel runs on a distributed-memory

View file

@ -80,26 +80,13 @@ assign them to a :class:`openmc.Plots` collection and export it to XML::
plots += [plot2, plot3]
plots.export_to_xml()
To actually generate the plots, run the :func:`openmc.plot_geometry`
function. Alternatively, run the :ref:`scripts_openmc` executable with the
``--plot`` command-line flag. When that has finished, you will have one or more
``.ppm`` files, i.e., `portable pixmap
<http://netpbm.sourceforge.net/doc/ppm.html>`_ files. On some Linux
distributions, these ``.ppm`` files are natively viewable. If you find that
you're unable to open them on your system (or you don't like the fact that they
are not compressed), you may want to consider converting them to another format.
This is easily accomplished with the ``convert`` command available on most Linux
distributions as part of the `ImageMagick
<http://www.imagemagick.org/script/convert.php>`_ package. (On Debian
derivatives: ``sudo apt install imagemagick``). Images are then converted like:
.. code-block:: sh
convert myplot.ppm myplot.png
Alternatively, if you're working within a `Jupyter <https://jupyter.org/>`_
Notebook or QtConsole, you can use the :func:`openmc.plot_inline` to run OpenMC
in plotting mode and display the resulting plot within the notebook.
To actually generate the plots, run the :func:`openmc.plot_geometry` function.
Alternatively, run the :ref:`scripts_openmc` executable with the ``--plot``
command-line flag. When that has finished, you will have one or more ``.png``
files. Alternatively, if you're working within a `Jupyter
<https://jupyter.org/>`_ Notebook or QtConsole, you can use the
:func:`openmc.plot_inline` to run OpenMC in plotting mode and display the
resulting plot within the notebook.
.. _usersguide_voxel:

View file

@ -10,7 +10,7 @@
#include "hdf5.h"
#include "pugixml.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/constants.h"
#include "openmc/memory.h" // for unique_ptr
@ -244,14 +244,14 @@ public:
explicit CSGCell(pugi::xml_node cell_node);
bool contains(Position r, Direction u, int32_t on_surface) const;
bool contains(Position r, Direction u, int32_t on_surface) const override;
std::pair<double, int32_t> distance(
Position r, Direction u, int32_t on_surface, Particle* p) const;
Position r, Direction u, int32_t on_surface, Particle* p) const override;
void to_hdf5_inner(hid_t group_id) const override;
BoundingBox bounding_box() const;
BoundingBox bounding_box() const override;
protected:
bool contains_simple(Position r, Direction u, int32_t on_surface) const;

View file

@ -24,6 +24,14 @@ public:
virtual double sample(uint64_t* seed) const = 0;
};
using UPtrDist = unique_ptr<Distribution>;
//! Return univariate probability distribution specified in XML file
//! \param[in] node XML node representing distribution
//! \return Unique pointer to distribution
UPtrDist distribution_from_xml(pugi::xml_node node);
//==============================================================================
//! A discrete distribution (probability mass function)
//==============================================================================
@ -222,12 +230,27 @@ private:
vector<double> x_; //! Possible outcomes
};
using UPtrDist = unique_ptr<Distribution>;
//==============================================================================
//! Mixture distribution
//==============================================================================
class Mixture : public Distribution {
public:
explicit Mixture(pugi::xml_node node);
//! Sample a value from the distribution
//! \param seed Pseudorandom number seed pointer
//! \return Sampled value
double sample(uint64_t* seed) const;
private:
// Storrage for probability + distribution
using DistPair = std::pair<double, UPtrDist>;
vector<DistPair>
distribution_; //!< sub-distributions + cummulative probabilities
};
//! Return univariate probability distribution specified in XML file
//! \param[in] node XML node representing distribution
//! \return Unique pointer to distribution
UPtrDist distribution_from_xml(pugi::xml_node node);
} // namespace openmc

View file

@ -6,7 +6,7 @@
#include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <hdf5.h>
#include "openmc/bremsstrahlung.h"

View file

@ -331,6 +331,15 @@ public:
true}; //!< Write tallies onto the unstructured mesh at the end of a run
std::string filename_; //!< Path to unstructured mesh file
protected:
//! Set the length multiplier to apply to each point in the mesh
void set_length_multiplier(const double length_multiplier);
// Data members
double length_multiplier_ {
1.0}; //!< Constant multiplication factor to apply to mesh coordinates
bool specified_length_multiplier_ {false};
private:
//! Setup method for the mesh. Builds data structures,
//! sets up element mapping, creates bounding boxes, etc.
@ -344,7 +353,7 @@ public:
// Constructors
MOABMesh() = default;
MOABMesh(pugi::xml_node);
MOABMesh(const std::string& filename);
MOABMesh(const std::string& filename, double length_multiplier = 1.0);
MOABMesh(std::shared_ptr<moab::Interface> external_mbi);
// Overridden Methods
@ -492,7 +501,7 @@ class LibMesh : public UnstructuredMesh {
public:
// Constructors
LibMesh(pugi::xml_node node);
LibMesh(const std::string& filename);
LibMesh(const std::string& filename, double length_multiplier = 1.0);
// Overridden Methods
void bins_crossed(Position r0, Position r1, const Direction& u,

View file

@ -7,7 +7,7 @@
#include <unordered_map>
#include <utility> // for pair
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <hdf5.h>
#include "openmc/array.h"

View file

@ -7,7 +7,7 @@
#include "openmc/vector.h"
#include "xtensor/xtensor.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <hdf5.h>
#include <string>

View file

@ -30,9 +30,7 @@ namespace model {
extern std::unordered_map<int, int> plot_map; //!< map of plot ids to index
extern vector<Plot> plots; //!< Plot instance container
extern uint64_t
plotter_prn_seeds[N_STREAMS]; // Random number seeds used for plotter
extern int plotter_stream; // Stream index used by the plotter
extern uint64_t plotter_seed; // Stream index used by the plotter
} // namespace model
@ -239,11 +237,18 @@ public:
//! \param[out] image data associated with the plot object
void draw_mesh_lines(Plot const& pl, ImageData& data);
//! Write a ppm image to file using a plot object's image data
//! Write a PPM image using a plot object's image data
//! \param[in] plot object
//! \param[out] image data associated with the plot object
void output_ppm(Plot const& pl, const ImageData& data);
#ifdef USE_LIBPNG
//! Write a PNG image using a plot object's image data
//! \param[in] plot object
//! \param[out] image data associated with the plot object
void output_png(Plot const& pl, const ImageData& data);
#endif
//! Initialize a voxel file
//! \param[in] id of an open hdf5 file
//! \param[in] dimensions of the voxel file (dx, dy, dz)
@ -274,9 +279,12 @@ void voxel_finalize(hid_t dspace, hid_t dset, hid_t memspace);
//! Read plot specifications from a plots.xml file
void read_plots_xml();
//! Create a ppm image for a plot object
//! Clear memory
void free_memory_plot();
//! Create an image for a plot object
//! \param[in] plot object
void create_ppm(Plot const& pl);
void create_image(Plot const& pl);
//! Create an hdf5 voxel file for a plot object
//! \param[in] plot object

View file

@ -7,7 +7,7 @@
#include <string>
#include "hdf5.h"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/reaction_product.h"
#include "openmc/vector.h"

View file

@ -32,7 +32,8 @@ extern "C" bool cmfd_run; //!< is a CMFD run?
extern bool
delayed_photon_scaling; //!< Scale fission photon yield to include delayed
extern "C" bool entropy_on; //!< calculate Shannon entropy?
extern bool event_based; //!< use event-based mode (instead of history-based)
extern "C" bool
event_based; //!< use event-based mode (instead of history-based)
extern bool legendre_to_tabular; //!< convert Legendre distributions to tabular?
extern bool material_cell_offsets; //!< create material cells offsets?
extern "C" bool output_summary; //!< write summary.h5?

View file

@ -6,7 +6,7 @@
#include <unordered_map>
#include "pugixml.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/constants.h"
#include "openmc/hdf5_interface.h"

View file

@ -4,7 +4,7 @@
#include "openmc/vector.h"
#include <string>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"

View file

@ -4,7 +4,7 @@
#include <cstdint>
#include <unordered_map>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -4,7 +4,7 @@
#include <cstdint>
#include <unordered_map>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/cell.h"
#include "openmc/tallies/filter.h"

View file

@ -1,7 +1,7 @@
#ifndef OPENMC_TALLIES_FILTER_COLLISIONS_H
#define OPENMC_TALLIES_FILTER_COLLISIONS_H
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <unordered_map>
#include "openmc/tallies/filter.h"

View file

@ -1,7 +1,7 @@
#ifndef OPENMC_TALLIES_FILTER_DELAYEDGROUP_H
#define OPENMC_TALLIES_FILTER_DELAYEDGROUP_H
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -1,7 +1,7 @@
#ifndef OPENMC_TALLIES_FILTER_ENERGY_H
#define OPENMC_TALLIES_FILTER_ENERGY_H
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -4,7 +4,7 @@
#include <cstdint>
#include <unordered_map>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -1,7 +1,7 @@
#ifndef OPENMC_TALLIES_FILTER_MU_H
#define OPENMC_TALLIES_FILTER_MU_H
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -3,7 +3,7 @@
#include <cmath>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -3,7 +3,7 @@
#include <string>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"

View file

@ -4,7 +4,7 @@
#include <cstdint>
#include <unordered_map>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -4,7 +4,7 @@
#include <cstdint>
#include <unordered_map>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/tallies/filter.h"
#include "openmc/vector.h"

View file

@ -10,7 +10,7 @@
#include "pugixml.hpp"
#include "xtensor/xfixed.hpp"
#include "xtensor/xtensor.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <string>
#include <unordered_map>

View file

@ -9,7 +9,7 @@
#include "pugixml.hpp"
#include "xtensor/xtensor.hpp"
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <string>
namespace openmc {

View file

@ -57,8 +57,8 @@ Indicates the default path to an HDF5 file that contains multi-group cross
section libraries if the user has not specified the <cross_sections> tag in
.I materials.xml\fP.
.SH LICENSE
Copyright \(co 2011-2021 Massachusetts Institute of Technology and OpenMC
contributors.
Copyright \(co 2011-2021 Massachusetts Institute of Technology, UChicago
Argonne LLC, and OpenMC contributors.
.PP
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

View file

@ -34,4 +34,5 @@ from . import examples
# Import a few names from the model module
from openmc.model import rectangular_prism, hexagonal_prism, Model
__version__ = '0.13.0-dev'

View file

@ -57,8 +57,13 @@ def get_decay_modes(value):
List of successive decays, e.g. ('beta-', 'neutron')
"""
return [_DECAY_MODES[int(x)][0] for x in
str(value).strip('0').replace('.', '')]
if int(value) == 10:
# The logic below would treat 10.0 as [1, 0] rather than [10] as it
# should, so we handle this case separately
return ['unknown']
else:
return [_DECAY_MODES[int(x)][0] for x in
str(value).strip('0').replace('.', '')]
class FissionProductYields(EqualityMixin):

View file

@ -5,95 +5,74 @@ import shutil
from subprocess import Popen, PIPE, STDOUT, CalledProcessError
import tempfile
from pathlib import Path
import warnings
from . import endf
import openmc.data
# For a given MAT number, give a name for the ACE table and a list of ZAID
# identifiers. This is based on Appendix C in the ENDF manual.
# For a given material, give a name for the ACE table and a list of ZAID
# identifiers.
ThermalTuple = namedtuple('ThermalTuple', ['name', 'zaids', 'nmix'])
_THERMAL_DATA = {
1: ThermalTuple('hh2o', [1001], 1),
2: ThermalTuple('parah', [1001], 1),
3: ThermalTuple('orthoh', [1001], 1),
5: ThermalTuple('hyh2', [1001], 1),
7: ThermalTuple('hzrh', [1001], 1),
8: ThermalTuple('hcah2', [1001], 1),
10: ThermalTuple('hice', [1001], 1),
11: ThermalTuple('dd2o', [1002], 1),
12: ThermalTuple('parad', [1002], 1),
13: ThermalTuple('orthod', [1002], 1),
14: ThermalTuple('dice', [1002], 1),
26: ThermalTuple('be', [4009], 1),
27: ThermalTuple('bebeo', [4009], 1),
28: ThermalTuple('bebe2c', [4009], 1),
30: ThermalTuple('graph', [6000, 6012, 6013], 1),
31: ThermalTuple('grph10', [6000, 6012, 6013], 1),
32: ThermalTuple('grph30', [6000, 6012, 6013], 1),
33: ThermalTuple('lch4', [1001], 1),
34: ThermalTuple('sch4', [1001], 1),
35: ThermalTuple('sch4p2', [1001], 1),
37: ThermalTuple('hch2', [1001], 1),
38: ThermalTuple('mesi00', [1001], 1),
39: ThermalTuple('lucite', [1001], 1),
40: ThermalTuple('benz', [1001, 6000, 6012], 2),
42: ThermalTuple('tol00', [1001], 1),
43: ThermalTuple('sisic', [14028, 14029, 14030], 1),
44: ThermalTuple('csic', [6000, 6012, 6013], 1),
45: ThermalTuple('ouo2', [8016, 8017, 8018], 1),
46: ThermalTuple('obeo', [8016, 8017, 8018], 1),
47: ThermalTuple('sio2-a', [8016, 8017, 8018, 14028, 14029, 14030], 3),
48: ThermalTuple('osap00', [92238], 1),
49: ThermalTuple('sio2-b', [8016, 8017, 8018, 14028, 14029, 14030], 3),
50: ThermalTuple('oice', [8016, 8017, 8018], 1),
51: ThermalTuple('od2o', [8016, 8017, 8018], 1),
52: ThermalTuple('mg24', [12024], 1),
53: ThermalTuple('al27', [13027], 1),
55: ThermalTuple('yyh2', [39089], 1),
56: ThermalTuple('fe56', [26056], 1),
58: ThermalTuple('zrzrh', [40000, 40090, 40091, 40092, 40094, 40096], 1),
59: ThermalTuple('si00', [14028], 1),
60: ThermalTuple('asap00', [13027], 1),
71: ThermalTuple('n-un', [7014, 7015], 1),
72: ThermalTuple('u-un', [92238], 1),
75: ThermalTuple('uuo2', [8016, 8017, 8018], 1),
'c_Al27': ThermalTuple('al27', [13027], 1),
'c_Al_in_Al2O3': ThermalTuple('asap00', [13027], 1),
'c_Be': ThermalTuple('be', [4009], 1),
'c_Be_in_BeO': ThermalTuple('bebeo', [4009], 1),
'c_Be_in_Be2C': ThermalTuple('bebe2c', [4009], 1),
'c_Be_in_FLiBe': ThermalTuple('beflib', [4009], 1),
'c_C6H6': ThermalTuple('benz', [1001, 6000, 6012], 2),
'c_C_in_SiC': ThermalTuple('csic', [6000, 6012, 6013], 1),
'c_Ca_in_CaH2': ThermalTuple('cacah2', [20040, 20042, 20043, 20044, 20046, 20048], 1),
'c_D_in_D2O': ThermalTuple('dd2o', [1002], 1),
'c_D_in_D2O_solid': ThermalTuple('dice', [1002], 1),
'c_F_in_FLiBe': ThermalTuple('fflibe', [9019], 1),
'c_Fe56': ThermalTuple('fe56', [26056], 1),
'c_Graphite': ThermalTuple('graph', [6000, 6012, 6013], 1),
'c_Graphite_10p': ThermalTuple('grph10', [6000, 6012, 6013], 1),
'c_Graphite_30p': ThermalTuple('grph30', [6000, 6012, 6013], 1),
'c_H_in_C5O2H8': ThermalTuple('lucite', [1001], 1),
'c_H_in_CaH2': ThermalTuple('hcah2', [1001], 1),
'c_H_in_CH2': ThermalTuple('hch2', [1001], 1),
'c_H_in_CH4_liquid': ThermalTuple('lch4', [1001], 1),
'c_H_in_CH4_solid': ThermalTuple('sch4', [1001], 1),
'c_H_in_CH4_solid_phase_II': ThermalTuple('sch4p2', [1001], 1),
'c_H_in_H2O': ThermalTuple('hh2o', [1001], 1),
'c_H_in_H2O_solid': ThermalTuple('hice', [1001], 1),
'c_H_in_HF': ThermalTuple('hhf', [1001], 1),
'c_H_in_Mesitylene': ThermalTuple('mesi00', [1001], 1),
'c_H_in_ParaffinicOil': ThermalTuple('hparaf', [1001], 1),
'c_H_in_Toluene': ThermalTuple('tol00', [1001], 1),
'c_H_in_UH3': ThermalTuple('huh3', [1001], 1),
'c_H_in_YH2': ThermalTuple('hyh2', [1001], 1),
'c_H_in_ZrH': ThermalTuple('hzrh', [1001], 1),
'c_H_in_ZrH2': ThermalTuple('hzrh2', [1001], 1),
'c_H_in_ZrHx': ThermalTuple('hzrhx', [1001], 1),
'c_Li_in_FLiBe': ThermalTuple('liflib', [3006, 3007], 1),
'c_Mg24': ThermalTuple('mg24', [12024], 1),
'c_N_in_UN': ThermalTuple('n-un', [7014, 7015], 1),
'c_O_in_Al2O3': ThermalTuple('osap00', [92238], 1),
'c_O_in_BeO': ThermalTuple('obeo', [8016, 8017, 8018], 1),
'c_O_in_D2O': ThermalTuple('od2o', [8016, 8017, 8018], 1),
'c_O_in_H2O_solid': ThermalTuple('oice', [8016, 8017, 8018], 1),
'c_O_in_UO2': ThermalTuple('ouo2', [8016, 8017, 8018], 1),
'c_ortho_D': ThermalTuple('orthod', [1002], 1),
'c_ortho_H': ThermalTuple('orthoh', [1001], 1),
'c_para_D': ThermalTuple('parad', [1002], 1),
'c_para_H': ThermalTuple('parah', [1001], 1),
'c_Si28': ThermalTuple('si00', [14028], 1),
'c_Si_in_SiC': ThermalTuple('sisic', [14028, 14029, 14030], 1),
'c_SiO2_alpha': ThermalTuple('sio2-a', [8016, 8017, 8018, 14028, 14029, 14030], 3),
'c_SiO2_beta': ThermalTuple('sio2-b', [8016, 8017, 8018, 14028, 14029, 14030], 3),
'c_U_in_UN': ThermalTuple('u-un', [92238], 1),
'c_U_in_UO2': ThermalTuple('uuo2', [8016, 8017, 8018], 1),
'c_Y_in_YH2': ThermalTuple('yyh2', [39089], 1),
'c_Zr_in_ZrH': ThermalTuple('zrzrh', [40000, 40090, 40091, 40092, 40094, 40096], 1),
'c_Zr_in_ZrH2': ThermalTuple('zrzrh2', [40000, 40090, 40091, 40092, 40094, 40096], 1),
'c_Zr_in_ZrHx': ThermalTuple('zrzrhx', [40000, 40090, 40091, 40092, 40094, 40096], 1),
}
def _get_thermal_data(ev, mat):
"""Return appropriate ThermalTuple, accounting for bugs."""
# JEFF assigns MAT=59 to Ca in CaH2 (which is supposed to be silicon).
if ev.info['library'][0] == 'JEFF':
if ev.material == 59:
if 'CaH2' in ''.join(ev.info['description']):
zaids = [20040, 20042, 20043, 20044, 20046, 20048]
return ThermalTuple('cacah2', zaids, 1)
# Before ENDF/B-VIII.0, crystalline graphite was MAT=31
if ev.info['library'] != ('ENDF/B', 8, 0):
if ev.material == 31:
return _THERMAL_DATA[30]
# ENDF/B incorrectly assigns MAT numbers for UO2
#
# Material | ENDF Manual | VII.0 | VII.1 | VIII.0
# ---------|-------------|-------|-------|-------
# O in UO2 | 45 | 75 | 75 | 75
# U in UO2 | 75 | 76 | 48 | 48
if ev.info['library'][0] == 'ENDF/B':
if ev.material == 75:
return _THERMAL_DATA[45]
version = ev.info['library'][1:]
if version in ((7, 1), (8, 0)) and ev.material == 48:
return _THERMAL_DATA[75]
if version == (7, 0) and ev.material == 76:
return _THERMAL_DATA[75]
# If not a problematic material, use the dictionary as is
return _THERMAL_DATA[mat]
_TEMPLATE_RECONR = """
reconr / %%%%%%%%%%%%%%%%%%% Reconstruct XS for neutrons %%%%%%%%%%%%%%%%%%%%%%%
{nendf} {npendf}
@ -170,7 +149,7 @@ acer / %%%%%%%%%%%%%%%%%%%%%%%% Write out in ACE format %%%%%%%%%%%%%%%%%%%%%%%%
{nendf} {nthermal_acer_in} 0 {nace} {ndir}
2 0 1 .{ext}/
'{library}: {zsymam_thermal} processed by NJOY'/
{mat} {temperature} '{data.name}' /
{mat} {temperature} '{data.name}' {nza} /
{zaids} /
222 64 {mt_elastic} {elastic_type} {data.nmix} {energy_max} {iwt}/
"""
@ -445,7 +424,8 @@ def make_ace(filename, temperatures=None, acer=True, xsdir=None,
def make_ace_thermal(filename, filename_thermal, temperatures=None,
ace='ace', xsdir=None, output_dir=None, error=0.001,
iwt=2, evaluation=None, evaluation_thermal=None, **kwargs):
iwt=2, evaluation=None, evaluation_thermal=None,
table_name=None, zaids=None, nmix=None, **kwargs):
"""Generate thermal scattering ACE file from ENDF files
Parameters
@ -476,6 +456,12 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None,
evaluation_thermal : openmc.data.endf.Evaluation, optional
If the ENDF thermal scattering sublibrary file contains multiple
material evaluations, this argument indicates which evaluation to use.
table_name : str, optional
Name to assign to ACE table
zaids : list of int, optional
ZAIDs that the thermal scattering data applies to
nmix : int, optional
Number of atom types in mixed moderator
**kwargs
Keyword arguments passed to :func:`openmc.data.njoy.run`
@ -499,11 +485,23 @@ def make_ace_thermal(filename, filename_thermal, temperatures=None,
ev_thermal = (evaluation_thermal if evaluation_thermal is not None
else endf.Evaluation(filename_thermal))
mat_thermal = ev_thermal.material
zsymam_thermal = ev_thermal.target['zsymam']
zsymam_thermal = ev_thermal.target['zsymam'].strip()
# Determine name, isotopes based on MAT number
data = _get_thermal_data(ev_thermal, mat_thermal)
zaids = ' '.join(str(zaid) for zaid in data.zaids[:3])
# Determine name, isotopes, and number of atom types
if table_name and zaids and nmix:
data = ThermalTuple(table_name, zaids, nmix)
else:
with warnings.catch_warnings(record=True) as w:
proper_name = openmc.data.get_thermal_name(zsymam_thermal)
if w:
raise RuntimeError(
f"Thermal scattering material {zsymam_thermal} not "
"recognized. Please contact OpenMC developers at "
"https://openmc.discourse.group.")
data = _THERMAL_DATA[proper_name]
zaids = ' '.join(str(zaid) for zaid in data.zaids)
nza = len(data.zaids)
# Determine name of library
library = '{}-{}.{}'.format(*ev_thermal.info['library'])

View file

@ -28,52 +28,62 @@ from .thermal_angle_energy import (CoherentElasticAE, IncoherentElasticAE,
_THERMAL_NAMES = {
'c_Al27': ('al', 'al27', 'al-27'),
'c_Al_in_Sapphire': ('asap00', 'asap'),
'c_Be': ('be', 'be-metal', 'be-met', 'be00'),
'c_Al27': ('al', 'al27', 'al-27', '13-al- 27'),
'c_Al_in_Al2O3': ('asap00', 'asap', 'al(al2o3)'),
'c_Be': ('be', 'be-metal', 'be-met', 'be00', 'be-metal', 'be metal', '4-be'),
'c_BeO': ('beo',),
'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00'),
'c_Be_in_BeO': ('bebeo', 'be-beo', 'be-o', 'be/o', 'bbeo00', 'be(beo)'),
'c_Be_in_Be2C': ('bebe2c',),
'c_C6H6': ('benz', 'c6h6'),
'c_C_in_SiC': ('csic', 'c-sic'),
'c_Ca_in_CaH2': ('cah', 'cah00', 'cacah2'),
'c_D_in_D2O': ('dd2o', 'd-d2o', 'hwtr', 'hw', 'dhw00'),
'c_Be_in_FLiBe': ('beflib', 'be(flibe)'),
'c_C6H6': ('benz', 'c6h6', 'benzine'),
'c_C_in_SiC': ('csic', 'c-sic', 'c(3c-sic)'),
'c_Ca_in_CaH2': ('cah', 'cah00', 'cacah2', 'ca(cah2)'),
'c_D_in_D2O': ('dd2o', 'd-d2o', 'hwtr', 'hw', 'dhw00', 'd(d2o)'),
'c_D_in_D2O_ice': ('dice',),
'c_Fe56': ('fe', 'fe56', 'fe-56'),
'c_Graphite': ('graph', 'grph', 'gr', 'gr00'),
'c_Graphite_10p': ('grph10',),
'c_Graphite_30p': ('grph30',),
'c_H_in_CaH2': ('hcah2', 'hca00'),
'c_H_in_CH2': ('hch2', 'poly', 'pol', 'h-poly', 'pol00'),
'c_H_in_CH4_liquid': ('lch4', 'lmeth'),
'c_H_in_CH4_solid': ('sch4', 'smeth'),
'c_F_in_FLiBe': ('fflibe', 'f(flibe)'),
'c_Fe56': ('fe', 'fe56', 'fe-56', '26-fe- 56'),
'c_Graphite': ('graph', 'grph', 'gr', 'gr00', 'graphite'),
'c_Graphite_10p': ('grph10', '10p graphit'),
'c_Graphite_30p': ('grph30', '30p graphit'),
'c_H_in_C5O2H8': ('lucite', 'c5o2h8', 'h-luci', 'h(lucite)'),
'c_H_in_CaH2': ('hcah2', 'hca00', 'h(cah2)'),
'c_H_in_CH2': ('hch2', 'poly', 'pol', 'h-poly', 'pol00', 'h(ch2)'),
'c_H_in_CH4_liquid': ('lch4', 'lmeth', 'l-ch4'),
'c_H_in_CH4_solid': ('sch4', 'smeth', 's-ch4'),
'c_H_in_CH4_solid_phase_II': ('sch4p2',),
'c_H_in_H2O': ('hh2o', 'h-h2o', 'lwtr', 'lw', 'lw00'),
'c_H_in_H2O_solid': ('hice', 'h-ice', 'ice00'),
'c_H_in_C5O2H8': ('lucite', 'c5o2h8', 'h-luci'),
'c_H_in_Mesitylene': ('mesi00', 'mesi'),
'c_H_in_Toluene': ('tol00', 'tol'),
'c_H_in_YH2': ('hyh2', 'h-yh2'),
'c_H_in_ZrH': ('hzrh', 'h-zrh', 'h-zr', 'h/zr', 'hzr', 'hzr00'),
'c_Mg24': ('mg', 'mg24', 'mg00'),
'c_O_in_Sapphire': ('osap00', 'osap'),
'c_O_in_BeO': ('obeo', 'o-beo', 'o-be', 'o/be', 'obeo00'),
'c_O_in_D2O': ('od2o', 'o-d2o', 'ohw00'),
'c_O_in_H2O_ice': ('oice', 'o-ice'),
'c_O_in_UO2': ('ouo2', 'o-uo2', 'o2-u', 'o2/u', 'ouo200'),
'c_N_in_UN': ('n-un',),
'c_ortho_D': ('orthod', 'orthoD', 'dortho', 'od200', 'ortod'),
'c_ortho_H': ('orthoh', 'orthoH', 'hortho', 'oh200', 'ortoh'),
'c_Si28': ('si00', 'sili'),
'c_Si_in_SiC': ('sisic', 'si-sic'),
'c_SiO2_alpha': ('sio2', 'sio2a'),
'c_SiO2_beta': ('sio2b',),
'c_para_D': ('parad', 'paraD', 'dpara', 'pd200'),
'c_para_H': ('parah', 'paraH', 'hpara', 'ph200'),
'c_U_in_UN': ('u-un',),
'c_U_in_UO2': ('uuo2', 'u-uo2', 'u-o2', 'u/o2', 'uuo200'),
'c_Y_in_YH2': ('yyh2', 'y-yh2'),
'c_Zr_in_ZrH': ('zrzrh', 'zr-zrh', 'zr-h', 'zr/h')
'c_H_in_H2O': ('hh2o', 'h-h2o', 'lwtr', 'lw', 'lw00', 'h(h2o)'),
'c_H_in_H2O_solid': ('hice', 'h-ice', 'ice00', 'h(ice-ih)', 'h(ice)'),
'c_H_in_HF': ('hhf', 'h(hf)'),
'c_H_in_Mesitylene': ('mesi00', 'mesi', 'mesi-phii'),
'c_H_in_ParaffinicOil': ('hparaf', 'h(paraffin'),
'c_H_in_Toluene': ('tol00', 'tol', 'tolue-phii'),
'c_H_in_UH3': ('huh3', 'h(uh3)'),
'c_H_in_YH2': ('hyh2', 'h-yh2', 'h(yh2)'),
'c_H_in_ZrH': ('hzrh', 'h-zrh', 'h-zr', 'h/zr', 'hzr', 'hzr00', 'h(zrh)'),
'c_H_in_ZrH2': ('hzrh2', 'h(zrh2)'),
'c_H_in_ZrHx': ('hzrhx', 'h(zrhx)'),
'c_Li_in_FLiBe': ('liflib', 'li(flibe)'),
'c_Mg24': ('mg', 'mg24', 'mg00', '24-mg'),
'c_N_in_UN': ('n-un', 'n(un)', 'n(un) l'),
'c_O_in_Al2O3': ('osap00', 'osap', 'o(al2o3)'),
'c_O_in_BeO': ('obeo', 'o-beo', 'o-be', 'o/be', 'obeo00', 'o(beo)'),
'c_O_in_D2O': ('od2o', 'o-d2o', 'ohw00', 'o(d2o)'),
'c_O_in_H2O_solid': ('oice', 'o-ice', 'o(ice-ih)'),
'c_O_in_UO2': ('ouo2', 'o-uo2', 'o2-u', 'o2/u', 'ouo200', 'o(uo2)'),
'c_ortho_D': ('orthod', 'orthoD', 'dortho', 'od200', 'ortod', 'ortho-d'),
'c_ortho_H': ('orthoh', 'orthoH', 'hortho', 'oh200', 'ortoh', 'ortho-h'),
'c_para_D': ('parad', 'paraD', 'dpara', 'pd200', 'para-d'),
'c_para_H': ('parah', 'paraH', 'hpara', 'ph200', 'para-h'),
'c_Si28': ('si00', 'sili', 'si'),
'c_Si_in_SiC': ('sisic', 'si-sic', 'si(3c-sic)'),
'c_SiO2_alpha': ('sio2', 'sio2a', 'sio2alpha'),
'c_SiO2_beta': ('sio2b', 'sio2beta'),
'c_U_in_UN': ('u-un', 'u(un)', 'u(un) l'),
'c_U_in_UO2': ('uuo2', 'u-uo2', 'u-o2', 'u/o2', 'uuo200', 'u(uo2)'),
'c_Y_in_YH2': ('yyh2', 'y-yh2', 'y(yh2)'),
'c_Zr_in_ZrH': ('zrzrh', 'zr-zrh', 'zr-h', 'zr/h', 'zr(zrh)'),
'c_Zr_in_ZrH2': ('zrzrh2', 'zr(zrh2)'),
'c_Zr_in_ZrHx': ('zrzrhx', 'zr(zrhx)'),
}

View file

@ -4,17 +4,6 @@ openmc.deplete
A depletion front-end tool.
"""
import sys
from unittest.mock import Mock
from .dummy_comm import DummyCommunicator
try:
from mpi4py import MPI
comm = MPI.COMM_WORLD
except ImportError:
MPI = Mock()
comm = DummyCommunicator()
from .nuclide import *
from .chain import *

View file

@ -22,7 +22,7 @@ from uncertainties import ufloat
from openmc.data import DataLibrary
from openmc.lib import MaterialFilter, Tally
from openmc.checkvalue import check_type, check_greater_than
from . import comm
from openmc.mpi import comm
from .results import Results
from .chain import Chain
from .results_list import ResultsList

View file

@ -10,7 +10,7 @@ import sys
from numpy import dot, zeros, newaxis, asarray
from . import comm
from openmc.mpi import comm
from openmc.checkvalue import check_type, check_greater_than
from openmc.data import JOULE_PER_EV, REACTION_MT
from openmc.lib import (

View file

@ -581,3 +581,14 @@ class SILEQIIntegrator(SIIntegrator):
proc_time += time1 + time2
return proc_time, [eos_conc, inter_conc], [res_bar]
integrator_by_name = {
'cecm': CECMIntegrator,
'predictor': PredictorIntegrator,
'cf4': CF4Integrator,
'epc_rk4': EPCRK4Integrator,
'si_celi': SICELIIntegrator,
'si_leqi': SILEQIIntegrator,
'celi': CELIIntegrator,
'leqi': LEQIIntegrator}

View file

@ -294,7 +294,8 @@ class Nuclide:
for mode_type, daughter, br in self.decay_modes:
mode_elem = ET.SubElement(elem, 'decay')
mode_elem.set('type', mode_type)
mode_elem.set('target', daughter or "Nothing")
if daughter:
mode_elem.set('target', daughter)
mode_elem.set('branching_ratio', str(br))
elem.set('reactions', str(len(self.reactions)))

View file

@ -20,7 +20,7 @@ from uncertainties import ufloat
import openmc
from openmc.checkvalue import check_value
import openmc.lib
from . import comm
from openmc.mpi import comm
from .abc import TransportOperator, OperatorResult
from .atom_number import AtomNumber
from .reaction_rates import ReactionRates
@ -176,6 +176,9 @@ class Operator(TransportOperator):
results are to be used.
diff_burnable_mats : bool
Whether to differentiate burnable materials with multiple instances
cleanup_when_done : bool
Whether to finalize and clear the shared library memory when the
depletion operation is complete. Defaults to clearing the library.
"""
_fission_helpers = {
"average": AveragedFissionYieldHelper,
@ -202,6 +205,7 @@ class Operator(TransportOperator):
self.settings = settings
self.geometry = geometry
self.diff_burnable_mats = diff_burnable_mats
self.cleanup_when_done = True
# Reduce the chain before we create more materials
if reduce_chain:
@ -317,6 +321,10 @@ class Operator(TransportOperator):
# Reset results in OpenMC
openmc.lib.reset()
# Update the number densities regardless of the source rate
self.number.set_density(vec)
self._update_materials()
# If the source rate is zero, return zero reaction rates without running
# a transport solve
if source_rate == 0.0:
@ -327,11 +335,7 @@ class Operator(TransportOperator):
# Prevent OpenMC from complaining about re-creating tallies
openmc.reset_auto_ids()
# Update status
self.number.set_density(vec)
# Update material compositions and tally nuclides
self._update_materials()
# Update tally nuclides data in preparation for transport solve
nuclides = self._get_tally_nuclides()
self._rate_helper.nuclides = nuclides
self._normalization_helper.nuclides = nuclides
@ -536,7 +540,8 @@ class Operator(TransportOperator):
# Initialize OpenMC library
comm.barrier()
openmc.lib.init(intracomm=comm)
if not openmc.lib.is_initialized:
openmc.lib.init(intracomm=comm)
# Generate tallies in memory
materials = [openmc.lib.materials[int(i)]
@ -554,7 +559,8 @@ class Operator(TransportOperator):
def finalize(self):
"""Finalize a depletion simulation and release resources."""
openmc.lib.finalize()
if self.cleanup_when_done:
openmc.lib.finalize()
def _update_materials(self):
"""Updates material compositions in OpenMC on all processes."""

View file

@ -9,7 +9,7 @@ import copy
import h5py
import numpy as np
from . import comm, MPI
from openmc.mpi import comm, MPI
from .reaction_rates import ReactionRates
VERSION_RESULTS = (1, 1)

View file

@ -3,6 +3,84 @@ from numbers import Integral
import subprocess
import openmc
from .plots import _get_plot_image
def _process_CLI_arguments(volume=False, geometry_debug=False, particles=None,
plot=False, restart_file=None, threads=None,
tracks=False, event_based=None,
openmc_exec='openmc', mpi_args=None):
"""Converts user-readable flags in to command-line arguments to be run with
the OpenMC executable via subprocess.
Parameters
----------
volume : bool, optional
Run in stochastic volume calculation mode. Defaults to False.
geometry_debug : bool, optional
Turn on geometry debugging during simulation. Defaults to False.
particles : int, optional
Number of particles to simulate per generation.
plot : bool, optional
Run in plotting mode. Defaults to False.
restart_file : str, optional
Path to restart file to use
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP threading
enabled, the default is implementation-dependent but is usually equal
to the number of hardware threads available (or a value set by the
:envvar:`OMP_NUM_THREADS` environment variable).
tracks : bool, optional
Write tracks for all particles. Defaults to False.
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
mpi_args : list of str, optional
MPI execute command and any additional MPI arguments to pass,
e.g. ['mpiexec', '-n', '8'].
.. versionadded:: 0.13.0
Returns
-------
args : Iterable of str
The runtime flags converted to CLI arguments of the OpenMC executable
"""
args = [openmc_exec]
if volume:
args.append('--volume')
if isinstance(particles, Integral) and particles > 0:
args += ['-n', str(particles)]
if isinstance(threads, Integral) and threads > 0:
args += ['-s', str(threads)]
if geometry_debug:
args.append('-g')
if event_based is not None:
if event_based:
args.append('-e')
if isinstance(restart_file, str):
args += ['-r', restart_file]
if tracks:
args.append('-t')
if plot:
args.append('-p')
if mpi_args is not None:
args = mpi_args + args
return args
def _run(args, output, cwd):
@ -59,12 +137,13 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'):
_run([openmc_exec, '-p'], output, cwd)
def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
def plot_inline(plots, openmc_exec='openmc', cwd='.'):
"""Display plots inline in a Jupyter notebook.
This function requires that you have a program installed to convert PPM
files to PNG files. Typically, that would be `ImageMagick
<https://www.imagemagick.org>`_ which includes a `convert` command.
.. versionchanged:: 0.13.0
The *convert_exec* argument was removed since OpenMC now produces
.png images directly.
Parameters
----------
@ -74,8 +153,6 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
Path to OpenMC executable
cwd : str, optional
Path to working directory to run in
convert_exec : str, optional
Command that can convert PPM files into PNG files
Raises
------
@ -83,7 +160,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
If the `openmc` executable returns a non-zero status
"""
from IPython.display import Image, display
from IPython.display import display
if not isinstance(plots, Iterable):
plots = [plots]
@ -94,16 +171,8 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'):
# Run OpenMC in geometry plotting mode
plot_geometry(False, openmc_exec, cwd)
images = []
if plots is not None:
for p in plots:
if p.filename is not None:
ppm_file = f'{p.filename}.ppm'
else:
ppm_file = f'plot_{p.id}.ppm'
png_file = ppm_file.replace('.ppm', '.png')
subprocess.check_call([convert_exec, ppm_file, png_file])
images.append(Image(png_file))
images = [_get_plot_image(p) for p in plots]
display(*images)
@ -126,8 +195,8 @@ def calculate_volumes(threads=None, output=True, cwd='.',
----------
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP threading
enabled, the default is implementation-dependent but is usually equal to
the number of hardware threads available (or a value set by the
enabled, the default is implementation-dependent but is usually equal
to the number of hardware threads available (or a value set by the
:envvar:`OMP_NUM_THREADS` environment variable).
output : bool, optional
Capture OpenMC output from standard out
@ -150,12 +219,9 @@ def calculate_volumes(threads=None, output=True, cwd='.',
openmc.VolumeCalculation
"""
args = [openmc_exec, '--volume']
if isinstance(threads, Integral) and threads > 0:
args += ['-s', str(threads)]
if mpi_args is not None:
args = mpi_args + args
args = _process_CLI_arguments(volume=True, threads=threads,
openmc_exec=openmc_exec, mpi_args=mpi_args)
_run(args, output, cwd)
@ -171,8 +237,8 @@ def run(particles=None, threads=None, geometry_debug=False,
Number of particles to simulate per generation.
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP threading
enabled, the default is implementation-dependent but is usually equal to
the number of hardware threads available (or a value set by the
enabled, the default is implementation-dependent but is usually equal
to the number of hardware threads available (or a value set by the
:envvar:`OMP_NUM_THREADS` environment variable).
geometry_debug : bool, optional
Turn on geometry debugging during simulation. Defaults to False.
@ -201,27 +267,10 @@ def run(particles=None, threads=None, geometry_debug=False,
If the `openmc` executable returns a non-zero status
"""
args = [openmc_exec]
if isinstance(particles, Integral) and particles > 0:
args += ['-n', str(particles)]
if isinstance(threads, Integral) and threads > 0:
args += ['-s', str(threads)]
if geometry_debug:
args.append('-g')
if event_based:
args.append('-e')
if isinstance(restart_file, str):
args += ['-r', restart_file]
if tracks:
args.append('-t')
if mpi_args is not None:
args = mpi_args + args
args = _process_CLI_arguments(
volume=False, geometry_debug=geometry_debug, particles=particles,
restart_file=restart_file, threads=threads, tracks=tracks,
event_based=event_based, openmc_exec=openmc_exec, mpi_args=mpi_args)
_run(args, output, cwd)

View file

@ -59,3 +59,8 @@ from .tally import *
from .settings import settings
from .math import *
from .plot import *
# Flag to denote whether or not openmc.lib.init has been called
# TODO: Establish and use a flag in the C++ code to represent the status of the
# openmc_init and openmc_finalize methods
is_initialized = False

View file

@ -2,6 +2,7 @@ from contextlib import contextmanager
from ctypes import (c_bool, c_int, c_int32, c_int64, c_double, c_char_p,
c_char, POINTER, Structure, c_void_p, create_string_buffer)
import sys
import os
import numpy as np
from numpy.ctypeslib import as_array
@ -109,9 +110,22 @@ def global_bounding_box():
return llc, urc
def calculate_volumes():
"""Run stochastic volume calculation"""
_dll.openmc_calculate_volumes()
def calculate_volumes(output=True):
"""Run stochastic volume calculation
.. versionchanged:: 0.13.0
The *output* argument was added.
Parameters
----------
output : bool, optional
Whether or not to show output. Defaults to showing output
"""
with quiet_dll(output):
_dll.openmc_calculate_volumes()
def current_batch():
@ -126,13 +140,18 @@ def current_batch():
return c_int.in_dll(_dll, 'current_batch').value
def export_properties(filename=None):
def export_properties(filename=None, output=True):
"""Export physical properties.
.. versionchanged:: 0.13.0
The *output* argument was added.
Parameters
----------
filename : str or None
Filename to export properties to (defaults to "properties.h5")
output : bool, optional
Whether or not to show output. Defaults to showing output
See Also
--------
@ -141,12 +160,15 @@ def export_properties(filename=None):
"""
if filename is not None:
filename = c_char_p(filename.encode())
_dll.openmc_properties_export(filename)
with quiet_dll(output):
_dll.openmc_properties_export(filename)
def finalize():
"""Finalize simulation and free memory"""
_dll.openmc_finalize()
openmc.lib.is_initialized = False
def find_cell(xyz):
@ -218,15 +240,20 @@ def import_properties(filename):
_dll.openmc_properties_import(filename.encode())
def init(args=None, intracomm=None):
def init(args=None, intracomm=None, output=True):
"""Initialize OpenMC
.. versionchanged:: 0.13.0
The *output* argument was added.
Parameters
----------
args : list of str
args : list of str, optional
Command-line arguments
intracomm : mpi4py.MPI.Intracomm or None
intracomm : mpi4py.MPI.Intracomm or None, optional
MPI intracommunicator
output : bool, optional
Whether or not to show output. Defaults to showing output
"""
if args is not None:
@ -252,7 +279,9 @@ def init(args=None, intracomm=None):
address = MPI._addressof(intracomm)
intracomm = c_void_p(address)
_dll.openmc_init(argc, argv, intracomm)
with quiet_dll(output):
_dll.openmc_init(argc, argv, intracomm)
openmc.lib.is_initialized = True
def is_statepoint_batch():
@ -342,9 +371,20 @@ def next_batch():
return status.value
def plot_geometry():
"""Plot geometry"""
_dll.openmc_plot_geometry()
def plot_geometry(output=True):
"""Plot geometry
.. versionchanged:: 0.13.0
The *output* argument was added.
Parameters
----------
output : bool, optional
Whether or not to show output. Defaults to showing output
"""
with quiet_dll(output):
_dll.openmc_plot_geometry()
def reset():
@ -357,9 +397,20 @@ def reset_timers():
_dll.openmc_reset_timers()
def run():
"""Run simulation"""
_dll.openmc_run()
def run(output=True):
"""Run simulation
.. versionchanged:: 0.13.0
The *output* argument was added.
Parameters
----------
output : bool, optional
Whether or not to show output. Defaults to showing output
"""
with quiet_dll(output):
_dll.openmc_run()
def simulation_init():
@ -475,3 +526,46 @@ class _FortranObjectWithID(_FortranObject):
# assigned. If the array index of the object is out of bounds, an
# OutOfBoundsError will be raised here by virtue of referencing self.id
self.id
@contextmanager
def quiet_dll(output=True):
"""This context manager allows us to suppress standard output from DLLs
Parameters
----------
output : bool
Denotes whether the output should be displayed (True) or not (False)
.. versionadded:: 0.13.0
"""
# This contextmanager is modified from that provided here:
# https://stackoverflow.com/a/14797594
if output:
yield
else:
sys.stdout.flush()
# Save the initial file descriptor states
initial_stdout = sys.stdout
initial_stdout_fno = os.dup(sys.stdout.fileno())
# Get a garbage descriptor so we can throw away output
devnull = os.open(os.devnull, os.O_WRONLY)
# Get the current stdout stream and make a duplicate of it
new_stdout = os.dup(1)
# Copy the garbage output to the stdout stream
os.dup2(devnull, 1)
os.close(devnull)
# Now point stdout to the re-defined stdout
sys.stdout = os.fdopen(new_stdout, 'w')
try:
yield
finally:
# Now we just clean up after ourselves and reset the streams
sys.stdout = initial_stdout
sys.stdout.flush()
os.dup2(initial_stdout_fno, 1)

View file

@ -172,7 +172,6 @@ class Material(_FortranObjectWithID):
@property
def nuclides(self):
return self._get_densities()[0]
return nuclides
@property
def densities(self):

View file

@ -34,6 +34,7 @@ class _Settings:
restart_run = _DLLGlobal(c_bool, 'restart_run')
run_CE = _DLLGlobal(c_bool, 'run_CE')
verbosity = _DLLGlobal(c_int, 'verbosity')
event_based = _DLLGlobal(c_bool, 'event_based')
@property
def run_mode(self):

View file

@ -615,8 +615,8 @@ class UnstructuredMesh(MeshBase):
Unique identifier for the mesh
name : str
Name of the mesh
size : int
Number of elements in the unstructured mesh
length_multiplier: float
Constant multiplier to apply to mesh coordinates
Attributes
----------
@ -626,11 +626,16 @@ class UnstructuredMesh(MeshBase):
Name of the mesh
filename : str
Name of the file containing the unstructured mesh
length_multiplier: float
Multiplicative factor to apply to mesh coordinates
library : str
Mesh library used for the unstructured mesh tally
output : bool
Indicates whether or not automatic tally output should
be generated for this mesh
specified_length_multiplier: bool
Indicates whether a non-unity length multiplier has been
applied to this mesh
volumes : Iterable of float
Volumes of the unstructured mesh elements
total_volume : float
@ -639,13 +644,16 @@ class UnstructuredMesh(MeshBase):
An iterable of element centroid coordinates, e.g. [(0.0, 0.0, 0.0),
(1.0, 1.0, 1.0), ...]
"""
def __init__(self, filename, library, mesh_id=None, name=''):
def __init__(self, filename, library, mesh_id=None, name='',
length_multiplier=1.0):
super().__init__(mesh_id, name)
self.filename = filename
self._volumes = None
self._centroids = None
self.library = library
self._output = True
self._specified_length_multiplier = False
self.length_multiplier = length_multiplier
@property
def filename(self):
@ -713,6 +721,20 @@ class UnstructuredMesh(MeshBase):
Iterable, Real)
self._centroids = centroids
@property
def length_multiplier(self):
return self._length_multiplier
@length_multiplier.setter
def length_multiplier(self, length_multiplier):
cv.check_type("Unstructured mesh length multiplier",
length_multiplier,
Real)
self._length_multiplier = length_multiplier
if (self._length_multiplier != 1.0):
self._specified_length_multiplier = True
def __repr__(self):
string = super().__repr__()
string += '{: <16}=\t{}\n'.format('\tFilename', self.filename)
@ -763,7 +785,7 @@ class UnstructuredMesh(MeshBase):
for centroid in self.centroids:
# create a point for each centroid
point_id = points.InsertNextPoint(centroid)
point_id = points.InsertNextPoint(centroid * self.length_multiplier)
# create a cell of type "Vertex" for each point
cell_id = vertices.InsertNextCell(cell_dim, (point_id,))
@ -817,6 +839,9 @@ class UnstructuredMesh(MeshBase):
mesh.centroids = np.reshape(centroids, (vol_data.shape[0], 3))
mesh.size = mesh.volumes.size
if 'length_multiplier' in group:
mesh.length_multiplier = group['length_multiplier'][()]
return mesh
def to_xml_element(self):
@ -836,6 +861,9 @@ class UnstructuredMesh(MeshBase):
subelement = ET.SubElement(element, "filename")
subelement.text = self.filename
if (self._specified_length_multiplier):
element.set("length_multiplier", str(self.length_multiplier))
return element
@classmethod
@ -855,5 +883,6 @@ class UnstructuredMesh(MeshBase):
mesh_id = int(get_text(elem, 'id'))
filename = get_text(elem, 'filename')
library = get_text(elem, 'library')
length_multiplier = float(get_text(elem, 'length_multiplier', 1.0))
return cls(filename, library, mesh_id)
return cls(filename, library, mesh_id, '', length_multiplier)

View file

@ -1,11 +1,32 @@
from collections.abc import Iterable
import operator
import os
from pathlib import Path
from numbers import Integral
import time
import warnings
import subprocess
from contextlib import contextmanager
import h5py
import openmc
from openmc.dummy_comm import DummyCommunicator
from openmc.executor import _process_CLI_arguments
from openmc.checkvalue import check_type, check_value
from openmc.exceptions import InvalidIDError
@contextmanager
def _change_directory(working_dir):
"""A context manager for executing in a provided working directory"""
start_dir = Path.cwd()
Path.mkdir(working_dir, exist_ok=True)
os.chdir(working_dir)
try:
yield
finally:
os.chdir(start_dir)
class Model:
@ -19,6 +40,10 @@ class Model:
not set, it will attempt to create a ``materials.xml`` file based on all
materials appearing in the geometry.
.. versionchanged:: 0.13.0
The model information can now be loaded in to OpenMC directly via
openmc.lib
Parameters
----------
geometry : openmc.Geometry, optional
@ -66,6 +91,30 @@ class Model:
if plots is not None:
self.plots = plots
# Store dictionaries to the materials and cells by ID and names
if materials is None:
mats = self.geometry.get_all_materials().values()
else:
mats = self.materials
cells = self.geometry.get_all_cells()
# Get the ID maps
self._materials_by_id = {mat.id: mat for mat in mats}
self._cells_by_id = {cell.id: cell for cell in cells.values()}
# Get the names maps, but since names are not unique, store a list for
# each name key. In this way when the user requests a change by a name,
# the change will be applied to all of the same name.
self._cells_by_name = {}
for cell in cells.values():
if cell.name not in self._cells_by_name:
self._cells_by_name[cell.name] = set()
self._cells_by_name[cell.name].add(cell)
self._materials_by_name = {}
for mat in mats:
if mat.name not in self._materials_by_name:
self._materials_by_name[mat.name] = set()
self._materials_by_name[mat.name].add(mat)
@property
def geometry(self):
return self._geometry
@ -86,6 +135,10 @@ class Model:
def plots(self):
return self._plots
@property
def is_initialized(self):
return openmc.lib.is_initialized
@geometry.setter
def geometry(self, geometry):
check_type('geometry', geometry, openmc.Geometry)
@ -130,6 +183,8 @@ class Model:
def from_xml(cls, geometry='geometry.xml', materials='materials.xml',
settings='settings.xml'):
"""Create model from existing XML files
When initializing this way, the user must manually load plots and
tallies.
Parameters
----------
@ -151,44 +206,159 @@ class Model:
settings = openmc.Settings.from_xml(settings)
return cls(geometry, materials, settings)
def deplete(self, timesteps, chain_file=None, method='cecm',
fission_q=None, **kwargs):
def init_lib(self, threads=None, geometry_debug=False, restart_file=None,
tracks=False, output=True, event_based=None, intracomm=None):
"""Initializes the model in memory via the C API
.. versionadded:: 0.13.0
Parameters
----------
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP
threading enabled, the default is implementation-dependent but is
usually equal to the number of hardware threads available
(or a value set by the :envvar:`OMP_NUM_THREADS` environment
variable).
geometry_debug : bool, optional
Turn on geometry debugging during simulation. Defaults to False.
restart_file : str, optional
Path to restart file to use
tracks : bool, optional
Write tracks for all particles. Defaults to False.
output : bool
Capture OpenMC output from standard out
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
intracomm : mpi4py.MPI.Intracomm or None, optional
MPI intracommunicator
"""
# TODO: right now the only way to set most of the above parameters via
# the C API are at initialization time despite use-cases existing to
# set them for individual runs. For now this functionality is exposed
# where it exists (here in init), but in the future the functionality
# should be exposed so that it can be accessed via model.run(...)
args = _process_CLI_arguments(
volume=False, geometry_debug=geometry_debug,
restart_file=restart_file, threads=threads, tracks=tracks,
event_based=event_based)
# Args adds the openmc_exec command in the first entry; remove it
args = args[1:]
self.finalize_lib()
# The Model object needs to be aware of the communicator so it can
# use it in certain cases, therefore lets store the communicator
if intracomm is not None:
self._intracomm = intracomm
else:
self._intracomm = DummyCommunicator()
if self._intracomm.rank == 0:
self.export_to_xml()
self._intracomm.barrier()
# We cannot pass DummyCommunicator to openmc.lib.init so pass instead
# the user-provided intracomm which will either be None or an mpi4py
# communicator
openmc.lib.init(args=args, intracomm=intracomm, output=output)
def finalize_lib(self):
"""Finalize simulation and free memory allocated for the C API
.. versionadded:: 0.13.0
"""
openmc.lib.finalize()
def deplete(self, timesteps, method='cecm', final_step=True,
operator_kwargs=None, directory='.', output=True,
**integrator_kwargs):
"""Deplete model using specified timesteps/power
.. versionchanged:: 0.13.0
The *final_step*, *operator_kwargs*, *directory*, and *output*
arguments were added.
Parameters
----------
timesteps : iterable of float
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 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')
fission_q : dict, optional
Dictionary of nuclides and their fission Q values [eV].
If not given, values will be pulled from the ``chain_file``.
**kwargs
Keyword arguments passed to integration function (e.g.,
:func:`openmc.deplete.integrator.cecm`)
method : str, optional
Integration method used for depletion (e.g., 'cecm', 'predictor').
Defaults to 'cecm'.
final_step : bool, optional
Indicate whether or not a transport solve should be run at the end
of the last timestep. Defaults to running this transport solve.
operator_kwargs : dict
Keyword arguments passed to the depletion Operator initializer
(e.g., :func:`openmc.deplete.Operator`)
directory : str, optional
Directory to write XML files to. If it doesn't exist already, it
will be created. Defaults to the current working directory
output : bool
Capture OpenMC output from standard out
integrator_kwargs : dict
Remaining keyword arguments passed to the depletion Integrator
initializer (e.g., :func:`openmc.deplete.integrator.cecm`).
"""
# Import the depletion module. This is done here rather than the module
# header to delay importing openmc.lib (through openmc.deplete) which
# can be tough to install properly.
if operator_kwargs is None:
op_kwargs = {}
elif isinstance(operator_kwargs, dict):
op_kwargs = operator_kwargs
else:
raise ValueError("operator_kwargs must be a dict or None")
# Import openmc.deplete here so the Model can be used even if the
# shared library is unavailable.
import openmc.deplete as dep
# Create OpenMC transport operator
op = dep.Operator(
self.geometry, self.settings, chain_file,
fission_q=fission_q,
)
# Store whether or not the library was initialized when we started
started_initialized = self.is_initialized
# Perform depletion
check_value('method', method, ('cecm', 'predictor', 'cf4', 'epc_rk4',
'si_celi', 'si_leqi', 'celi', 'leqi'))
getattr(dep.integrator, method)(op, timesteps, **kwargs)
with _change_directory(Path(directory)):
with openmc.lib.quiet_dll(output):
depletion_operator = \
dep.Operator(self.geometry, self.settings, **op_kwargs)
# Tell depletion_operator.finalize NOT to clear C API memory when
# it is done
depletion_operator.cleanup_when_done = False
# Set up the integrator
check_value('method', method,
dep.integrators.integrator_by_name.keys())
integrator_class = dep.integrators.integrator_by_name[method]
integrator = integrator_class(depletion_operator, timesteps,
**integrator_kwargs)
# Now perform the depletion
with openmc.lib.quiet_dll(output):
integrator.integrate(final_step)
# Now make the python Materials match the C API material data
for mat_id, mat in self._materials_by_id.items():
if mat.depletable:
# Get the C data
c_mat = openmc.lib.materials[mat_id]
nuclides, densities = c_mat._get_densities()
# And now we can remove isotopes and add these ones in
mat.nuclides.clear()
for nuc, density in zip(nuclides, densities):
mat.add_nuclide(nuc, density)
mat.set_density('atom/b-cm', sum(densities))
# If we didnt start intialized, we should cleanup after ourselves
if not started_initialized:
depletion_operator.cleanup_when_done = True
depletion_operator.finalize()
def export_to_xml(self, directory='.'):
"""Export model to XML files.
@ -226,6 +396,9 @@ class Model:
def import_properties(self, filename):
"""Import physical properties
.. versionchanged:: 0.13.0
This method now updates values as loaded in memory with the C API
Parameters
----------
filename : str
@ -254,32 +427,69 @@ class Model:
cell = cells[cell_id]
if cell.fill_type in ('material', 'distribmat'):
cell.temperature = group['temperature'][()]
if self.is_initialized:
lib_cell = openmc.lib.cells[cell_id]
lib_cell.set_temperature(group['temperature'][()])
# Make sure number of materials matches
mats_group = fh['materials']
n_cells = mats_group.attrs['n_materials']
if n_cells != len(materials):
raise ValueError("Number of materials in properties file doesn't "
"match current model.")
raise ValueError("Number of materials in properties file "
"doesn't match current model.")
# Update material densities
for name, group in mats_group.items():
mat_id = int(name.split()[1])
atom_density = group.attrs['atom_density']
materials[mat_id].set_density('atom/b-cm', atom_density)
if self.is_initialized:
C_mat = openmc.lib.materials[mat_id]
C_mat.set_density(atom_density, 'atom/b-cm')
def run(self, **kwargs):
"""Creates the XML files, runs OpenMC, and returns the path to the last
def run(self, particles=None, threads=None, geometry_debug=False,
restart_file=None, tracks=False, output=True, cwd='.',
openmc_exec='openmc', mpi_args=None, event_based=None):
"""Runs OpenMC. If the C API has been initialized, then the C API is
used, otherwise, this method creates the XML files and runs OpenMC via
a system call. In both cases this method returns the path to the last
statepoint file generated.
.. versionchanged:: 0.12
Instead of returning the final k-effective value, this function now
returns the path to the final statepoint written.
.. versionchanged:: 0.13.0
This method can utilize the C API for execution
Parameters
----------
**kwargs
Keyword arguments passed to :func:`openmc.run`
particles : int, optional
Number of particles to simulate per generation.
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP
threading enabled, the default is implementation-dependent but is
usually equal to the number of hardware threads available (or a
value set by the :envvar:`OMP_NUM_THREADS` environment variable).
geometry_debug : bool, optional
Turn on geometry debugging during simulation. Defaults to False.
restart_file : str, optional
Path to restart file to use
tracks : bool, optional
Write tracks for all particles. Defaults to False.
output : bool, optional
Capture OpenMC output from standard out
cwd : str, optional
Path to working directory to run in. Defaults to the current
working directory.
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
mpi_args : list of str, optional
MPI execute command and any additional MPI arguments to pass,
e.g. ['mpiexec', '-n', '8'].
event_based : None or bool, optional
Turns on event-based parallelism if True. If None, the value in
the Settings will be used.
Returns
-------
@ -289,23 +499,346 @@ class Model:
"""
self.export_to_xml()
# Setting tstart here ensures we don't pick up any pre-existing statepoint
# files in the output directory
# Setting tstart here ensures we don't pick up any pre-existing
# statepoint files in the output directory
tstart = time.time()
last_statepoint = None
openmc.run(**kwargs)
# Operate in the provided working directory
with _change_directory(Path(cwd)):
if self.is_initialized:
# Handle the run options as applicable
# First dont allow ones that must be set via init
for arg_name, arg, default in zip(
['threads', 'geometry_debug', 'restart_file', 'tracks'],
[threads, geometry_debug, restart_file, tracks],
[None, False, None, False]):
if arg != default:
msg = f"{arg_name} must be set via Model.is_initialized(...)"
raise ValueError(msg)
# Get output directory and return the last statepoint written by this run
if self.settings.output and 'path' in self.settings.output:
output_dir = Path(self.settings.output['path'])
else:
output_dir = Path.cwd()
for sp in output_dir.glob('statepoint.*.h5'):
mtime = sp.stat().st_mtime
if mtime >= tstart: # >= allows for poor clock resolution
tstart = mtime
last_statepoint = sp
init_particles = openmc.lib.settings.particles
if particles is not None:
if isinstance(particles, Integral) and particles > 0:
openmc.lib.settings.particles = particles
init_event_based = openmc.lib.settings.event_based
if event_based is not None:
openmc.lib.settings.event_based = event_based
# Then run using the C API
openmc.lib.run(output)
# Reset changes for the openmc.run kwargs handling
openmc.lib.settings.particles = init_particles
openmc.lib.settings.event_based = init_event_based
else:
# Then run via the command line
self.export_to_xml()
openmc.run(particles, threads, geometry_debug, restart_file,
tracks, output, Path('.'), openmc_exec, mpi_args,
event_based)
# Get output directory and return the last statepoint written
if self.settings.output and 'path' in self.settings.output:
output_dir = Path(self.settings.output['path'])
else:
output_dir = Path.cwd()
for sp in output_dir.glob('statepoint.*.h5'):
mtime = sp.stat().st_mtime
if mtime >= tstart: # >= allows for poor clock resolution
tstart = mtime
last_statepoint = sp
return last_statepoint
def calculate_volumes(self, threads=None, output=True, cwd='.',
openmc_exec='openmc', mpi_args=None,
apply_volumes=True):
"""Runs an OpenMC stochastic volume calculation and, if requested,
applies volumes to the model
.. versionadded:: 0.13.0
Parameters
----------
threads : int, optional
Number of OpenMP threads. If OpenMC is compiled with OpenMP
threading enabled, the default is implementation-dependent but is
usually equal to the number of hardware threads available (or a
value set by the :envvar:`OMP_NUM_THREADS` environment variable).
This currenty only applies to the case when not using the C API.
output : bool, optional
Capture OpenMC output from standard out
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
This only applies to the case when not using the C API.
mpi_args : list of str, optional
MPI execute command and any additional MPI arguments to pass,
e.g. ['mpiexec', '-n', '8'].
This only applies to the case when not using the C API.
cwd : str, optional
Path to working directory to run in. Defaults to the current
working directory.
apply_volumes : bool, optional
Whether apply the volume calculation results from this calculation
to the model. Defaults to applying the volumes.
"""
if len(self.settings.volume_calculations) == 0:
# Then there is no volume calculation specified
raise ValueError("The Settings.volume_calculation attribute must"
" be specified before executing this method!")
with _change_directory(Path(cwd)):
if self.is_initialized:
if threads is not None:
msg = "Threads must be set via Model.is_initialized(...)"
raise ValueError(msg)
if mpi_args is not None:
msg = "The MPI environment must be set otherwise such as" \
"with the call to mpi4py"
raise ValueError(msg)
# Compute the volumes
openmc.lib.calculate_volumes(output)
else:
self.export_to_xml()
openmc.calculate_volumes(threads=threads, output=output,
openmc_exec=openmc_exec,
mpi_args=mpi_args)
# Now we apply the volumes
if apply_volumes:
# Load the results and add them to the model
for i, vol_calc in enumerate(self.settings.volume_calculations):
vol_calc.load_results(f"volume_{i + 1}.h5")
# First add them to the Python side
self.geometry.add_volume_information(vol_calc)
# And now repeat for the C API
if self.is_initialized and vol_calc.domain_type == 'material':
# Then we can do this in the C API
for domain_id in vol_calc.ids:
openmc.lib.materials[domain_id].volume = \
vol_calc.volumes[domain_id].n
def plot_geometry(self, output=True, cwd='.', openmc_exec='openmc'):
"""Creates plot images as specified by the Model.plots attribute
.. versionadded:: 0.13.0
Parameters
----------
output : bool, optional
Capture OpenMC output from standard out
cwd : str, optional
Path to working directory to run in. Defaults to the current
working directory.
openmc_exec : str, optional
Path to OpenMC executable. Defaults to 'openmc'.
This only applies to the case when not using the C API.
"""
if len(self.plots) == 0:
# Then there is no volume calculation specified
raise ValueError("The Model.plots attribute must be specified "
"before executing this method!")
with _change_directory(Path(cwd)):
if self.is_initialized:
# Compute the volumes
openmc.lib.plot_geometry(output)
else:
self.export_to_xml()
openmc.plot_geometry(output=output, openmc_exec=openmc_exec)
def _change_py_lib_attribs(self, names_or_ids, value, obj_type,
attrib_name, density_units='atom/b-cm'):
# Method to do the same work whether it is a cell or material and
# a temperature or volume
check_type('names_or_ids', names_or_ids, Iterable, (Integral, str))
check_type('obj_type', obj_type, str)
obj_type = obj_type.lower()
check_value('obj_type', obj_type, ('material', 'cell'))
check_value('attrib_name', attrib_name,
('temperature', 'volume', 'density', 'rotation',
'translation'))
# The C API only allows setting density units of atom/b-cm and g/cm3
check_value('density_units', density_units, ('atom/b-cm', 'g/cm3'))
# The C API has no way to set cell volume or material temperature
# so lets raise exceptions as needed
if obj_type == 'cell' and attrib_name == 'volume':
raise NotImplementedError(
'Setting a Cell volume is not supported!')
if obj_type == 'material' and attrib_name == 'temperature':
raise NotImplementedError(
'Setting a material temperature is not supported!')
# And some items just dont make sense
if obj_type == 'cell' and attrib_name == 'density':
raise ValueError('Cannot set a Cell density!')
if obj_type == 'material' and attrib_name in ('rotation',
'translation'):
raise ValueError('Cannot set a material rotation/translation!')
# Set the
if obj_type == 'cell':
by_name = self._cells_by_name
by_id = self._cells_by_id
obj_by_id = openmc.lib.cells
else:
by_name = self._materials_by_name
by_id = self._materials_by_id
obj_by_id = openmc.lib.materials
# Get the list of ids to use if converting from names and accepting
# only values that have actual ids
ids = []
for name_or_id in names_or_ids:
if isinstance(name_or_id, Integral):
if name_or_id in by_id:
ids.append(int(name_or_id))
else:
cap_obj = obj_type.capitalize()
msg = f'{cap_obj} ID {name_or_id} " \
"is not present in the model!'
raise InvalidIDError(msg)
elif isinstance(name_or_id, str):
if name_or_id in by_name:
# Then by_name[name_or_id] is a list so we need to add all
# entries
ids.extend([obj.id for obj in by_name[name_or_id]])
else:
cap_obj = obj_type.capitalize()
msg = f'{cap_obj} {name_or_id} " \
"is not present in the model!'
raise InvalidIDError(msg)
# Now perform the change to both python and C API
for id_ in ids:
obj = by_id[id_]
if attrib_name == 'density':
obj.set_density(density_units, value)
else:
setattr(obj, attrib_name, value)
# Next lets keep what is in C API memory up to date as well
if self.is_initialized:
lib_obj = obj_by_id[id_]
if attrib_name == 'density':
lib_obj.set_density(value, density_units)
elif attrib_name == 'temperature':
lib_obj.set_temperature(value)
else:
setattr(lib_obj, attrib_name, value)
def rotate_cells(self, names_or_ids, vector):
"""Rotate the identified cell(s) by the specified rotation vector.
The rotation is only applied to cells filled with a universe.
.. note:: If applying this change to a name that is not unique, then
the change will be applied to all objects of that name.
.. versionadded:: 0.13.0
Parameters
----------
names_or_ids : Iterable of str or int
The cell names (if str) or id (if int) that are to be translated
or rotated. This parameter can include a mix of names and ids.
vector : Iterable of float
The rotation vector of length 3 to apply. This array specifies the
angles in degrees about the x, y, and z axes, respectively.
"""
self._change_py_lib_attribs(names_or_ids, vector, 'cell', 'rotation')
def translate_cells(self, names_or_ids, vector):
"""Translate the identified cell(s) by the specified translation vector.
The translation is only applied to cells filled with a universe.
.. note:: If applying this change to a name that is not unique, then
the change will be applied to all objects of that name.
.. versionadded:: 0.13.0
Parameters
----------
names_or_ids : Iterable of str or int
The cell names (if str) or id (if int) that are to be translated
or rotated. This parameter can include a mix of names and ids.
vector : Iterable of float
The translation vector of length 3 to apply. This array specifies
the x, y, and z dimensions of the translation.
"""
self._change_py_lib_attribs(names_or_ids, vector, 'cell',
'translation')
def update_densities(self, names_or_ids, density, density_units='atom/b-cm'):
"""Update the density of a given set of materials to a new value
.. note:: If applying this change to a name that is not unique, then
the change will be applied to all objects of that name.
.. versionadded:: 0.13.0
Parameters
----------
names_or_ids : Iterable of str or int
The material names (if str) or id (if int) that are to be updated.
This parameter can include a mix of names and ids.
density : float
The density to apply in the units specified by `density_units`
density_units : {'atom/b-cm', 'g/cm3'}, optional
Units for `density`. Defaults to 'atom/b-cm'
"""
self._change_py_lib_attribs(names_or_ids, density, 'material',
'density', density_units)
def update_cell_temperatures(self, names_or_ids, temperature):
"""Update the temperature of a set of cells to the given value
.. note:: If applying this change to a name that is not unique, then
the change will be applied to all objects of that name.
.. versionadded:: 0.13.0
Parameters
----------
names_or_ids : Iterable of str or int
The cell names (if str) or id (if int) that are to be updated.
This parameter can include a mix of names and ids.
temperature : float
The temperature to apply in units of Kelvin
"""
self._change_py_lib_attribs(names_or_ids, temperature, 'cell',
'temperature')
def update_material_volumes(self, names_or_ids, volume):
"""Update the volume of a set of materials to the given value
.. note:: If applying this change to a name that is not unique, then
the change will be applied to all objects of that name.
.. versionadded:: 0.13.0
Parameters
----------
names_or_ids : Iterable of str or int
The material names (if str) or id (if int) that are to be updated.
This parameter can include a mix of names and ids.
volume : float
The volume to apply in units of cm^3
"""
self._change_py_lib_attribs(names_or_ids, volume, 'material', 'volume')

8
openmc/mpi.py Normal file
View file

@ -0,0 +1,8 @@
try:
from mpi4py import MPI
comm = MPI.COMM_WORLD
except ImportError:
from unittest.mock import Mock
MPI = Mock()
from openmc.dummy_comm import DummyCommunicator
comm = DummyCommunicator()

View file

@ -1,6 +1,7 @@
from collections.abc import Iterable, Mapping
from numbers import Real, Integral
from pathlib import Path
import shutil
import subprocess
from xml.etree import ElementTree as ET
@ -165,6 +166,18 @@ _SVG_COLORS = {
}
def _get_plot_image(plot):
from IPython.display import Image
# Make sure .png file was created
stem = plot.filename if plot.filename is not None else f'plot_{plot.id}'
png_file = f'{stem}.png'
if not Path(png_file).exists():
raise FileNotFoundError(f"Could not find .png image for plot {plot.id}")
return Image(png_file)
class Plot(IDManagerMixin):
"""Definition of a finite region of space to be plotted.
@ -671,15 +684,14 @@ class Plot(IDManagerMixin):
return element
def to_ipython_image(self, openmc_exec='openmc', cwd='.',
convert_exec='convert'):
def to_ipython_image(self, openmc_exec='openmc', cwd='.'):
"""Render plot as an image
This method runs OpenMC in plotting mode to produce a bitmap image which
is then converted to a .png file and loaded in as an
:class:`IPython.display.Image` object. As such, it requires that your
model geometry, materials, and settings have already been exported to
XML.
This method runs OpenMC in plotting mode to produce a .png file.
.. versionchanged:: 0.13.0
The *convert_exec* argument was removed since OpenMC now produces
.png images directly.
Parameters
----------
@ -687,8 +699,6 @@ class Plot(IDManagerMixin):
Path to OpenMC executable
cwd : str, optional
Path to working directory to run in
convert_exec : str, optional
Command that can convert PPM files into PNG files
Returns
-------
@ -696,23 +706,14 @@ class Plot(IDManagerMixin):
Image generated
"""
from IPython.display import Image
# Create plots.xml
Plots([self]).export_to_xml()
# Run OpenMC in geometry plotting mode
openmc.plot_geometry(False, openmc_exec, cwd)
# Convert to .png
if self.filename is not None:
ppm_file = f'{self.filename}.ppm'
else:
ppm_file = f'plot_{self.id}.ppm'
png_file = ppm_file.replace('.ppm', '.png')
subprocess.check_call([convert_exec, ppm_file, png_file])
return Image(png_file)
# Return produced image
return _get_plot_image(self)
class Plots(cv.CheckedList):

View file

@ -812,8 +812,48 @@ class Mixture(Univariate):
self._distribution = distribution
def to_xml_element(self, element_name):
raise NotImplementedError
"""Return XML representation of the mixture distribution
Parameters
----------
element_name : str
XML element name
Returns
-------
element : xml.etree.ElementTree.Element
XML element containing mixture distribution data
"""
element = ET.Element(element_name)
element.set("type", "mixture")
for p, d in zip(self.probability, self.distribution):
data = ET.SubElement(element, "pair")
data.set("probability", str(p))
data.append(d.to_xml_element("dist"))
return element
@classmethod
def from_xml_element(cls, elem):
raise NotImplementedError
"""Generate mixture distribution from an XML element
Parameters
----------
elem : xml.etree.ElementTree.Element
XML element
Returns
-------
openmc.stats.Mixture
Mixture distribution generated from XML element
"""
probability = []
distribution = []
for pair in elem.findall('pair'):
probability.append(float(get_text(pair, 'probability')))
distribution.append(Univariate.from_xml_element(pair.find("dist")))
return cls(probability, distribution)

View file

@ -10,7 +10,7 @@
#include <string>
#include <fmt/core.h>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/capi.h"
#include "openmc/constants.h"

View file

@ -713,7 +713,13 @@ int32_t next_cell(
namespace openmc {
void read_dagmc_universes(pugi::xml_node node) {};
void read_dagmc_universes(pugi::xml_node node)
{
if (check_for_node(node, "dagmc_universe")) {
fatal_error("DAGMC Universes are present but OpenMC was not configured"
"with DAGMC");
}
};
void check_dagmc_root_univ() {};
} // namespace openmc

View file

@ -7,6 +7,8 @@
#include <stdexcept> // for runtime_error
#include <string> // for string, stod
#include <gsl/gsl-lite.hpp>
#include "openmc/error.h"
#include "openmc/math_functions.h"
#include "openmc/random_dist.h"
@ -287,6 +289,48 @@ double Equiprobable::sample(uint64_t* seed) const
return xl + ((n - 1) * r - i) * (xr - xl);
}
//==============================================================================
// Mixture implementation
//==============================================================================
Mixture::Mixture(pugi::xml_node node)
{
double cumsum = 0.0;
for (pugi::xml_node pair : node.children("pair")) {
// Check that required data exists
if (!pair.attribute("probability")) fatal_error("Mixture pair element does not have probability.");
if (!pair.child("dist")) fatal_error("Mixture pair element does not have a distribution.");
// cummulative sum of probybilities
cumsum += std::stod(pair.attribute("probability").value());
// Save cummulative probybility and distrubution
distribution_.push_back(
std::make_pair(cumsum, distribution_from_xml(pair.child("dist"))));
}
// Normalize cummulative probabilities to 1
for (auto& pair : distribution_) {
pair.first /= cumsum;
}
}
double Mixture::sample(uint64_t* seed) const
{
// Sample value of CDF
const double p = prn(seed);
// find matching distribution
const auto it = std::lower_bound(distribution_.cbegin(), distribution_.cend(),
p, [](const DistPair& pair, double p) { return pair.first < p; });
// This should not happen. Catch it
Ensures(it != distribution_.cend());
// Sample the chosen distribution
return it->second->sample(seed);
}
//==============================================================================
// Helper function
//==============================================================================
@ -315,6 +359,8 @@ UPtrDist distribution_from_xml(pugi::xml_node node)
dist = UPtrDist {new Discrete(node)};
} else if (type == "tabular") {
dist = UPtrDist {new Tabular(node)};
} else if (type == "mixture") {
dist = UPtrDist {new Mixture(node)};
} else {
openmc::fatal_error("Invalid distribution type: " + type);
}

View file

@ -15,6 +15,7 @@
#include "openmc/message_passing.h"
#include "openmc/nuclide.h"
#include "openmc/photon.h"
#include "openmc/plot.h"
#include "openmc/random_lcg.h"
#include "openmc/settings.h"
#include "openmc/simulation.h"
@ -45,6 +46,7 @@ void free_memory()
free_memory_mesh();
free_memory_tally();
free_memory_bank();
free_memory_plot();
if (mpi::master) {
free_memory_cmfd();
}
@ -128,6 +130,7 @@ int openmc_finalize()
data::temperature_min = 0.0;
data::temperature_max = INFTY;
model::root_universe = -1;
model::plotter_seed = 1;
openmc::openmc_set_seed(DEFAULT_SEED);
// Deallocate arrays

View file

@ -305,9 +305,11 @@ void read_input_xml()
// Initialize distribcell_filters
prepare_distribcell();
// Read the plots.xml regardless of plot mode in case plots are requested
// via the API
read_plots_xml();
if (settings::run_mode == RunMode::PLOTTING) {
// Read plots.xml if it exists
read_plots_xml();
if (mpi::master && settings::verbosity >= 5)
print_plot();

View file

@ -2,7 +2,7 @@
#include <algorithm> // for copy, equal, min, min_element
#include <cmath> // for ceil
#include <cstddef> // for size_t
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include <string>
#ifdef OPENMC_MPI
@ -35,6 +35,7 @@
#ifdef LIBMESH
#include "libmesh/mesh_tools.h"
#include "libmesh/numeric_vector.h"
#include "libmesh/mesh_modification.h"
#endif
namespace openmc {
@ -171,6 +172,12 @@ UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node)
}
}
// check if a length unit multiplier was specified
if (check_for_node(node, "length_multiplier")) {
length_multiplier_ = std::stod(get_node_value(node, "length_multiplier"));
specified_length_multiplier_ = true;
}
// get the filename of the unstructured mesh to load
if (check_for_node(node, "filename")) {
filename_ = get_node_value(node, "filename");
@ -218,9 +225,21 @@ void UnstructuredMesh::to_hdf5(hid_t group) const
write_dataset(mesh_group, "volumes", tet_vols);
write_dataset(mesh_group, "centroids", centroids);
if (specified_length_multiplier_)
write_dataset(mesh_group, "length_multiplier", length_multiplier_);
close_group(mesh_group);
}
void UnstructuredMesh::set_length_multiplier(double length_multiplier)
{
length_multiplier_ = length_multiplier;
if (length_multiplier_ != 1.0)
specified_length_multiplier_ = true;
}
void StructuredMesh::get_indices(Position r, int* ijk, bool* in_mesh) const
{
*in_mesh = true;
@ -1585,9 +1604,10 @@ MOABMesh::MOABMesh(pugi::xml_node node) : UnstructuredMesh(node)
initialize();
}
MOABMesh::MOABMesh(const std::string& filename)
MOABMesh::MOABMesh(const std::string& filename, double length_multiplier)
{
filename_ = filename;
set_length_multiplier(length_multiplier);
initialize();
}
@ -1634,6 +1654,34 @@ void MOABMesh::initialize()
fatal_error("Failed to add tetrahedra to an entity set.");
}
if (specified_length_multiplier_) {
// get the connectivity of all tets
moab::Range adj;
rval = mbi_->get_adjacencies(ehs_, 0, true, adj, moab::Interface::UNION);
if (rval != moab::MB_SUCCESS) {
fatal_error("Failed to get adjacent vertices of tetrahedra.");
}
// scale all vertex coords by multiplier (done individually so not all
// coordinates are in memory twice at once)
for (auto vert : adj) {
// retrieve coords
std::array<double, 3> coord;
rval = mbi_->get_coords(&vert, 1, coord.data());
if (rval != moab::MB_SUCCESS) {
fatal_error("Could not get coordinates of vertex.");
}
// scale coords
for (auto& c : coord) {
c *= length_multiplier_;
}
// set new coords
rval = mbi_->set_coords(&vert, 1, coord.data());
if (rval != moab::MB_SUCCESS) {
fatal_error("Failed to set new vertex coordinates");
}
}
}
// build acceleration data structures
compute_barycentric_data(ehs_);
build_kdtree(ehs_);
@ -2144,9 +2192,10 @@ LibMesh::LibMesh(pugi::xml_node node) : UnstructuredMesh(node)
initialize();
}
LibMesh::LibMesh(const std::string& filename)
LibMesh::LibMesh(const std::string& filename, double length_multiplier)
{
filename_ = filename;
set_length_multiplier(length_multiplier);
initialize();
}
@ -2162,6 +2211,11 @@ void LibMesh::initialize()
m_ = make_unique<libMesh::Mesh>(*settings::libmesh_comm, n_dimension_);
m_->read(filename_);
if (specified_length_multiplier_) {
libMesh::MeshTools::Modification::scale(*m_, length_multiplier_);
}
m_->prepare_for_use();
// ensure that the loaded mesh is 3 dimensional

View file

@ -72,26 +72,26 @@ void title()
// Write version information
fmt::print(
" | The OpenMC Monte Carlo Code\n"
" Copyright | 2011-2021 MIT and OpenMC contributors\n"
" License | https://docs.openmc.org/en/latest/license.html\n"
" Version | {}.{}.{}{}\n",
" | The OpenMC Monte Carlo Code\n"
" Copyright | 2011-2021 MIT, UChicago Argonne LLC, and contributors\n"
" License | https://docs.openmc.org/en/latest/license.html\n"
" Version | {}.{}.{}{}\n",
VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_DEV ? "-dev" : "");
#ifdef GIT_SHA1
fmt::print(" Git SHA1 | {}\n", GIT_SHA1);
fmt::print(" Git SHA1 | {}\n", GIT_SHA1);
#endif
// Write the date and time
fmt::print(" Date/Time | {}\n", time_stamp());
fmt::print(" Date/Time | {}\n", time_stamp());
#ifdef OPENMC_MPI
// Write number of processors
fmt::print(" MPI Processes | {}\n", mpi::n_procs);
fmt::print(" MPI Processes | {}\n", mpi::n_procs);
#endif
#ifdef _OPENMP
// Write number of OpenMP threads
fmt::print(" OpenMP Threads | {}\n", omp_get_max_threads());
fmt::print(" OpenMP Threads | {}\n", omp_get_max_threads());
#endif
std::cout << std::endl;
}
@ -335,8 +335,8 @@ void print_version()
#ifdef GIT_SHA1
fmt::print("Git SHA1: {}\n", GIT_SHA1);
#endif
fmt::print("Copyright (c) 2011-2021 Massachusetts Institute of "
"Technology and OpenMC contributors\nMIT/X license at "
fmt::print("Copyright (c) 2011-2021 MIT, UChicago Argonne LLC, and "
"contributors\nMIT/X license at "
"<https://docs.openmc.org/en/latest/license.html>\n");
}
}

View file

@ -1,12 +1,16 @@
#include "openmc/plot.h"
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <sstream>
#include "xtensor/xview.hpp"
#include <fmt/core.h>
#include <fmt/ostream.h>
#ifdef USE_LIBPNG
#include <png.h>
#endif
#include "openmc/constants.h"
#include "openmc/error.h"
@ -103,25 +107,29 @@ uint64_t plotter_seed = 1;
extern "C" int openmc_plot_geometry()
{
for (auto& pl : model::plots) {
write_message(5, "Processing plot {}: {}...", pl.id_, pl.path_plot_);
if (PlotType::slice == pl.type_) {
// create 2D image
create_ppm(pl);
create_image(pl);
} else if (PlotType::voxel == pl.type_) {
// create voxel file for 3D viewing
create_voxel(pl);
}
}
return 0;
}
void read_plots_xml()
{
// Check if plots.xml exists
// Check if plots.xml exists; this is only necessary when the plot runmode is
// initiated. Otherwise, we want to read plots.xml because it may be called
// later via the API. In that case, its ok for a plots.xml to not exist
std::string filename = settings::path_input + "plots.xml";
if (!file_exists(filename)) {
if (!file_exists(filename) && settings::run_mode == RunMode::PLOTTING) {
fatal_error(fmt::format("Plots XML file '{}' does not exist!", filename));
}
@ -138,12 +146,18 @@ void read_plots_xml()
}
}
void free_memory_plot()
{
model::plots.clear();
model::plot_map.clear();
}
//==============================================================================
// CREATE_PPM creates an image based on user input from a plots.xml <plot>
// specification in the portable pixmap format (PPM)
// CREATE_IMAGE creates an image based on user input from a plots.xml <plot>
// specification in the PNG/PPM format
//==============================================================================
void create_ppm(Plot const& pl)
void create_image(Plot const& pl)
{
size_t width = pl.pixels_[0];
@ -184,8 +198,12 @@ void create_ppm(Plot const& pl)
draw_mesh_lines(pl, data);
}
// write ppm data to file
// create image file
#ifdef USE_LIBPNG
output_png(pl, data);
#else
output_ppm(pl, data);
#endif
}
void Plot::set_id(pugi::xml_node plot_node)
@ -238,7 +256,11 @@ void Plot::set_output_path(pugi::xml_node plot_node)
// add appropriate file extension to name
switch (type_) {
case PlotType::slice:
#ifdef USE_LIBPNG
filename.append(".png");
#else
filename.append(".ppm");
#endif
break;
case PlotType::voxel:
filename.append(".h5");
@ -673,6 +695,60 @@ void output_ppm(Plot const& pl, const ImageData& data)
of << "\n";
}
//==============================================================================
// OUTPUT_PNG writes out a previously generated image to a PNG file
//==============================================================================
#ifdef USE_LIBPNG
void output_png(Plot const& pl, const ImageData& data)
{
// Open PNG file for writing
std::string fname = pl.path_plot_;
fname = strtrim(fname);
auto fp = std::fopen(fname.c_str(), "wb");
// Initialize write and info structures
auto png_ptr =
png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
auto info_ptr = png_create_info_struct(png_ptr);
// Setup exception handling
if (setjmp(png_jmpbuf(png_ptr)))
fatal_error("Error during png creation");
png_init_io(png_ptr, fp);
// Write header (8 bit colour depth)
int width = pl.pixels_[0];
int height = pl.pixels_[1];
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
png_write_info(png_ptr, info_ptr);
// Allocate memory for one row (3 bytes per pixel - RGB)
std::vector<png_byte> row(3 * width);
// Write color for each pixel
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
RGBColor rgb = data(x, y);
row[3 * x] = rgb.red;
row[3 * x + 1] = rgb.green;
row[3 * x + 2] = rgb.blue;
}
png_write_row(png_ptr, row.data());
}
// End write
png_write_end(png_ptr, nullptr);
// Clean up data structures
std::fclose(fp);
png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
png_destroy_write_struct(&png_ptr, nullptr);
}
#endif
//==============================================================================
// DRAW_MESH_LINES draws mesh line boundaries on an image
//==============================================================================
@ -777,7 +853,7 @@ void draw_mesh_lines(Plot const& pl, ImageData& data)
//==============================================================================
// CREATE_VOXEL outputs a binary file that can be input into silomesh for 3D
// geometry visualization. It works the same way as create_ppm by dragging a
// geometry visualization. It works the same way as create_image by dragging a
// particle across the geometry for the specified number of voxels. The first 3
// int's in the binary are the number of x, y, and z voxels. The next 3
// double's are the widths of the voxels in the x, y, and z directions. The

View file

@ -5,7 +5,7 @@
#include <utility>
#include <fmt/core.h>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/array.h"
#include "openmc/container_util.h"

View file

@ -1,7 +1,7 @@
#include "openmc/tallies/filter_mesh.h"
#include <fmt/core.h>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/capi.h"
#include "openmc/constants.h"

View file

@ -3,7 +3,7 @@
#include <utility> // For pair
#include <fmt/core.h>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/capi.h"
#include "openmc/error.h"

View file

@ -5,7 +5,7 @@
#include <utility> // For pair
#include <fmt/core.h>
#include <gsl/gsl>
#include <gsl/gsl-lite.hpp>
#include "openmc/capi.h"
#include "openmc/error.h"

View file

@ -1 +1 @@
a8192f6029cf99748816fab2618fa48e180656eba313940ccdfff3e56890a5dadd13bf92cd7e3be6a4b535bca5e2a58a905fcfba018fb922273f8be6dfc19859
f85c20735a0c08525fe48b19a8e075c074539ee6c8860268fa0cb515d842496b7086e5b94305ef78dcf2106bb193abdf438259ac8ff1d0245a2782eb6f5af873

View file

@ -19,7 +19,7 @@ class PlotTestHarness(TestHarness):
openmc.plot_geometry(openmc_exec=config['exe'])
def _test_output_created(self):
"""Make sure *.ppm has been created."""
"""Make sure *.png has been created."""
for fname in self._plot_names:
assert os.path.exists(fname), 'Plot output file does not exist.'
@ -34,8 +34,8 @@ class PlotTestHarness(TestHarness):
outstr = bytes()
for fname in self._plot_names:
if fname.endswith('.ppm'):
# Add PPM output to results
if fname.endswith('.png'):
# Add PNG output to results
with open(fname, 'rb') as fh:
outstr += fh.read()
elif fname.endswith('.h5'):
@ -56,6 +56,6 @@ class PlotTestHarness(TestHarness):
def test_plot():
harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm',
harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png',
'plot_4.h5'))
harness.main()

View file

@ -1 +1 @@
566103831cb8273b0578565c39d30e479664e2b1783d877b45b42cf4f3af0b01671b6db423114b09a74bbe1ddf51a7db565ff2118d6d1ee987b52318773b719a
926065ceb2a9b8292fe6270317c38c4373473cfea19d2a8392a32e5ece8e314c04b9f032921d987bd195ae4b6f674d359b0e38302e6ae4c93b4ac9573a384ac6

View file

@ -19,7 +19,7 @@ class PlotTestHarness(TestHarness):
openmc.plot_geometry(openmc_exec=config['exe'])
def _test_output_created(self):
"""Make sure *.ppm has been created."""
"""Make sure *.png has been created."""
for fname in self._plot_names:
assert os.path.exists(fname), 'Plot output file does not exist.'
@ -34,8 +34,8 @@ class PlotTestHarness(TestHarness):
outstr = bytes()
for fname in self._plot_names:
if fname.endswith('.ppm'):
# Add PPM output to results
if fname.endswith('.png'):
# Add PNG output to results
with open(fname, 'rb') as fh:
outstr += fh.read()
elif fname.endswith('.h5'):
@ -56,6 +56,6 @@ class PlotTestHarness(TestHarness):
def test_plot_overlap():
harness = PlotTestHarness(('plot_1.ppm', 'plot_2.ppm', 'plot_3.ppm',
harness = PlotTestHarness(('plot_1.png', 'plot_2.png', 'plot_3.png',
'plot_4.h5'))
harness.main()

View file

@ -25,7 +25,7 @@ class PlotVoxelTestHarness(TestHarness):
glob.glob('plot_4.h5'))
def _test_output_created(self):
"""Make sure *.ppm has been created."""
"""Make sure plots have been created."""
for fname in self._plot_names:
assert os.path.exists(fname), 'Plot output file does not exist.'

View file

@ -76,4 +76,27 @@
<parameters>1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23</parameters>
</energy>
</source>
<source strength="0.1">
<space origin="1.0 1.0 0.0" type="cylindrical">
<r parameters="2.0 3.0" type="uniform" />
<phi parameters="0.0 6.283185307179586" type="uniform" />
<z interpolation="linear-linear" type="tabular">
<parameters>-2.0 0.0 2.0 0.2 0.3 0.2</parameters>
</z>
</space>
<angle type="isotropic" />
<energy type="mixture">
<pair probability="1">
<dist parameters="1289500.0" type="maxwell" />
</pair>
<pair probability="2">
<dist parameters="988000.0 2.249e-06" type="watt" />
</pair>
<pair probability="3">
<dist interpolation="histogram" type="tabular">
<parameters>1.0 1.3894954943731377 1.93069772888325 2.6826957952797255 3.72759372031494 5.17947467923121 7.196856730011519 10.0 13.894954943731374 19.306977288832496 26.826957952797247 37.2759372031494 51.7947467923121 71.96856730011518 100.0 138.94954943731375 193.06977288832496 268.26957952797244 372.7593720314938 517.9474679231207 719.6856730011514 1000.0 1389.4954943731375 1930.6977288832495 2682.6957952797247 3727.593720314938 5179.474679231207 7196.856730011514 10000.0 13894.95494373136 19306.977288832495 26826.95795279722 37275.93720314938 51794.74679231213 71968.56730011514 100000.0 138949.5494373136 193069.77288832495 268269.5795279722 372759.3720314938 517947.4679231202 719685.6730011514 1000000.0 1389495.494373136 1930697.7288832497 2682695.7952797217 3727593.720314938 5179474.679231202 7196856.730011513 10000000.0 0.0 2.9086439299358713e-08 5.80533561806147e-08 8.67817193689187e-08 1.1515347785771536e-07 1.4305204600565115e-07 1.7036278261198208e-07 1.9697346200185813e-07 2.227747351856934e-07 2.4766057919761985e-07 2.715287327665956e-07 2.9428111652990295e-07 3.1582423606228735e-07 3.360695660646056e-07 3.549339141332686e-07 3.723397626156721e-07 3.882155871468592e-07 4.024961505584776e-07 4.151227709522976e-07 4.260435628367196e-07 4.3521365033538783e-07 4.4259535159179273e-07 4.4815833361210174e-07 4.5187973690993757e-07 4.5374426944091084e-07 4.5374426944091084e-07 4.5187973690993757e-07 4.4815833361210174e-07 4.4259535159179273e-07 4.352136503353879e-07 4.2604356283671966e-07 4.1512277095229767e-07 4.0249615055847764e-07 3.8821558714685926e-07 3.723397626156722e-07 3.5493391413326864e-07 3.360695660646057e-07 3.158242360622874e-07 2.942811165299031e-07 2.715287327665957e-07 2.4766057919762e-07 2.2277473518569352e-07 1.9697346200185819e-07 1.7036278261198226e-07 1.4305204600565126e-07 1.1515347785771556e-07 8.678171936891881e-08 5.805335618061493e-08 2.9086439299358858e-08 5.559621115282002e-23</parameters>
</dist>
</pair>
</energy>
</source>
</settings>

View file

@ -1,2 +1,2 @@
k-combined:
3.033600E-01 1.676108E-03
2.980096E-01 9.632798E-04

View file

@ -55,18 +55,20 @@ class SourceTestHarness(PyAPITestHarness):
energy1 = openmc.stats.Maxwell(1.2895e6)
energy2 = openmc.stats.Watt(0.988e6, 2.249e-6)
energy3 = openmc.stats.Tabular(E, p, interpolation='histogram')
energy4 = openmc.stats.Mixture([1, 2, 3], [energy1, energy2, energy3])
source1 = openmc.Source(spatial1, angle1, energy1, strength=0.5)
source2 = openmc.Source(spatial2, angle2, energy2, strength=0.3)
source3 = openmc.Source(spatial3, angle3, energy3, strength=0.1)
source4 = openmc.Source(spatial4, angle3, energy3, strength=0.1)
source5 = openmc.Source(spatial5, angle3, energy3, strength=0.1)
source6 = openmc.Source(spatial5, angle3, energy4, strength=0.1)
settings = openmc.Settings()
settings.batches = 10
settings.inactive = 5
settings.particles = 1000
settings.source = [source1, source2, source3, source4, source5]
settings.source = [source1, source2, source3, source4, source5, source6]
settings.export_to_xml()

View file

@ -31,6 +31,18 @@ def u235_yields():
return openmc.data.FissionProductYields.from_endf(filename)
def test_get_decay_modes():
assert openmc.data.get_decay_modes(1.0) == ['beta-']
assert openmc.data.get_decay_modes(6.0) == ['sf']
assert openmc.data.get_decay_modes(10.0) == ['unknown']
assert openmc.data.get_decay_modes(1.5) == ['beta-', 'n']
assert openmc.data.get_decay_modes(1.4) == ['beta-', 'alpha']
assert openmc.data.get_decay_modes(1.55) == ['beta-', 'n', 'n']
assert openmc.data.get_decay_modes(1.555) == ['beta-', 'n', 'n', 'n']
assert openmc.data.get_decay_modes(2.4) == ['ec/beta+', 'alpha']
def test_nb90_halflife(nb90):
ufloat_close(nb90.half_life, ufloat(52560.0, 180.0))
ufloat_close(nb90.decay_constant, log(2.)/nb90.half_life)

View file

@ -7,7 +7,8 @@ import os
from pathlib import Path
import numpy as np
from openmc.deplete import comm, Chain, reaction_rates, nuclide, cram, pool
from openmc.mpi import comm
from openmc.deplete import Chain, reaction_rates, nuclide, cram, pool
import pytest
from tests import cdtemp

View file

@ -14,11 +14,11 @@ import numpy as np
from uncertainties import ufloat
import pytest
from openmc.mpi import comm
from openmc.deplete import (
ReactionRates, Results, ResultsList, comm, OperatorResult,
PredictorIntegrator, CECMIntegrator, CF4Integrator, CELIIntegrator,
EPCRK4Integrator, LEQIIntegrator, SICELIIntegrator, SILEQIIntegrator,
cram)
ReactionRates, Results, ResultsList, OperatorResult, PredictorIntegrator,
CECMIntegrator, CF4Integrator, CELIIntegrator, EPCRK4Integrator,
LEQIIntegrator, SICELIIntegrator, SILEQIIntegrator, cram)
from tests import dummy_operator

View file

@ -263,6 +263,7 @@ def test_settings(lib_init):
assert settings.generations_per_batch == 1
assert settings.particles == 100
assert settings.seed == 1
assert settings.event_based is False
settings.seed = 11
@ -713,7 +714,6 @@ def test_trigger_set_n_batches(uo2_trigger_model, mpi_intracomm):
def test_cell_translation(pincell_model_w_univ, mpi_intracomm):
openmc.lib.finalize()
openmc.lib.init(intracomm=mpi_intracomm)
openmc.lib.simulation_init()
# Cell 1 is filled with a material so it has a translation, but we can't
# set it.
cell = openmc.lib.cells[1]
@ -727,9 +727,12 @@ def test_cell_translation(pincell_model_w_univ, mpi_intracomm):
# This time we *can* set it
cell.translation = (1., 0., -1.)
assert cell.translation == pytest.approx([1., 0., -1.])
openmc.lib.finalize()
def test_cell_rotation(pincell_model_w_univ):
def test_cell_rotation(pincell_model_w_univ, mpi_intracomm):
openmc.lib.finalize()
openmc.lib.init(intracomm=mpi_intracomm)
# Cell 1 is filled with a material so we cannot rotate it, but we can get
# its rotation matrix (which will be the identity matrix)
cell = openmc.lib.cells[1]
@ -742,3 +745,4 @@ def test_cell_rotation(pincell_model_w_univ):
assert cell.rotation == pytest.approx([0., 0., 0.])
cell.rotation = (180., 0., 0.)
assert cell.rotation == pytest.approx([180., 0., 0.])
openmc.lib.finalize()

View file

@ -1,29 +1,281 @@
from math import pi
from pathlib import Path
from shutil import which
import numpy as np
import pytest
import openmc
import openmc.lib
@pytest.fixture(scope='function')
def pin_model_attributes():
uo2 = openmc.Material(material_id=1, name='UO2')
uo2.set_density('g/cm3', 10.29769)
uo2.add_element('U', 1., enrichment=2.4)
uo2.add_element('O', 2.)
uo2.depletable = True
zirc = openmc.Material(material_id=2, name='Zirc')
zirc.set_density('g/cm3', 6.55)
zirc.add_element('Zr', 1.)
zirc.depletable = False
borated_water = openmc.Material(material_id=3, name='Borated water')
borated_water.set_density('g/cm3', 0.740582)
borated_water.add_element('B', 4.0e-5)
borated_water.add_element('H', 5.0e-2)
borated_water.add_element('O', 2.4e-2)
borated_water.add_s_alpha_beta('c_H_in_H2O')
borated_water.depletable = False
mats = openmc.Materials([uo2, zirc, borated_water])
pitch = 1.25984
fuel_or = openmc.ZCylinder(r=0.39218, name='Fuel OR')
clad_or = openmc.ZCylinder(r=0.45720, name='Clad OR')
box = openmc.model.rectangular_prism(pitch, pitch,
boundary_type='reflective')
# Define cells
fuel_inf_cell = openmc.Cell(cell_id=1, name='inf fuel', fill=uo2)
fuel_inf_univ = openmc.Universe(universe_id=1, cells=[fuel_inf_cell])
fuel = openmc.Cell(cell_id=2, name='fuel',
fill=fuel_inf_univ, region=-fuel_or)
clad = openmc.Cell(cell_id=3, fill=zirc, region=+fuel_or & -clad_or)
water = openmc.Cell(cell_id=4, fill=borated_water, region=+clad_or & box)
# Define overall geometry
geom = openmc.Geometry([fuel, clad, water])
uo2.volume = pi * fuel_or.r**2
settings = openmc.Settings()
settings.batches = 100
settings.inactive = 10
settings.particles = 1000
# Create a uniform spatial source distribution over fissionable zones
bounds = [-0.62992, -0.62992, -1, 0.62992, 0.62992, 1]
uniform_dist = openmc.stats.Box(
bounds[:3], bounds[3:], only_fissionable=True)
settings.source = openmc.source.Source(space=uniform_dist)
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]
settings.entropy_mesh = entropy_mesh
tals = openmc.Tallies()
tal = openmc.Tally(tally_id=1, name='test')
tal.filters = [openmc.MaterialFilter(bins=[uo2])]
tal.scores = ['flux', 'fission']
tals.append(tal)
plot1 = openmc.Plot(plot_id=1)
plot1.origin = (0., 0., 0.)
plot1.width = (pitch, pitch)
plot1.pixels = (300, 300)
plot1.color_by = 'material'
plot1.filename = 'test'
plot2 = openmc.Plot(plot_id=2)
plot2.origin = (0., 0., 0.)
plot2.width = (pitch, pitch)
plot2.pixels = (300, 300)
plot2.color_by = 'cell'
plots = openmc.Plots((plot1, plot2))
chain = './test_chain.xml'
chain_file_xml = """<?xml version="1.0"?>
<depletion_chain>
<nuclide name="Xe136" decay_modes="0" reactions="0" />
<nuclide name="U235" decay_modes="0" reactions="1">
<reaction type="fission" Q="200000000."/>
<neutron_fission_yields>
<energies>2.53000e-02</energies>
<fission_yields energy="2.53000e-02">
<products>Xe136</products>
<data>1.0</data>
</fission_yields>
</neutron_fission_yields>
</nuclide>
</depletion_chain>
"""
operator_kwargs = {'chain_file': chain}
return (mats, geom, settings, tals, plots, operator_kwargs, chain_file_xml)
def test_init(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _ = \
pin_model_attributes
openmc.reset_auto_ids()
# Check blank initialization of a model
test_model = openmc.Model()
assert test_model.geometry.root_universe is None
assert len(test_model.materials) == 0
ref_settings = openmc.Settings()
assert sorted(test_model.settings.__dict__.keys()) == \
sorted(ref_settings.__dict__.keys())
for ref_k, ref_v in ref_settings.__dict__.items():
assert test_model.settings.__dict__[ref_k] == ref_v
assert len(test_model.tallies) == 0
assert len(test_model.plots) == 0
assert test_model._materials_by_id == {}
assert test_model._materials_by_name == {}
assert test_model._cells_by_id == {}
assert test_model._cells_by_name == {}
assert test_model.is_initialized is False
# Now check proper init of an actual model. Assume no interference between
# parameters and so we can apply them all at once instead of testing one
# parameter initialization at a time
test_model = openmc.Model(geom, mats, settings, tals, plots)
assert test_model.geometry is geom
assert test_model.materials is mats
assert test_model.settings is settings
assert test_model.tallies is tals
assert test_model.plots is plots
assert test_model._materials_by_id == {1: mats[0], 2: mats[1], 3: mats[2]}
assert test_model._materials_by_name == {
'UO2': {mats[0]}, 'Zirc': {mats[1]}, 'Borated water': {mats[2]}}
# The last cell is the one that contains the infinite fuel
assert test_model._cells_by_id == \
{2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3],
4: geom.root_universe.cells[4],
1: geom.root_universe.cells[2].fill.cells[1]}
# No cell name for 2 and 3, so we expect a blank name to be assigned to
# cell 3 due to overwriting
assert test_model._cells_by_name == {
'fuel': {geom.root_universe.cells[2]},
'': {geom.root_universe.cells[3], geom.root_universe.cells[4]},
'inf fuel': {geom.root_universe.cells[2].fill.cells[1]}}
assert test_model.is_initialized is False
# Finally test the parameter type checking by passing bad types and
# obtaining the right exception types
def_params = [geom, mats, settings, tals, plots]
for i in range(len(def_params)):
args = def_params.copy()
# Try an integer, as that is a bad type for all arguments
args[i] = i
with pytest.raises(TypeError):
test_model = openmc.Model(*args)
def test_from_xml(run_in_tmpdir, pin_model_attributes):
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
# This test will write the individual files to xml and then init that way
# and run the same sort of test as in test_init
mats.export_to_xml()
geom.export_to_xml()
settings.export_to_xml()
tals.export_to_xml()
plots.export_to_xml()
# This from_xml method cannot load chain and fission_q
test_model = openmc.Model.from_xml()
assert test_model.geometry.root_universe.cells.keys() == \
geom.root_universe.cells.keys()
assert [c.fill.name for c in
test_model.geometry.root_universe.cells.values()] == \
[c.fill.name for c in geom.root_universe.cells.values()]
assert [mat.name for mat in test_model.materials] == \
[mat.name for mat in mats]
# We will assume the attributes of settings that are custom objects are
# OK if the others are so we dotn need to implement explicit comparisons
no_test = ['_source', '_entropy_mesh']
assert sorted(k for k in test_model.settings.__dict__.keys()
if k not in no_test) == \
sorted(k for k in settings.__dict__.keys() if k not in no_test)
keys = sorted(k for k in settings.__dict__.keys() if k not in no_test)
for ref_k in keys:
assert test_model.settings.__dict__[ref_k] == settings.__dict__[ref_k]
assert len(test_model.tallies) == 0
assert len(test_model.plots) == 0
assert test_model._materials_by_id == \
{1: test_model.materials[0], 2: test_model.materials[1],
3: test_model.materials[2]}
assert test_model._materials_by_name == {
'UO2': {test_model.materials[0]}, 'Zirc': {test_model.materials[1]},
'Borated water': {test_model.materials[2]}}
assert test_model._cells_by_id == {
2: test_model.geometry.root_universe.cells[2],
3: test_model.geometry.root_universe.cells[3],
4: test_model.geometry.root_universe.cells[4],
1: test_model.geometry.root_universe.cells[2].fill.cells[1]}
# No cell name for 2 and 3, so we expect a blank name to be assigned to
# cell 3 due to overwriting
assert test_model._cells_by_name == {
'fuel': {test_model.geometry.root_universe.cells[2]},
'': {test_model.geometry.root_universe.cells[3],
test_model.geometry.root_universe.cells[4]},
'inf fuel': {test_model.geometry.root_universe.cells[2].fill.cells[1]}}
assert test_model.is_initialized is False
def test_init_finalize_lib(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
# We are going to init and then make sure data is loaded
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
test_model.init_lib(output=False, intracomm=mpi_intracomm)
# First check that the API is advertised as initialized
assert openmc.lib.is_initialized is True
assert test_model.is_initialized is True
# Now make sure it actually is initialized by making a call to the lib
c_mat = openmc.lib.find_material((0.6, 0., 0.))
# This should be Borated water
assert c_mat.name == 'Borated water'
assert c_mat.id == 3
# Ok, now lets test that we can clear the data and check that it is cleared
test_model.finalize_lib()
# First check that the API is advertised as initialized
assert openmc.lib.is_initialized is False
assert test_model.is_initialized is False
# Note we cant actually test that a sys call fails because we should get a
# seg fault
def test_import_properties(run_in_tmpdir, mpi_intracomm):
"""Test importing properties on the Model class """
# Create PWR pin cell model and write XML files
openmc.reset_auto_ids()
model = openmc.examples.pwr_pin_cell()
model.export_to_xml()
model.init_lib(output=False, intracomm=mpi_intracomm)
# Change fuel temperature and density and export properties
openmc.lib.init(intracomm=mpi_intracomm)
cell = openmc.lib.cells[1]
cell.set_temperature(600.0)
cell.fill.set_density(5.0, 'g/cm3')
openmc.lib.export_properties()
openmc.lib.export_properties(output=False)
# Import properties to existing model
model.import_properties("properties.h5")
# Check to see that values are assigned to the C and python representations
# First python
cell = model.geometry.get_all_cells()[1]
assert cell.temperature == [600.0]
assert cell.fill.get_mass_density() == pytest.approx(5.0)
# Now C
assert openmc.lib.cells[1].get_temperature() == 600.
assert openmc.lib.materials[1].get_density('g/cm3') == pytest.approx(5.0)
# Clear the C API
openmc.lib.finalize()
# Import properties to existing model and re-export to new directory
model.import_properties("properties.h5")
# Verify the attributes survived by exporting to XML and re-creating
model.export_to_xml("with_properties")
# Load model with properties and confirm temperature/density has been changed
# Load model with properties and confirm temperature/density changed
model_with_properties = openmc.Model.from_xml(
'with_properties/geometry.xml',
'with_properties/materials.xml',
@ -32,3 +284,251 @@ def test_import_properties(run_in_tmpdir, mpi_intracomm):
cell = model_with_properties.geometry.get_all_cells()[1]
assert cell.temperature == [600.0]
assert cell.fill.get_mass_density() == pytest.approx(5.0)
def test_run(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
# This case will run by getting the k-eff and tallies for command-line and
# C API execution modes and ensuring they give the same result.
sp_path = test_model.run(output=False)
with openmc.StatePoint(sp_path) as sp:
cli_keff = sp.k_combined
cli_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0]
cli_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0]
test_model.init_lib(output=False, intracomm=mpi_intracomm)
sp_path = test_model.run(output=False)
with openmc.StatePoint(sp_path) as sp:
lib_keff = sp.k_combined
lib_flux = sp.get_tally(id=1).get_values(scores=['flux'])[0, 0, 0]
lib_fiss = sp.get_tally(id=1).get_values(scores=['fission'])[0, 0, 0]
# and lets compare results
assert lib_keff.n == pytest.approx(cli_keff.n, abs=1e-13)
assert lib_flux == pytest.approx(cli_flux, abs=1e-13)
assert lib_fiss == pytest.approx(cli_fiss, abs=1e-13)
# Now we should make sure that the flags for items which should be handled
# by init are properly set
with pytest.raises(ValueError):
test_model.run(threads=1)
with pytest.raises(ValueError):
test_model.run(geometry_debug=True)
with pytest.raises(ValueError):
test_model.run(restart_file='1.h5')
with pytest.raises(ValueError):
test_model.run(tracks=True)
test_model.finalize_lib()
def test_plots(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
# This test cannot check the correctness of the plot, but it can
# check that a plot was made and that the expected png files are there
# We will run the test twice, the first time without C API, the second with
for i in range(2):
if i == 1:
test_model.init_lib(output=False, intracomm=mpi_intracomm)
test_model.plot_geometry(output=False)
# Now look for the files
for fname in ('test.png', 'plot_2.png'):
test_file = Path(fname)
assert test_file.exists()
test_file.unlink()
test_model.finalize_lib()
def test_py_lib_attributes(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
test_model.init_lib(output=False, intracomm=mpi_intracomm)
# Now we can call rotate_cells, translate_cells, update_densities,
# and update_cell_temperatures and make sure the changes have taken hold.
# For each we will first try bad inputs to make sure we get the right
# errors and then we do a good one which calls the material by name and
# then id to make sure it worked
# The rotate_cells and translate_cells will work on the cell named fill, as
# it is filled with a universe and thus the operation will be valid
# First rotate_cells
with pytest.raises(TypeError):
# Make sure it tells us we have a bad names_or_ids type
test_model.rotate_cells(None, (0, 0, 90))
with pytest.raises(TypeError):
test_model.rotate_cells([None], (0, 0, 90))
with pytest.raises(openmc.exceptions.InvalidIDError):
# Make sure it tells us we had a bad id
test_model.rotate_cells([7200], (0, 0, 90))
with pytest.raises(openmc.exceptions.InvalidIDError):
# Make sure it tells us we had a bad id
test_model.rotate_cells(['bad_name'], (0, 0, 90))
# Now a good one
assert np.all(openmc.lib.cells[2].rotation == (0., 0., 0.))
test_model.rotate_cells([2], (0, 0, 90))
assert np.all(openmc.lib.cells[2].rotation == (0., 0., 90.))
# And same thing by name
test_model.rotate_cells(['fuel'], (0, 0, 180))
# Now translate_cells. We dont need to re-check the TypeErrors/bad ids,
# because the other functions use the same hidden method as rotate_cells
assert np.all(openmc.lib.cells[2].translation == (0., 0., 0.))
test_model.translate_cells([2], (0, 0, 10))
assert np.all(openmc.lib.cells[2].translation == (0., 0., 10.))
# Now lets do the density updates.
# Check initial conditions
assert openmc.lib.materials[1].get_density('atom/b-cm') == \
pytest.approx(0.06891296988603757, abs=1e-13)
mat_a_dens = np.sum(
[v[1] for v in test_model.materials[0].
get_nuclide_atom_densities().values()])
assert mat_a_dens == pytest.approx(0.06891296988603757, abs=1e-8)
# Change the density
test_model.update_densities(['UO2'], 2.)
assert openmc.lib.materials[1].get_density('atom/b-cm') == \
pytest.approx(2., abs=1e-13)
mat_a_dens = np.sum(
[v[1] for v in test_model.materials[0].
get_nuclide_atom_densities().values()])
assert mat_a_dens == pytest.approx(2., abs=1e-8)
# Now lets do the cell temperature updates.
# Check initial conditions
assert test_model._cells_by_id == \
{2: geom.root_universe.cells[2], 3: geom.root_universe.cells[3],
4: geom.root_universe.cells[4],
1: geom.root_universe.cells[2].fill.cells[1]}
assert openmc.lib.cells[3].get_temperature() == \
pytest.approx(293.6, abs=1e-13)
assert test_model.geometry.root_universe.cells[3].temperature is None
# Change the temperature
test_model.update_cell_temperatures([3], 600.)
assert openmc.lib.cells[3].get_temperature() == \
pytest.approx(600., abs=1e-13)
assert test_model.geometry.root_universe.cells[3].temperature == \
pytest.approx(600., abs=1e-13)
# And finally material volume
assert openmc.lib.materials[1].volume == \
pytest.approx(0.4831931368640985, abs=1e-13)
# The temperature on the material will be None because its just the default
assert test_model.materials[0].volume == \
pytest.approx(0.4831931368640985, abs=1e-13)
# Change the temperature
test_model.update_material_volumes(['UO2'], 2.)
assert openmc.lib.materials[1].volume == pytest.approx(2., abs=1e-13)
assert test_model.materials[0].volume == pytest.approx(2., abs=1e-13)
test_model.finalize_lib()
def test_deplete(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, op_kwargs, chain_file_xml = \
pin_model_attributes
with open('test_chain.xml', 'w') as f:
f.write(chain_file_xml)
test_model = openmc.Model(geom, mats, settings, tals, plots)
initial_mat = mats[0].clone()
initial_u = initial_mat.get_nuclide_atom_densities()['U235'][1]
# Note that the chain file includes only U-235 fission to a stable Xe136 w/
# a yield of 100%. Thus all the U235 we lose becomes Xe136
# In this test we first run without pre-initializing the shared library
# data and then compare. Then we repeat with the C API already initialized
# and make sure we get the same answer
test_model.deplete([1e6], 'predictor', final_step=False,
operator_kwargs=op_kwargs,
power=1., output=False)
# Get the new Xe136 and U235 atom densities
after_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1]
after_u = mats[0].get_nuclide_atom_densities()['U235'][1]
assert after_xe + after_u == pytest.approx(initial_u, abs=1e-15)
assert test_model.is_initialized is False
# Reset the initial material densities
mats[0].nuclides.clear()
densities = initial_mat.get_nuclide_atom_densities()
tot_density = 0.
for nuc, density in densities.values():
mats[0].add_nuclide(nuc, density)
tot_density += density
mats[0].set_density('atom/b-cm', tot_density)
# Now we can re-run with the pre-initialized API
test_model.init_lib(output=False, intracomm=mpi_intracomm)
test_model.deplete([1e6], 'predictor', final_step=False,
operator_kwargs=op_kwargs,
power=1., output=False)
# Get the new Xe136 and U235 atom densities
after_lib_xe = mats[0].get_nuclide_atom_densities()['Xe136'][1]
after_lib_u = mats[0].get_nuclide_atom_densities()['U235'][1]
assert after_lib_xe + after_lib_u == pytest.approx(initial_u, abs=1e-15)
assert test_model.is_initialized is True
# And end by comparing to the previous case
assert after_xe == pytest.approx(after_lib_xe, abs=1e-15)
assert after_u == pytest.approx(after_lib_u, abs=1e-15)
test_model.finalize_lib()
def test_calc_volumes(run_in_tmpdir, pin_model_attributes, mpi_intracomm):
mats, geom, settings, tals, plots, _, _ = pin_model_attributes
test_model = openmc.Model(geom, mats, settings, tals, plots)
# With no vol calcs, it should fail
with pytest.raises(ValueError):
test_model.calculate_volumes(output=False)
# Add a cell and mat volume calc
material_vol_calc = openmc.VolumeCalculation(
[mats[2]], samples=1000, lower_left=(-.63, -.63, -100.),
upper_right=(.63, .63, 100.))
cell_vol_calc = openmc.VolumeCalculation(
[geom.root_universe.cells[3]], samples=1000,
lower_left=(-.63, -.63, -100.), upper_right=(.63, .63, 100.))
test_model.settings.volume_calculations = \
[material_vol_calc, cell_vol_calc]
# Now lets compute the volumes and check to see if it was applied
# First lets do without using the C-API
# Make sure the volumes are unassigned first
assert mats[2].volume is None
assert geom.root_universe.cells[3].volume is None
test_model.calculate_volumes(output=False, apply_volumes=True)
# Now let's test that we have volumes assigned; we arent checking the
# value, just that the value was changed
assert mats[2].volume > 0.
assert geom.root_universe.cells[3].volume > 0.
# Now reset the values
mats[2].volume = None
geom.root_universe.cells[3].volume = None
# And do again with an initialized library
for file in ['volume_1.h5', 'volume_2.h5']:
file = Path(file)
file.unlink()
test_model.init_lib(output=False, intracomm=mpi_intracomm)
test_model.calculate_volumes(output=False, apply_volumes=True)
assert mats[2].volume > 0.
assert geom.root_universe.cells[3].volume > 0.
assert openmc.lib.materials[3].volume == mats[2].volume
test_model.finalize_lib()

View file

@ -101,8 +101,12 @@ def test_mixture():
assert mix.distribution == [d1, d2]
assert len(mix) == 4
with pytest.raises(NotImplementedError):
mix.to_xml_element('distribution')
elem = mix.to_xml_element('distribution')
d = openmc.stats.Mixture.from_xml_element(elem)
assert d.probability == p
assert d.distribution == [d1, d2]
assert len(d) == 4
def test_polar_azimuthal():

2
vendor/fmt vendored

@ -1 +1 @@
Subproject commit 65ac626c5856f5aad1f1542e79407a6714357043
Subproject commit d141cdbeb0fb422a3fb7173b285fd38e0d1772dc