From f83ab39b99dea7d5e1446585c6e8ea03f6bad413 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 8 Feb 2021 22:25:17 -0500 Subject: [PATCH 01/68] First pass at exposing rectilinear mesh through C API --- include/openmc/capi.h | 16 ++- include/openmc/mesh.h | 28 +++-- openmc/cmfd.py | 201 +++++++++++++++++++++++--------- openmc/lib/filter.py | 6 +- openmc/lib/mesh.py | 246 +++++++++++++++++++++++++++++---------- src/mesh.cpp | 259 ++++++++++++++++++++++++++++-------------- 6 files changed, 537 insertions(+), 219 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 605d4040b..6bde7b670 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -28,7 +28,8 @@ extern "C" { int openmc_extend_cells(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_filters(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_extend_materials(int32_t n, int32_t* index_start, int32_t* index_end); - int openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end); + int openmc_extend_meshes(int32_t n, const char* type, int32_t* index_start, + int32_t* index_end); int openmc_extend_tallies(int32_t n, int32_t* index_start, int32_t* index_end); int openmc_filter_get_id(int32_t index, int32_t* id); int openmc_filter_get_type(int32_t index, char* type); @@ -73,11 +74,7 @@ extern "C" { int openmc_mesh_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_mesh_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_mesh_get_id(int32_t index, int32_t* id); - int openmc_mesh_get_dimension(int32_t index, int** id, int* n); - int openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n); int openmc_mesh_set_id(int32_t index, int32_t id); - int openmc_mesh_set_dimension(int32_t index, int n, const int* dims); - int openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width); int openmc_meshsurface_filter_get_mesh(int32_t index, int32_t* index_mesh); int openmc_meshsurface_filter_set_mesh(int32_t index, int32_t index_mesh); int openmc_new_filter(const char* type, int32_t* index); @@ -86,6 +83,15 @@ extern "C" { int openmc_plot_geometry(); int openmc_id_map(const void* slice, int32_t* data_out); int openmc_property_map(const void* slice, double* data_out); + int openmc_rectilinear_mesh_get_grid(int32_t index, double** grid_x, int* nx, + double** grid_y, int * ny, double** grid_z, int* nz); + int openmc_rectilinear_mesh_set_grid(int32_t index, const double* grid_x, + const int nx, const double* grid_y, const int ny, + const double* grid_z, const int nz); + int openmc_regular_mesh_get_dimension(int32_t index, int** id, int* n); + int openmc_regular_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n); + int openmc_regular_mesh_set_dimension(int32_t index, int n, const int* dims); + int openmc_regular_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width); int openmc_reset(); int openmc_reset_timers(); int openmc_run(); diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index 5a8367b8b..b2e35819c 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -46,6 +46,8 @@ public: Mesh(pugi::xml_node node); virtual ~Mesh() = default; + virtual std::string type() const = 0; + // Methods //! Determine which bins were crossed by a particle @@ -114,6 +116,14 @@ public: void bins_crossed(const Particle& p, std::vector& bins, std::vector& lengths) const override; + //! Count number of bank sites in each mesh bin / energy bin + // + //! \param[in] Pointer to bank sites + //! \param[in] Number of bank sites + //! \param[out] Whether any bank sites are outside the mesh + xt::xtensor count_sites(const Particle::Bank* bank, + int64_t length, bool* outside) const; + //! Get bin given mesh indices // //! \param[in] Array of mesh indices @@ -186,6 +196,8 @@ public: // Overriden methods + std::string type() const override {return "regular";} + void surface_bins_crossed(const Particle& p, std::vector& bins) const override; @@ -200,16 +212,6 @@ public: void to_hdf5(hid_t group) const override; - // New methods - - //! Count number of bank sites in each mesh bin / energy bin - // - //! \param[in] bank Array of bank sites - //! \param[out] Whether any bank sites are outside the mesh - //! \return Array indicating number of sites in each mesh/energy bin - xt::xtensor count_sites(const Particle::Bank* bank, int64_t length, - bool* outside) const; - // Data members double volume_frac_; //!< Volume fraction of each mesh element @@ -221,10 +223,13 @@ class RectilinearMesh : public StructuredMesh { public: // Constructors + RectilinearMesh() = default; RectilinearMesh(pugi::xml_node node); // Overriden methods + std::string type() const override {return "rectilinear";} + void surface_bins_crossed(const Particle& p, std::vector& bins) const override; @@ -239,7 +244,6 @@ public: void to_hdf5(hid_t group) const override; -private: std::vector> grid_; }; @@ -252,6 +256,8 @@ public: UnstructuredMesh(pugi::xml_node); ~UnstructuredMesh() = default; + std::string type() const override {return "unstructured";} + void bins_crossed(const Particle& p, std::vector& bins, std::vector& lengths) const override; diff --git a/openmc/cmfd.py b/openmc/cmfd.py index a99b60619..91c264633 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -60,15 +60,17 @@ class CMFDMesh: Attributes ---------- lower_left : Iterable of float - 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. + The lower-left corner of a regular structured mesh. If only two + coordinates are given, it is assumed that the mesh is an x-y mesh. upper_right : Iterable of float - The upper-right corner of the structured mesh. If only two coordinates - are given, it is assumed that the mesh is an x-y mesh. + The upper-right corner of a regular structured mesh. If only two + coordinates are given, it is assumed that the mesh is an x-y mesh. dimension : Iterable of int - The number of mesh cells in each direction. + The number of mesh cells in each direction for a regular structured + mesh. width : Iterable of float - The width of mesh cells in each direction. + The width of mesh cells in each direction for a regular structured + mesh energy : Iterable of float Energy bins in eV, listed in ascending order (e.g. [0.0, 0.625e-1, 20.0e6]) for CMFD tallies and acceleration. If no energy bins are @@ -96,6 +98,14 @@ class CMFDMesh: is extremely important to use in reflectors as neutrons will not contribute to any tallies far away from fission source neutron regions. A ``1`` must be used to identify any fission source region. + mesh_type : str + Type of structured mesh to use. Acceptable values are: + * "regular" - Use RegularMesh to define CMFD mesh + * "rectilinear" - Use RectilinearMesh to define CMFD + grid : Iterable of Iterable of float + Grid used to define RectilinearMesh. First dimension must have length + 3 where grid[0], grid[1], and grid[2] correspond to the x-, y-, and + z-grids respectively """ @@ -107,6 +117,28 @@ class CMFDMesh: self._energy = None self._albedo = None self._map = None + self._mesh_type = 'regular' + self._grid = None + + def __repr__(self): + outstr = type(self).__name__ + '\n' + if self._mesh_type == 'regular': + outstr += (self._get_repr(self._lower_left, "Lower left") + "\n" + + self._get_repr(self._upper_right, "Upper right") + "\n" + + self._get_repr(self._dimension, "Dimension") + "\n" + + self._get_repr(self._width, "Width") + "\n" + + self._get_repr(self._albedo, "Albedo")) + elif self._mesh_type == 'rectilinear': + outstr += (self._get_repr(self._grid[0], "X-grid") + "\n" + + self._get_repr(self._grid[1], "Y-grid") + "\n" + + self._get_repr(self._grid[2], "Z-grid")) + return outstr + + def _get_repr(self, list_var, label): + outstr = "\t{:<11} = ".format(label) + if list(list_var): + outstr += ", ".join(str(i) for i in list_var) + return outstr @property def lower_left(self): @@ -136,17 +168,27 @@ class CMFDMesh: def map(self): return self._map + @property + def mesh_type(self): + return self._mesh_type + + @property + def grid(self): + return self._grid + @lower_left.setter def lower_left(self, lower_left): check_type('CMFD mesh lower_left', lower_left, Iterable, Real) check_length('CMFD mesh lower_left', lower_left, 2, 3) self._lower_left = lower_left + self._display_mesh_warning('regular', 'CMFD mesh lower_left') @upper_right.setter def upper_right(self, upper_right): check_type('CMFD mesh upper_right', upper_right, Iterable, Real) check_length('CMFD mesh upper_right', upper_right, 2, 3) self._upper_right = upper_right + self._display_mesh_warning('regular', 'CMFD mesh upper_right') @dimension.setter def dimension(self, dimension): @@ -163,6 +205,7 @@ class CMFDMesh: for w in width: check_greater_than('CMFD mesh width', w, 0) self._width = width + self._display_mesh_warning('regular', 'CMFD mesh width') @energy.setter def energy(self, energy): @@ -187,6 +230,31 @@ class CMFDMesh: check_value('CMFD mesh map', m, [0, 1]) self._map = mesh_map + @mesh_type.setter + def mesh_type(self, mesh_type): + check_value('CMFD mesh type', mesh_type, ['regular', 'rectilinear']) + self._mesh_type = mesh_type + + @grid.setter + def grid(self, grid): + grid_length = 3 + dims = ['x', 'y', 'z'] + + check_length('CMFD mesh grid', grid, grid_length) + for i in range(grid_length): + check_type('CMFD mesh {}-grid'.format(dims[i]), grid[i], Iterable, + Real) + check_greater_than('CMFD mesh {}-grid length'.format(dims[i]), + len(grid[i]), 1) + self._grid = np.array(grid) + self._display_mesh_warning('rectilinear', 'CMFD mesh grid') + + def _display_mesh_warning(self, mesh_type, variable_label): + if self._mesh_type != mesh_type: + warn_msg = 'Setting {} if mesh type is not set to {} ' \ + 'will have no effect'.format(variable_label, mesh_type) + warnings.warn(warn_msg, RuntimeWarning) + class CMFDRun: r"""Class for running CMFD acceleration through the C API. @@ -566,48 +634,56 @@ class CMFDRun: def mesh(self, cmfd_mesh): check_type('CMFD mesh', cmfd_mesh, CMFDMesh) - # Check dimension defined - if cmfd_mesh.dimension is None: - raise ValueError('CMFD mesh requires spatial ' - 'dimensions to be specified') + if cmfd_mesh.mesh_type == 'regular': + # Check dimension defined + if cmfd_mesh.dimension is None: + raise ValueError('CMFD regular mesh requires spatial ' + 'dimensions to be specified') - # Check lower left defined - if cmfd_mesh.lower_left is None: - raise ValueError('CMFD mesh requires lower left coordinates ' - 'to be specified') + # Check lower left defined + if cmfd_mesh.lower_left is None: + raise ValueError('CMFD regular mesh requires lower left ' + 'coordinates to be specified') - # Check that both upper right and width both not defined - if cmfd_mesh.upper_right is not None and cmfd_mesh.width is not None: - raise ValueError('Both upper right coordinates and width ' - 'cannot be specified for CMFD mesh') + # Check that both upper right and width both not defined + if cmfd_mesh.upper_right is not None and cmfd_mesh.width is not None: + raise ValueError('Both upper right coordinates and width ' + 'cannot be specified for CMFD regular mesh') - # Check that at least one of width or upper right is defined - if cmfd_mesh.upper_right is None and cmfd_mesh.width is None: - raise ValueError('CMFD mesh requires either upper right ' - 'coordinates or width to be specified') + # Check that at least one of width or upper right is defined + if cmfd_mesh.upper_right is None and cmfd_mesh.width is None: + raise ValueError('CMFD regular mesh requires either upper right ' + 'coordinates or width to be specified') - # Check width and lower length are same dimension and define - # upper_right - if cmfd_mesh.width is not None: - check_length('CMFD mesh width', cmfd_mesh.width, - len(cmfd_mesh.lower_left)) - cmfd_mesh.upper_right = np.array(cmfd_mesh.lower_left) + \ - np.array(cmfd_mesh.width) * np.array(cmfd_mesh.dimension) + # Check width and lower length are same dimension and define + # upper_right + if cmfd_mesh.width is not None: + check_length('CMFD mesh width', cmfd_mesh.width, + len(cmfd_mesh.lower_left)) + cmfd_mesh.upper_right = np.array(cmfd_mesh.lower_left) + \ + np.array(cmfd_mesh.width) * np.array(cmfd_mesh.dimension) + + # Check upper_right and lower length are same dimension and define + # width + elif cmfd_mesh.upper_right is not None: + check_length('CMFD mesh upper right', cmfd_mesh.upper_right, + len(cmfd_mesh.lower_left)) + # Check upper right coordinates are greater than lower left + if np.any(np.array(cmfd_mesh.upper_right) <= + np.array(cmfd_mesh.lower_left)): + raise ValueError('CMFD regular mesh requires upper right ' + 'coordinates to be greater than lower ' + 'left coordinates') + cmfd_mesh.width = np.true_divide((np.array(cmfd_mesh.upper_right) - + np.array(cmfd_mesh.lower_left)), + np.array(cmfd_mesh.dimension)) + elif cmfd_mesh.mesh_type == 'rectilinear': + # Check dimension defined + if cmfd_mesh.grid is None: + raise ValueError('CMFD rectilinear mesh requires spatial ' + 'grid to be specified') + cmfd_mesh.dimension = [len(cmfd_mesh.grid[i]) - 1 for i in range(3)] - # Check upper_right and lower length are same dimension and define - # width - elif cmfd_mesh.upper_right is not None: - check_length('CMFD mesh upper right', cmfd_mesh.upper_right, - len(cmfd_mesh.lower_left)) - # Check upper right coordinates are greater than lower left - if np.any(np.array(cmfd_mesh.upper_right) <= - np.array(cmfd_mesh.lower_left)): - raise ValueError('CMFD mesh requires upper right ' - 'coordinates to be greater than lower ' - 'left coordinates') - cmfd_mesh.width = np.true_divide((np.array(cmfd_mesh.upper_right) - - np.array(cmfd_mesh.lower_left)), - np.array(cmfd_mesh.dimension)) self._mesh = cmfd_mesh @norm.setter @@ -1088,10 +1164,17 @@ class CMFDRun: # Overwrite CMFD mesh properties cmfd_mesh_name = 'mesh ' + str(cmfd_group.attrs['mesh_id']) cmfd_mesh = f['tallies']['meshes'][cmfd_mesh_name] - self._mesh.dimension = cmfd_mesh['dimension'][()] - self._mesh.lower_left = cmfd_mesh['lower_left'][()] - self._mesh.upper_right = cmfd_mesh['upper_right'][()] - self._mesh.width = cmfd_mesh['width'][()] + self._mesh.mesh_type = cmfd_mesh['type'][()].decode() + if self._mesh.mesh_type == 'regular': + self._mesh.dimension = cmfd_mesh['dimension'][()] + self._mesh.lower_left = cmfd_mesh['lower_left'][()] + self._mesh.upper_right = cmfd_mesh['upper_right'][()] + self._mesh.width = cmfd_mesh['width'][()] + elif self._mesh.mesh_type == 'rectilinear': + x_grid = cmfd_mesh['x_grid'][()] + y_grid = cmfd_mesh['y_grid'][()] + z_grid = cmfd_mesh['z_grid'][()] + self._mesh.grid = [x_grid, y_grid, z_grid] # Define variables that exist only on master process if openmc.lib.master(): @@ -1370,8 +1453,7 @@ class CMFDRun: # Compute fission source cmfd_src = (np.sum(self._nfissxs[:,:,:,:,:] * - cmfd_flux[:,:,:,:,np.newaxis], axis=3) * - vol[:,:,:,np.newaxis]) + cmfd_flux[:,:,:,:,np.newaxis], axis=3)) # Normalize source such that it sums to 1.0 self._cmfd_src = cmfd_src / np.sum(cmfd_src) @@ -2804,15 +2886,22 @@ class CMFDRun: def _create_cmfd_tally(self): """Creates all tallies in-memory that are used to solve CMFD problem""" - # Create Mesh object based on CMFDMesh, stored internally - cmfd_mesh = openmc.lib.RegularMesh() + # Create Mesh object based on CMFDMesh mesh_type, stored internally + if self._mesh.mesh_type == 'regular': + cmfd_mesh = openmc.lib.RegularMesh() + # Set dimension and parameters of mesh object + cmfd_mesh.dimension = self._mesh.dimension + cmfd_mesh.set_parameters(lower_left=self._mesh.lower_left, + upper_right=self._mesh.upper_right, + width=self._mesh.width) + elif self._mesh.mesh_type == 'rectilinear': + cmfd_mesh = openmc.lib.RectilinearMesh() + # Set grid of mesh object + x_grid, y_grid, z_grid = self._mesh.grid + cmfd_mesh.set_grid(x_grid, y_grid, z_grid) + # Store id of mesh object self._mesh_id = cmfd_mesh.id - # Set dimension and parameters of mesh object - cmfd_mesh.dimension = self._mesh.dimension - cmfd_mesh.set_parameters(lower_left=self._mesh.lower_left, - upper_right=self._mesh.upper_right, - width=self._mesh.width) # Create mesh Filter object, stored internally mesh_filter = openmc.lib.MeshFilter() diff --git a/openmc/lib/filter.py b/openmc/lib/filter.py index 1d16b68e0..a47b374e2 100644 --- a/openmc/lib/filter.py +++ b/openmc/lib/filter.py @@ -11,7 +11,7 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler from .material import Material -from .mesh import RegularMesh +from .mesh import _get_mesh __all__ = [ @@ -315,7 +315,7 @@ class MeshFilter(Filter): def mesh(self): index_mesh = c_int32() _dll.openmc_mesh_filter_get_mesh(self._index, index_mesh) - return RegularMesh(index=index_mesh.value) + return _get_mesh(index_mesh.value) @mesh.setter def mesh(self, mesh): @@ -334,7 +334,7 @@ class MeshSurfaceFilter(Filter): def mesh(self): index_mesh = c_int32() _dll.openmc_meshsurface_filter_get_mesh(self._index, index_mesh) - return RegularMesh(index=index_mesh.value) + return _get_mesh(index_mesh.value) @mesh.setter def mesh(self, mesh): diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index e96115049..92684ec8d 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -1,5 +1,6 @@ from collections.abc import Mapping -from ctypes import c_int, c_int32, c_double, POINTER +from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER, + create_string_buffer) from weakref import WeakValueDictionary from numpy.ctypeslib import as_array @@ -9,38 +10,97 @@ from . import _dll from .core import _FortranObjectWithID from .error import _error_handler -__all__ = ['RegularMesh', 'meshes'] +__all__ = ['RegularMesh', 'RectilinearMesh', 'meshes'] # Mesh functions -_dll.openmc_extend_meshes.argtypes = [c_int32, POINTER(c_int32), POINTER(c_int32)] +_dll.openmc_extend_meshes.argtypes = [c_int32, c_char_p, POINTER(c_int32), + POINTER(c_int32)] _dll.openmc_extend_meshes.restype = c_int _dll.openmc_extend_meshes.errcheck = _error_handler _dll.openmc_mesh_get_id.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_mesh_get_id.restype = c_int _dll.openmc_mesh_get_id.errcheck = _error_handler -_dll.openmc_mesh_get_dimension.argtypes = [c_int32, POINTER(POINTER(c_int)), POINTER(c_int)] -_dll.openmc_mesh_get_dimension.restype = c_int -_dll.openmc_mesh_get_dimension.errcheck = _error_handler -_dll.openmc_mesh_get_params.argtypes = [ - c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), - POINTER(POINTER(c_double)), POINTER(c_int)] -_dll.openmc_mesh_get_params.restype = c_int -_dll.openmc_mesh_get_params.errcheck = _error_handler _dll.openmc_mesh_set_id.argtypes = [c_int32, c_int32] _dll.openmc_mesh_set_id.restype = c_int _dll.openmc_mesh_set_id.errcheck = _error_handler -_dll.openmc_mesh_set_dimension.argtypes = [c_int32, c_int, POINTER(c_int)] -_dll.openmc_mesh_set_dimension.restype = c_int -_dll.openmc_mesh_set_dimension.errcheck = _error_handler -_dll.openmc_mesh_set_params.argtypes = [ - c_int32, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)] -_dll.openmc_mesh_set_params.restype = c_int -_dll.openmc_mesh_set_params.errcheck = _error_handler _dll.openmc_get_mesh_index.argtypes = [c_int32, POINTER(c_int32)] _dll.openmc_get_mesh_index.restype = c_int _dll.openmc_get_mesh_index.errcheck = _error_handler _dll.n_meshes.argtypes = [] _dll.n_meshes.restype = c_int +_dll.openmc_rectilinear_mesh_get_grid.argtypes = [c_int32, + POINTER(POINTER(c_double)), POINTER(c_int), POINTER(POINTER(c_double)), + POINTER(c_int), POINTER(POINTER(c_double)), POINTER(c_int)] +_dll.openmc_rectilinear_mesh_get_grid.restype = c_int +_dll.openmc_rectilinear_mesh_get_grid.errcheck = _error_handler +_dll.openmc_rectilinear_mesh_set_grid.argtypes = [c_int32, POINTER(c_double), + c_int, POINTER(c_double), c_int, POINTER(c_double), c_int] +_dll.openmc_rectilinear_mesh_set_grid.restype = c_int +_dll.openmc_rectilinear_mesh_set_grid.errcheck = _error_handler +_dll.openmc_regular_mesh_get_dimension.argtypes = [c_int32, + POINTER(POINTER(c_int)), POINTER(c_int)] +_dll.openmc_regular_mesh_get_dimension.restype = c_int +_dll.openmc_regular_mesh_get_dimension.errcheck = _error_handler +_dll.openmc_regular_mesh_get_params.argtypes = [ + c_int32, POINTER(POINTER(c_double)), POINTER(POINTER(c_double)), + POINTER(POINTER(c_double)), POINTER(c_int)] +_dll.openmc_regular_mesh_get_params.restype = c_int +_dll.openmc_regular_mesh_get_params.errcheck = _error_handler +_dll.openmc_regular_mesh_set_dimension.argtypes = [c_int32, c_int, + POINTER(c_int)] +_dll.openmc_regular_mesh_set_dimension.restype = c_int +_dll.openmc_regular_mesh_set_dimension.errcheck = _error_handler +_dll.openmc_regular_mesh_set_params.argtypes = [ + c_int32, c_int, POINTER(c_double), POINTER(c_double), POINTER(c_double)] +_dll.openmc_regular_mesh_set_params.restype = c_int +_dll.openmc_regular_mesh_set_params.errcheck = _error_handler + + +class Mesh(_FortranObjectWithID): + """Base class to represent mesh objects + + """ + __instances = WeakValueDictionary() + + def __new__(cls, uid=None, new=True, index=None): + mapping = meshes + if index is None: + if new: + # Determine ID to assign + if uid is None: + uid = max(mapping, default=0) + 1 + else: + if uid in mapping: + raise AllocationError('A mesh with ID={} has already ' + 'been allocated.'.format(uid)) + + # Set the mesh type -- note that mesh type attribute only + # exists on subclasses! + index = c_int32() + _dll.openmc_extend_meshes(1, cls.mesh_type.encode(), index, + None) + index = index.value + else: + index = mapping[uid]._index + + if index not in cls.__instances: + instance = super().__new__(cls) + instance._index = index + if uid is not None: + instance.id = uid + cls.__instances[index] = instance + + return cls.__instances[index] + + @property + def id(self): + mesh_id = c_int32() + _dll.openmc_mesh_get_id(self._index, mesh_id) + return mesh_id.value + + @id.setter + def id(self, mesh_id): + _dll.openmc_mesh_set_id(self._index, mesh_id) class RegularMesh(_FortranObjectWithID): @@ -71,57 +131,24 @@ class RegularMesh(_FortranObjectWithID): The width of mesh cells in each direction. """ - __instances = WeakValueDictionary() + mesh_type = 'regular' - def __new__(cls, uid=None, new=True, index=None): - mapping = meshes - if index is None: - if new: - # Determine ID to assign - if uid is None: - uid = max(mapping, default=0) + 1 - else: - if uid in mapping: - raise AllocationError('A mesh with ID={} has already ' - 'been allocated.'.format(uid)) - - index = c_int32() - _dll.openmc_extend_meshes(1, index, None) - index = index.value - else: - index = mapping[uid]._index - - if index not in cls.__instances: - instance = super().__new__(cls) - instance._index = index - if uid is not None: - instance.id = uid - cls.__instances[index] = instance - - return cls.__instances[index] - - @property - def id(self): - mesh_id = c_int32() - _dll.openmc_mesh_get_id(self._index, mesh_id) - return mesh_id.value - - @id.setter - def id(self, mesh_id): - _dll.openmc_mesh_set_id(self._index, mesh_id) + def __init__(self, uid=None, new=True, index=None): + super().__init__(uid, new, index) @property def dimension(self): dims = POINTER(c_int)() n = c_int() - _dll.openmc_mesh_get_dimension(self._index, dims, n) + _dll.openmc_regular_mesh_get_dimension(self._index, dims, n) return tuple(as_array(dims, (n.value,))) @dimension.setter def dimension(self, dimension): n = len(dimension) dimension = (c_int*n)(*dimension) - _dll.openmc_mesh_set_dimension(self._index, n, dimension) + _dll.openmc_regular_mesh_set_dimension(self._index, n, dimension) + @property def lower_left(self): @@ -140,7 +167,7 @@ class RegularMesh(_FortranObjectWithID): ur = POINTER(c_double)() w = POINTER(c_double)() n = c_int() - _dll.openmc_mesh_get_params(self._index, ll, ur, w, n) + _dll.openmc_regular_mesh_get_params(self._index, ll, ur, w, n) return ( as_array(ll, (n.value,)), as_array(ur, (n.value,)), @@ -157,7 +184,103 @@ class RegularMesh(_FortranObjectWithID): if width is not None: n = len(width) width = (c_double*n)(*width) - _dll.openmc_mesh_set_params(self._index, n, lower_left, upper_right, width) + _dll.openmc_regular_mesh_set_params(self._index, n, lower_left, upper_right, width) + + +class RectilinearMesh(Mesh): + """RectilinearMesh stored internally. + This class exposes a mesh that is stored internally in the OpenMC + library. To obtain a view of a mesh with a given ID, use the + :data:`openmc.lib.meshes` mapping. + Parameters + ---------- + index : int + Index in the `meshes` array. + Attributes + ---------- + id : int + ID of the mesh + dimension : iterable of int + The number of mesh cells in each direction. + lower_left : numpy.ndarray + The lower-left corner of the structured mesh. + upper_right : numpy.ndarray + The upper-right corner of the structrued mesh. + width : numpy.ndarray + The width of mesh cells in each direction. + """ + mesh_type = 'rectilinear' + + def __init__(self, uid=None, new=True, index=None): + super().__init__(uid, new, index) + + @property + def lower_left(self): + return self._get_parameters()[0] + + @property + def upper_right(self): + return self._get_parameters()[1] + + @property + def dimension(self): + return self._get_parameters()[2] + + @property + def width(self): + return self._get_parameters()[3] + + def _get_parameters(self): + gx = POINTER(c_double)() + nx = c_int() + gy = POINTER(c_double)() + ny = c_int() + gz = POINTER(c_double)() + nz = c_int() + # Call C API to get grid parameters + _dll.openmc_rectilinear_mesh_get_grid(self._index, gx, nx, gy, ny, gz, + nz) + + # Convert grid parameters to Numpy arrays + grid_x = as_array(gx, (nx.value,)) + grid_y = as_array(gy, (ny.value,)) + grid_z = as_array(gz, (nz.value,)) + + # Calculate lower_left, upper_right, width, and dimension from grid + lower_left = np.array((grid_x[0], grid_y[0], grid_z[0])) + upper_right = np.array((grid_x[-1], grid_y[-1], grid_z[-1])) + dimension = np.array((nx.value - 1, ny.value - 1, nz.value - 1)) + width = np.zeros(list(dimension) + [3]) + + for i, diff_x in enumerate(np.diff(grid_x)): + for j, diff_y in enumerate(np.diff(grid_y)): + for k, diff_z in enumerate(np.diff(grid_z)): + width[i, j, k, :] = diff_x, diff_y, diff_z + + return (lower_left, upper_right, dimension, width) + + def set_grid(self, x_grid, y_grid, z_grid): + nx = len(x_grid) + x_grid = (c_double*nx)(*x_grid) + ny = len(y_grid) + y_grid = (c_double*ny)(*y_grid) + nz = len(z_grid) + z_grid = (c_double*nz)(*z_grid) + _dll.openmc_rectilinear_mesh_set_grid(self._index, x_grid, nx, y_grid, + ny, z_grid, nz) + + +_MESH_TYPE_MAP = { + 'regular': RegularMesh, + 'rectilinear': RectilinearMesh +} + + +def _get_mesh(index): + mesh_type = create_string_buffer(20) + _dll.openmc_mesh_get_type(index, mesh_type) + mesh_type = mesh_type.value.decode() + return _MESH_TYPE_MAP[mesh_type](index=index) class _MeshMapping(Mapping): @@ -168,11 +291,12 @@ class _MeshMapping(Mapping): except (AllocationError, InvalidIDError) as e: # __contains__ expects a KeyError to work correctly raise KeyError(str(e)) - return RegularMesh(index=index.value) + return _get_mesh(index.value) + def __iter__(self): for i in range(len(self)): - yield RegularMesh(index=i).id + yield _get_mesh(i).id def __len__(self): return _dll.n_meshes() diff --git a/src/mesh.cpp b/src/mesh.cpp index a5409f05a..576ae3e77 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -162,6 +162,62 @@ int StructuredMesh::n_surface_bins() const return 4 * n_dimension_ * n_bins(); } +xt::xtensor +StructuredMesh::count_sites(const Particle::Bank* bank, + int64_t length, + bool* outside) const +{ + // Determine shape of array for counts + std::size_t m = this->n_bins(); + std::vector shape = {m}; + + // Create array of zeros + xt::xarray cnt {shape, 0.0}; + bool outside_ = false; + + for (int64_t i = 0; i < length; i++) { + const auto& site = bank[i]; + + // determine scoring bin for entropy mesh + int mesh_bin = get_bin(site.r); + + // if outside mesh, skip particle + if (mesh_bin < 0) { + outside_ = true; + continue; + } + + // Add to appropriate bin + cnt(mesh_bin) += site.wgt; + } + + // Create copy of count data. Since ownership will be acquired by xtensor, + // std::allocator must be used to avoid Valgrind mismatched free() / delete + // warnings. + int total = cnt.size(); + double* cnt_reduced = std::allocator{}.allocate(total); + +#ifdef OPENMC_MPI + // collect values from all processors + MPI_Reduce(cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, + mpi::intracomm); + + // Check if there were sites outside the mesh for any processor + if (outside) { + MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); + } +#else + std::copy(cnt.data(), cnt.data() + total, cnt_reduced); + if (outside) *outside = outside_; +#endif + + // Adapt reduced values in array back into an xarray + auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape); + xt::xarray counts = arr; + + return counts; +} + bool StructuredMesh::intersects(Position& r0, Position r1, int* ijk) const { switch(n_dimension_) { @@ -809,7 +865,7 @@ void RegularMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - write_dataset(mesh_group, "type", "regular"); + write_dataset(mesh_group, "type", type()); write_dataset(mesh_group, "dimension", shape_); write_dataset(mesh_group, "lower_left", lower_left_); write_dataset(mesh_group, "upper_right", upper_right_); @@ -818,62 +874,6 @@ void RegularMesh::to_hdf5(hid_t group) const close_group(mesh_group); } -xt::xtensor -RegularMesh::count_sites(const Particle::Bank* bank, - int64_t length, - bool* outside) const -{ - // Determine shape of array for counts - std::size_t m = this->n_bins(); - std::vector shape = {m}; - - // Create array of zeros - xt::xarray cnt {shape, 0.0}; - bool outside_ = false; - - for (int64_t i = 0; i < length; i++) { - const auto& site = bank[i]; - - // determine scoring bin for entropy mesh - int mesh_bin = get_bin(site.r); - - // if outside mesh, skip particle - if (mesh_bin < 0) { - outside_ = true; - continue; - } - - // Add to appropriate bin - cnt(mesh_bin) += site.wgt; - } - - // Create copy of count data. Since ownership will be acquired by xtensor, - // std::allocator must be used to avoid Valgrind mismatched free() / delete - // warnings. - int total = cnt.size(); - double* cnt_reduced = std::allocator{}.allocate(total); - -#ifdef OPENMC_MPI - // collect values from all processors - MPI_Reduce(cnt.data(), cnt_reduced, total, MPI_DOUBLE, MPI_SUM, 0, - mpi::intracomm); - - // Check if there were sites outside the mesh for any processor - if (outside) { - MPI_Reduce(&outside_, outside, 1, MPI_C_BOOL, MPI_LOR, 0, mpi::intracomm); - } -#else - std::copy(cnt.data(), cnt.data() + total, cnt_reduced); - if (outside) *outside = outside_; -#endif - - // Adapt reduced values in array back into an xarray - auto arr = xt::adapt(cnt_reduced, total, xt::acquire_ownership(), shape); - xt::xarray counts = arr; - - return counts; -} - //============================================================================== // RectilinearMesh implementation //============================================================================== @@ -883,7 +883,7 @@ RectilinearMesh::RectilinearMesh(pugi::xml_node node) { n_dimension_ = 3; - grid_.resize(3); + grid_.resize(n_dimension_); grid_[0] = get_node_array(node, "x_grid"); grid_[1] = get_node_array(node, "y_grid"); grid_[2] = get_node_array(node, "z_grid"); @@ -1122,7 +1122,7 @@ void RectilinearMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - write_dataset(mesh_group, "type", "rectilinear"); + write_dataset(mesh_group, "type", type()); write_dataset(mesh_group, "x_grid", grid_[0]); write_dataset(mesh_group, "y_grid", grid_[1]); write_dataset(mesh_group, "z_grid", grid_[2]); @@ -1145,12 +1145,13 @@ check_mesh(int32_t index) } int -check_regular_mesh(int32_t index, RegularMesh** mesh) +check_mesh_type(int32_t index, const std::string& mesh_compare_type) { if (int err = check_mesh(index)) return err; - *mesh = dynamic_cast(model::meshes[index].get()); - if (!*mesh) { - set_errmsg("This function is only valid for regular meshes."); + StructuredMesh* mesh = dynamic_cast(model::meshes[index].get()); + auto mesh_type = mesh->type(); + if (mesh_compare_type != mesh_type) { + set_errmsg("This function is only valid for " + mesh_type + " meshes."); return OPENMC_E_INVALID_TYPE; } return 0; @@ -1160,17 +1161,37 @@ check_regular_mesh(int32_t index, RegularMesh** mesh) // C API functions //============================================================================== +// Return the type of mesh as a C string +extern "C" int +openmc_mesh_get_type(int32_t index, char* type) +{ + if (int err = check_mesh(index)) return err; + + StructuredMesh* mesh = dynamic_cast(model::meshes[index].get()); + std::strcpy(type, mesh->type().c_str()); + return 0; +} + RegularMesh* get_regular_mesh(int32_t index) { return dynamic_cast(model::meshes[index].get()); } //! Extend the meshes array by n elements extern "C" int -openmc_extend_meshes(int32_t n, int32_t* index_start, int32_t* index_end) +openmc_extend_meshes(int32_t n, const char* type, int32_t* index_start, + int32_t* index_end) { if (index_start) *index_start = model::meshes.size(); for (int i = 0; i < n; ++i) { - model::meshes.push_back(std::make_unique()); + if (std::strcmp(type, "regular") == 0) + { + model::meshes.push_back(std::make_unique()); + } else if (std::strcmp(type, "rectilinear") == 0) + { + model::meshes.push_back(std::make_unique()); + } else { + throw std::runtime_error{"Unknown mesh type: " + std::string(type)}; + } } if (index_end) *index_end = model::meshes.size() - 1; @@ -1209,23 +1230,23 @@ openmc_mesh_set_id(int32_t index, int32_t id) return 0; } -//! Get the dimension of a mesh +//! Get the dimension of a regular mesh extern "C" int -openmc_mesh_get_dimension(int32_t index, int** dims, int* n) +openmc_regular_mesh_get_dimension(int32_t index, int** dims, int* n) { - RegularMesh* mesh; - if (int err = check_regular_mesh(index, &mesh)) return err; + if (int err = check_mesh_type(index, "regular")) return err; + RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); *dims = mesh->shape_.data(); *n = mesh->n_dimension_; return 0; } -//! Set the dimension of a mesh +//! Set the dimension of a regular mesh extern "C" int -openmc_mesh_set_dimension(int32_t index, int n, const int* dims) +openmc_regular_mesh_set_dimension(int32_t index, int n, const int* dims) { - RegularMesh* mesh; - if (int err = check_regular_mesh(index, &mesh)) return err; + if (int err = check_mesh_type(index, "regular")) return err; + RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); // Copy dimension std::vector shape = {static_cast(n)}; @@ -1234,12 +1255,13 @@ openmc_mesh_set_dimension(int32_t index, int n, const int* dims) return 0; } -//! Get the mesh parameters +//! Get the regular mesh parameters extern "C" int -openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n) +openmc_regular_mesh_get_params(int32_t index, double** ll, double** ur, + double** width, int* n) { - RegularMesh* m; - if (int err = check_regular_mesh(index, &m)) return err; + if (int err = check_mesh_type(index, "regular")) return err; + RegularMesh* m = dynamic_cast(model::meshes[index].get()); if (m->lower_left_.dimension() == 0) { set_errmsg("Mesh parameters have not been set."); @@ -1253,13 +1275,13 @@ openmc_mesh_get_params(int32_t index, double** ll, double** ur, double** width, return 0; } -//! Set the mesh parameters +//! Set the regular mesh parameters extern "C" int -openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, - const double* width) +openmc_regular_mesh_set_params(int32_t index, int n, const double* ll, + const double* ur, const double* width) { - RegularMesh* m; - if (int err = check_regular_mesh(index, &m)) return err; + if (int err = check_mesh_type(index, "regular")) return err; + RegularMesh* m = dynamic_cast(model::meshes[index].get()); std::vector shape = {static_cast(n)}; if (ll && ur) { @@ -1282,6 +1304,77 @@ openmc_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, return 0; } +//! Get the rectilinear mesh grid +extern "C" int +openmc_rectilinear_mesh_get_grid(int32_t index, double** grid_x, int* nx, + double** grid_y, int * ny, double** grid_z, int* nz) +{ + if (int err = check_mesh_type(index, "rectilinear")) return err; + RectilinearMesh* m = dynamic_cast(model::meshes[index].get()); + + if (m->lower_left_.dimension() == 0) { + set_errmsg("Mesh parameters have not been set."); + return OPENMC_E_ALLOCATE; + } + + *grid_x = m->grid_[0].data(); + *nx = m->grid_[0].size(); + *grid_y = m->grid_[1].data(); + *ny = m->grid_[1].size(); + *grid_z = m->grid_[2].data(); + *nz = m->grid_[2].size(); + + return 0; +} + +//! Set the regular mesh parameters +extern "C" int +openmc_rectilinear_mesh_set_grid(int32_t index, const double* grid_x, + const int nx, const double* grid_y, const int ny, + const double* grid_z, const int nz) +{ + if (int err = check_mesh_type(index, "rectilinear")) return err; + RectilinearMesh* m = dynamic_cast(model::meshes[index].get()); + + m->n_dimension_ = 3; + m->grid_.resize(m->n_dimension_); + + for (int i = 0; i < nx; i++) { + m->grid_[0].push_back(grid_x[i]); + } + for (int i = 0; i < ny; i++) { + m->grid_[1].push_back(grid_y[i]); + } + for (int i = 0; i < nz; i++) { + m->grid_[2].push_back(grid_z[i]); + } + + m->shape_ = {static_cast(m->grid_[0].size()) - 1, + static_cast(m->grid_[1].size()) - 1, + static_cast(m->grid_[2].size()) - 1}; + + for (const auto& g : m->grid_) { + if (g.size() < 2) { + set_errmsg("x-, y-, and z- grids for rectilinear meshes " + "must each have at least 2 points"); + return OPENMC_E_INVALID_ARGUMENT; + } + for (int i = 1; i < g.size(); ++i) { + if (g[i] <= g[i-1]) { + std::cout << g[i] << " " << g[i-1] << "\n"; + set_errmsg("Values in for x-, y-, and z- grids for " + "rectilinear meshes must be sorted and unique."); + return OPENMC_E_INVALID_ARGUMENT; + } + } + } + + m->lower_left_ = {m->grid_[0].front(), m->grid_[1].front(), m->grid_[2].front()}; + m->upper_right_ = {m->grid_[0].back(), m->grid_[1].back(), m->grid_[2].back()}; + + return 0; +} + #ifdef DAGMC UnstructuredMesh::UnstructuredMesh(pugi::xml_node node) : Mesh(node) @@ -1577,7 +1670,7 @@ UnstructuredMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, fmt::format("mesh {}", id_)); - write_dataset(mesh_group, "type", "unstructured"); + write_dataset(mesh_group, "type", type()); write_dataset(mesh_group, "filename", filename_); // write volume and centroid of each tet From 34275f4c726a1a5ad578fb5e50dccc86d31a00af Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 9 Feb 2021 12:41:41 -0500 Subject: [PATCH 02/68] Minor bug fixes --- openmc/lib/mesh.py | 2 +- src/cmfd_solver.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index 92684ec8d..90db09397 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -103,7 +103,7 @@ class Mesh(_FortranObjectWithID): _dll.openmc_mesh_set_id(self._index, mesh_id) -class RegularMesh(_FortranObjectWithID): +class RegularMesh(Mesh): """RegularMesh stored internally. This class exposes a mesh that is stored internally in the OpenMC diff --git a/src/cmfd_solver.cpp b/src/cmfd_solver.cpp index 98ba0f6b4..ab6283662 100644 --- a/src/cmfd_solver.cpp +++ b/src/cmfd_solver.cpp @@ -40,7 +40,7 @@ xt::xtensor indexmap; int use_all_threads; -RegularMesh* mesh; +StructuredMesh* mesh; std::vector egrid; @@ -222,7 +222,7 @@ void openmc_initialize_mesh_egrid(const int meshtally_id, const int* cmfd_indice openmc_mesh_filter_get_mesh(meshfilter_index, &mesh_index); // Get mesh from mesh index - cmfd::mesh = get_regular_mesh(mesh_index); + cmfd::mesh = dynamic_cast(model::meshes[mesh_index].get()); // Get energy bins from energy index, otherwise use default if (energy_index != -1) { From ee077b4d6168cc2dbfe6a1b2c51bccfb4805d522 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 9 Feb 2021 13:47:04 -0500 Subject: [PATCH 03/68] Define cmfd_src on all procs --- openmc/cmfd.py | 9 ++++++--- openmc/lib/mesh.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/openmc/cmfd.py b/openmc/cmfd.py index 91c264633..61bda9ed9 100644 --- a/openmc/cmfd.py +++ b/openmc/cmfd.py @@ -1090,6 +1090,12 @@ class CMFDRun: # Initialize parameters for CMFD tally windows self._set_tally_window() + # Extract spatial and energy indices + nx, ny, nz, ng = self._indices + + # Initialize CMFD source to all ones + self._cmfd_src = np.ones((nx, ny, nz, ng)) + # Define all variables that will exist only on master process if openmc.lib.master(): # Set global albedo @@ -1101,9 +1107,6 @@ class CMFDRun: # Set up CMFD coremap self._set_coremap() - # Extract spatial and energy indices - nx, ny, nz, ng = self._indices - # Allocate parameters that need to be stored for tally window self._openmc_src_rate = np.zeros((nx, ny, nz, ng, 0)) self._flux_rate = np.zeros((nx, ny, nz, ng, 0)) diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index 90db09397..79404c628 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -3,6 +3,7 @@ from ctypes import (c_int, c_int32, c_char_p, c_double, POINTER, create_string_buffer) from weakref import WeakValueDictionary +import numpy as np from numpy.ctypeslib import as_array from ..exceptions import AllocationError, InvalidIDError From b4e00b73248de25754ee71775096449c9184e557 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Tue, 9 Feb 2021 16:13:55 -0500 Subject: [PATCH 04/68] Add test cases --- tests/regression_tests/cmfd_feed/test.py | 25 + .../cmfd_feed_rectlin/__init__.py | 0 .../cmfd_feed_rectlin/geometry.xml | 43 ++ .../cmfd_feed_rectlin/materials.xml | 12 + .../cmfd_feed_rectlin/results_true.dat | 636 ++++++++++++++++++ .../cmfd_feed_rectlin/settings.xml | 26 + .../cmfd_feed_rectlin/tallies.xml | 21 + .../cmfd_feed_rectlin/test.py | 30 + tests/unit_tests/test_lib.py | 33 +- 9 files changed, 825 insertions(+), 1 deletion(-) create mode 100644 tests/regression_tests/cmfd_feed_rectlin/__init__.py create mode 100644 tests/regression_tests/cmfd_feed_rectlin/geometry.xml create mode 100644 tests/regression_tests/cmfd_feed_rectlin/materials.xml create mode 100644 tests/regression_tests/cmfd_feed_rectlin/results_true.dat create mode 100644 tests/regression_tests/cmfd_feed_rectlin/settings.xml create mode 100644 tests/regression_tests/cmfd_feed_rectlin/tallies.xml create mode 100644 tests/regression_tests/cmfd_feed_rectlin/test.py diff --git a/tests/regression_tests/cmfd_feed/test.py b/tests/regression_tests/cmfd_feed/test.py index 7723a47b1..fa22b0fe3 100644 --- a/tests/regression_tests/cmfd_feed/test.py +++ b/tests/regression_tests/cmfd_feed/test.py @@ -141,6 +141,31 @@ def test_cmfd_feed(): harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) harness.main() +def test_cmfd_feed_rectlin(): + """Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.mesh_type = 'rectilinear' + x_grid = np.linspace(-10, 10, 11) + y_grid = [-1., 1.] + z_grid = [-1., 1.] + cmfd_mesh.grid = [x_grid, y_grid, z_grid] + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + + # Initialize and run CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.solver_begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness.main() + def test_cmfd_multithread(): """Test 1 group CMFD solver with all available threads""" # Initialize and set CMFD mesh diff --git a/tests/regression_tests/cmfd_feed_rectlin/__init__.py b/tests/regression_tests/cmfd_feed_rectlin/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/cmfd_feed_rectlin/geometry.xml b/tests/regression_tests/cmfd_feed_rectlin/geometry.xml new file mode 100644 index 000000000..73ea679c4 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/geometry.xml @@ -0,0 +1,43 @@ + + + + + + 0 + -1 2 -3 4 -5 6 + 1 + + + + + x-plane + 10 + vacuum + + + x-plane + -10 + vacuum + + + y-plane + 1 + reflective + + + y-plane + -1 + reflective + + + z-plane + 1 + reflective + + + z-plane + -1 + reflective + + + diff --git a/tests/regression_tests/cmfd_feed_rectlin/materials.xml b/tests/regression_tests/cmfd_feed_rectlin/materials.xml new file mode 100644 index 000000000..70580e3a8 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/materials.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/regression_tests/cmfd_feed_rectlin/results_true.dat b/tests/regression_tests/cmfd_feed_rectlin/results_true.dat new file mode 100644 index 000000000..596e0880a --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/results_true.dat @@ -0,0 +1,636 @@ +k-combined: +1.157187E+00 1.636299E-02 +tally 1: +1.105119E+01 +1.225764E+01 +2.005562E+01 +4.044077E+01 +2.763651E+01 +7.659204E+01 +3.350385E+01 +1.124748E+02 +3.708973E+01 +1.378980E+02 +3.819687E+01 +1.461904E+02 +3.563815E+01 +1.272689E+02 +2.951618E+01 +8.720253E+01 +2.110567E+01 +4.462562E+01 +1.177125E+01 +1.391704E+01 +tally 2: +8.413458E+00 +3.600701E+00 +5.811221E+00 +1.720423E+00 +3.152237E+01 +4.997128E+01 +2.209153E+01 +2.455911E+01 +2.181469E+01 +2.395139E+01 +1.541498E+01 +1.196916E+01 +5.445894E+01 +1.490652E+02 +3.866629E+01 +7.523403E+01 +3.282600E+01 +5.400949E+01 +2.342806E+01 +2.753117E+01 +7.081101E+01 +2.514150E+02 +5.036626E+01 +1.272409E+02 +3.737870E+01 +7.016018E+01 +2.659096E+01 +3.553255E+01 +3.782640E+01 +7.182993E+01 +2.699064E+01 +3.660686E+01 +7.361578E+01 +2.717788E+02 +5.234377E+01 +1.374393E+02 +3.428621E+01 +5.895928E+01 +2.449124E+01 +3.009739E+01 +5.922017E+01 +1.756748E+02 +4.200257E+01 +8.838800E+01 +2.336382E+01 +2.740967E+01 +1.658498E+01 +1.382335E+01 +3.297049E+01 +5.456989E+01 +2.339783E+01 +2.749345E+01 +8.828373E+00 +3.943974E+00 +6.102413E+00 +1.883966E+00 +tally 3: +5.596852E+00 +1.598537E+00 +3.522058E-01 +6.713731E-03 +2.126148E+01 +2.274990E+01 +1.422059E+00 +1.023184E-01 +1.486366E+01 +1.113118E+01 +9.672994E-01 +4.841756E-02 +3.718151E+01 +6.959813E+01 +2.434965E+00 +2.992672E-01 +2.255899E+01 +2.552453E+01 +1.400193E+00 +9.974367E-02 +4.850275E+01 +1.180150E+02 +3.030061E+00 +4.650085E-01 +2.561202E+01 +3.297554E+01 +1.660808E+00 +1.399354E-01 +2.603551E+01 +3.406292E+01 +1.644980E+00 +1.378019E-01 +5.041383E+01 +1.274936E+02 +3.230209E+00 +5.274516E-01 +2.357275E+01 +2.788910E+01 +1.519553E+00 +1.172908E-01 +4.043162E+01 +8.190810E+01 +2.584619E+00 +3.376865E-01 +1.598070E+01 +1.283439E+01 +1.032328E+00 +5.432257E-02 +2.254607E+01 +2.553039E+01 +1.449211E+00 +1.061279E-01 +5.871759E+00 +1.744573E+00 +3.943129E-01 +8.103936E-03 +tally 4: +3.066217E+00 +4.730450E-01 +0.000000E+00 +0.000000E+00 +1.360953E+00 +9.533513E-02 +4.308283E+00 +9.334136E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +4.308283E+00 +9.334136E-01 +1.360953E+00 +9.533513E-02 +3.783652E+00 +7.238359E-01 +6.291955E+00 +1.990579E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +6.291955E+00 +1.990579E+00 +3.783652E+00 +7.238359E-01 +4.885573E+00 +1.200082E+00 +7.106830E+00 +2.535661E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.106830E+00 +2.535661E+00 +4.885573E+00 +1.200082E+00 +6.866283E+00 +2.371554E+00 +8.503237E+00 +3.630917E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.503237E+00 +3.630917E+00 +6.866283E+00 +2.371554E+00 +7.768902E+00 +3.027583E+00 +9.082457E+00 +4.135990E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.082457E+00 +4.135990E+00 +7.768902E+00 +3.027583E+00 +8.769969E+00 +3.865147E+00 +9.319943E+00 +4.358847E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.319943E+00 +4.358847E+00 +8.769969E+00 +3.865147E+00 +9.325939E+00 +4.366610E+00 +9.435739E+00 +4.466600E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.435739E+00 +4.466600E+00 +9.325939E+00 +4.366610E+00 +9.542810E+00 +4.573627E+00 +9.199847E+00 +4.247714E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +9.199847E+00 +4.247714E+00 +9.542810E+00 +4.573627E+00 +9.374166E+00 +4.404055E+00 +8.192575E+00 +3.370018E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +8.192575E+00 +3.370018E+00 +9.374166E+00 +4.404055E+00 +8.889272E+00 +3.956143E+00 +7.298067E+00 +2.671190E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +7.298067E+00 +2.671190E+00 +8.889272E+00 +3.956143E+00 +7.459052E+00 +2.787818E+00 +5.208762E+00 +1.362339E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +5.208762E+00 +1.362339E+00 +7.459052E+00 +2.787818E+00 +6.514105E+00 +2.127726E+00 +3.935834E+00 +7.791845E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +3.935834E+00 +7.791845E-01 +6.514105E+00 +2.127726E+00 +4.382888E+00 +9.637493E-01 +1.398986E+00 +9.904821E-02 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +1.398986E+00 +9.904821E-02 +4.382888E+00 +9.637493E-01 +3.080536E+00 +4.769864E-01 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +0.000000E+00 +tally 5: +5.593947E+00 +1.596885E+00 +8.106566E-01 +3.664721E-02 +2.125531E+01 +2.273647E+01 +3.065490E+00 +4.787394E-01 +1.486069E+01 +1.112690E+01 +1.949973E+00 +1.949146E-01 +3.717221E+01 +6.956205E+01 +5.001200E+00 +1.267587E+00 +2.255049E+01 +2.550506E+01 +2.899016E+00 +4.325940E-01 +4.849423E+01 +1.179738E+02 +6.528348E+00 +2.152826E+00 +2.561091E+01 +3.297274E+01 +3.488550E+00 +6.229290E-01 +2.603063E+01 +3.405034E+01 +3.601295E+00 +6.558219E-01 +5.040479E+01 +1.274480E+02 +6.786233E+00 +2.323774E+00 +2.357068E+01 +2.788373E+01 +2.889115E+00 +4.258034E-01 +4.042343E+01 +8.187501E+01 +5.332998E+00 +1.439867E+00 +1.597773E+01 +1.282957E+01 +1.942949E+00 +1.954364E-01 +2.254116E+01 +2.551921E+01 +3.104316E+00 +4.901972E-01 +5.869140E+00 +1.742897E+00 +9.010904E-01 +4.316080E-02 +cmfd indices +1.400000E+01 +1.000000E+00 +1.000000E+00 +1.000000E+00 +k cmfd +1.175888E+00 +1.173169E+00 +1.178686E+00 +1.169259E+00 +1.159624E+00 +1.161905E+00 +1.150366E+00 +1.156936E+00 +1.152230E+00 +1.158120E+00 +1.151832E+00 +1.156090E+00 +1.156076E+00 +1.159749E+00 +1.159483E+00 +1.159617E+00 +cmfd entropy +3.585755E+00 +3.588829E+00 +3.591601E+00 +3.596486E+00 +3.598552E+00 +3.600536E+00 +3.606024E+00 +3.602025E+00 +3.603625E+00 +3.601073E+00 +3.602144E+00 +3.601031E+00 +3.603146E+00 +3.603878E+00 +3.602530E+00 +3.603442E+00 +cmfd balance +5.69096E-03 +5.82028E-03 +5.54245E-03 +3.56776E-03 +2.86949E-03 +2.55946E-03 +2.27787E-03 +2.58567E-03 +1.98573E-03 +2.01270E-03 +2.15386E-03 +1.88029E-03 +1.65172E-03 +1.50711E-03 +1.28653E-03 +1.19687E-03 +cmfd dominance ratio +6.039E-01 +6.045E-01 +6.026E-01 +6.050E-01 +6.067E-01 +6.071E-01 +6.097E-01 +6.060E-01 +6.067E-01 +6.060E-01 +6.069E-01 +6.056E-01 +6.064E-01 +6.066E-01 +6.050E-01 +6.052E-01 +cmfd openmc source comparison +6.220816E-03 +6.181852E-03 +4.920765E-03 +5.006176E-03 +4.717371E-03 +5.049855E-03 +2.182449E-03 +3.301480E-03 +3.408666E-03 +2.012317E-03 +1.751563E-03 +1.934989E-03 +1.827288E-03 +1.055350E-03 +1.321006E-03 +1.220975E-03 +cmfd source +1.501950E-02 +6.088975E-02 +4.178034E-02 +1.057740E-01 +6.080482E-02 +1.314338E-01 +7.208400E-02 +7.138769E-02 +1.400772E-01 +6.569802E-02 +1.114779E-01 +4.436863E-02 +6.224041E-02 +1.696388E-02 diff --git a/tests/regression_tests/cmfd_feed_rectlin/settings.xml b/tests/regression_tests/cmfd_feed_rectlin/settings.xml new file mode 100644 index 000000000..24b0b6ab5 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/settings.xml @@ -0,0 +1,26 @@ + + + + + eigenvalue + 20 + 10 + 1000 + + + + + box + -10 -1 -1 10 1 1 + + + + + + 10 1 1 + -10.0 -1.0 -1.0 + 10.0 1.0 1.0 + + 10 + + diff --git a/tests/regression_tests/cmfd_feed_rectlin/tallies.xml b/tests/regression_tests/cmfd_feed_rectlin/tallies.xml new file mode 100644 index 000000000..c86971114 --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/tallies.xml @@ -0,0 +1,21 @@ + + + + + regular + -10 -1 -1 + 10 1 1 + 10 1 1 + + + + mesh + 1 + + + + 1 + flux + + + diff --git a/tests/regression_tests/cmfd_feed_rectlin/test.py b/tests/regression_tests/cmfd_feed_rectlin/test.py new file mode 100644 index 000000000..8739e800d --- /dev/null +++ b/tests/regression_tests/cmfd_feed_rectlin/test.py @@ -0,0 +1,30 @@ +from tests.testing_harness import CMFDTestHarness +from openmc import cmfd +import numpy as np +import scipy.sparse + + +def test_cmfd_feed_rectlin(): + """Test 1 group CMFD solver with CMFD feedback""" + # Initialize and set CMFD mesh + cmfd_mesh = cmfd.CMFDMesh() + cmfd_mesh.mesh_type = 'rectilinear' + x_grid = [-10., -9., -7., -6., -4., -3., -1., 0., 1., 3., 4., 6., 7., 9., 10.] + y_grid = [-1., 1.] + z_grid = [-1., 1.] + cmfd_mesh.grid = [x_grid, y_grid, z_grid] + cmfd_mesh.albedo = (0.0, 0.0, 1.0, 1.0, 1.0, 1.0) + + # Initialize and run CMFDRun object + cmfd_run = cmfd.CMFDRun() + cmfd_run.mesh = cmfd_mesh + cmfd_run.tally_begin = 5 + cmfd_run.solver_begin = 5 + cmfd_run.display = {'dominance': True} + cmfd_run.feedback = True + cmfd_run.gauss_seidel_tolerance = [1.e-15, 1.e-20] + cmfd_run.run() + + # Initialize and run CMFD test harness + harness = CMFDTestHarness('statepoint.20.h5', cmfd_run) + harness.main() diff --git a/tests/unit_tests/test_lib.py b/tests/unit_tests/test_lib.py index 5553f91e9..4a6d69255 100644 --- a/tests/unit_tests/test_lib.py +++ b/tests/unit_tests/test_lib.py @@ -442,7 +442,7 @@ def test_find_material(lib_init): assert mat is openmc.lib.materials[2] -def test_mesh(lib_init): +def test_regular_mesh(lib_init): mesh = openmc.lib.RegularMesh() mesh.dimension = (2, 3, 4) assert mesh.dimension == (2, 3, 4) @@ -477,6 +477,37 @@ def test_mesh(lib_init): assert msf.mesh == mesh +def test_rectilinear_mesh(lib_init): + mesh = openmc.lib.RectilinearMesh() + x_grid = [-10., 0., 10.] + y_grid = [0., 10., 20.] + z_grid = [10., 20., 30.] + mesh.set_grid(x_grid, y_grid, z_grid) + assert np.all(mesh.lower_left == (-10., 0., 10.)) + assert np.all(mesh.upper_right == (10., 20., 30.)) + assert np.all(mesh.dimension == (2, 2, 2)) + for i, diff_x in enumerate(np.diff(x_grid)): + for j, diff_y in enumerate(np.diff(y_grid)): + for k, diff_z in enumerate(np.diff(z_grid)): + assert np.all(mesh.width[i, j, k, :] == (10, 10, 10)) + + with pytest.raises(exc.AllocationError): + mesh2 = openmc.lib.RectilinearMesh(mesh.id) + + meshes = openmc.lib.meshes + assert isinstance(meshes, Mapping) + assert len(meshes) == 2 + + mesh = meshes[mesh.id] + assert isinstance(mesh, openmc.lib.RectilinearMesh) + + mf = openmc.lib.MeshFilter(mesh) + assert mf.mesh == mesh + + msf = openmc.lib.MeshSurfaceFilter(mesh) + assert msf.mesh == mesh + + def test_restart(lib_init, mpi_intracomm): # Finalize and re-init to make internal state consistent with XML. openmc.lib.hard_reset() From ecb92de244f4768e7698a94174b12d2d5a812cc2 Mon Sep 17 00:00:00 2001 From: John Tramm Date: Fri, 12 Feb 2021 22:47:24 +0000 Subject: [PATCH 05/68] Added fix for the relaxed memory ordering problem in the SharedArray object. --- include/openmc/shared_array.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/openmc/shared_array.h b/include/openmc/shared_array.h index 1fa744f83..bdce58fab 100644 --- a/include/openmc/shared_array.h +++ b/include/openmc/shared_array.h @@ -73,12 +73,12 @@ public: { // Atomically capture the index we want to write to int64_t idx; - #pragma omp atomic capture + #pragma omp atomic capture seq_cst idx = size_++; // Check that we haven't written off the end of the array if (idx >= capacity_) { - #pragma omp atomic write + #pragma omp atomic write seq_cst size_ = capacity_; return -1; } From b3b6f6fedbba91e988e39e3773f6421b6c9b18d2 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Fri, 12 Feb 2021 19:18:57 -0500 Subject: [PATCH 06/68] Use dynamic_cast to get type of sub-class instead of storing type as variable --- include/openmc/mesh.h | 10 ---------- src/mesh.cpp | 45 ++++++++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index b2e35819c..b2d919d30 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -46,8 +46,6 @@ public: Mesh(pugi::xml_node node); virtual ~Mesh() = default; - virtual std::string type() const = 0; - // Methods //! Determine which bins were crossed by a particle @@ -196,8 +194,6 @@ public: // Overriden methods - std::string type() const override {return "regular";} - void surface_bins_crossed(const Particle& p, std::vector& bins) const override; @@ -228,8 +224,6 @@ public: // Overriden methods - std::string type() const override {return "rectilinear";} - void surface_bins_crossed(const Particle& p, std::vector& bins) const override; @@ -256,8 +250,6 @@ public: UnstructuredMesh(pugi::xml_node); ~UnstructuredMesh() = default; - std::string type() const override {return "unstructured";} - void bins_crossed(const Particle& p, std::vector& bins, std::vector& lengths) const override; @@ -430,8 +422,6 @@ void read_meshes(pugi::xml_node root); //! \param[in] group HDF5 group void meshes_to_hdf5(hid_t group); -RegularMesh* get_regular_mesh(int32_t index); - void free_memory_mesh(); } // namespace openmc diff --git a/src/mesh.cpp b/src/mesh.cpp index 576ae3e77..a4b93e9af 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -865,7 +865,7 @@ void RegularMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - write_dataset(mesh_group, "type", type()); + write_dataset(mesh_group, "type", "regular"); write_dataset(mesh_group, "dimension", shape_); write_dataset(mesh_group, "lower_left", lower_left_); write_dataset(mesh_group, "upper_right", upper_right_); @@ -1122,7 +1122,7 @@ void RectilinearMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, "mesh " + std::to_string(id_)); - write_dataset(mesh_group, "type", type()); + write_dataset(mesh_group, "type", "rectilinear"); write_dataset(mesh_group, "x_grid", grid_[0]); write_dataset(mesh_group, "y_grid", grid_[1]); write_dataset(mesh_group, "z_grid", grid_[2]); @@ -1148,10 +1148,23 @@ int check_mesh_type(int32_t index, const std::string& mesh_compare_type) { if (int err = check_mesh(index)) return err; - StructuredMesh* mesh = dynamic_cast(model::meshes[index].get()); - auto mesh_type = mesh->type(); - if (mesh_compare_type != mesh_type) { - set_errmsg("This function is only valid for " + mesh_type + " meshes."); + + if (mesh_compare_type == "regular") { + RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); + if (!mesh) { + set_errmsg("This function is only valid for regular meshes."); + return OPENMC_E_INVALID_TYPE; + } + } + else if (mesh_compare_type == "rectilinear") { + RectilinearMesh* mesh = dynamic_cast(model::meshes[index].get()); + if (!mesh) { + set_errmsg("This function is only valid for rectilinear meshes."); + return OPENMC_E_INVALID_TYPE; + } + } + else { + set_errmsg("Mesh type " + mesh_compare_type + " is not supported."); return OPENMC_E_INVALID_TYPE; } return 0; @@ -1167,21 +1180,27 @@ openmc_mesh_get_type(int32_t index, char* type) { if (int err = check_mesh(index)) return err; - StructuredMesh* mesh = dynamic_cast(model::meshes[index].get()); - std::strcpy(type, mesh->type().c_str()); + RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); + if (mesh) { + std::strcpy(type, "regular"); + } + else { + RectilinearMesh* mesh = dynamic_cast(model::meshes[index].get()); + if (mesh) { + std::strcpy(type, "rectilinear"); + } + } return 0; } -RegularMesh* get_regular_mesh(int32_t index) { - return dynamic_cast(model::meshes[index].get()); -} - //! Extend the meshes array by n elements extern "C" int openmc_extend_meshes(int32_t n, const char* type, int32_t* index_start, int32_t* index_end) { if (index_start) *index_start = model::meshes.size(); + std::string mesh_type; + for (int i = 0; i < n; ++i) { if (std::strcmp(type, "regular") == 0) { @@ -1670,7 +1689,7 @@ UnstructuredMesh::to_hdf5(hid_t group) const { hid_t mesh_group = create_group(group, fmt::format("mesh {}", id_)); - write_dataset(mesh_group, "type", type()); + write_dataset(mesh_group, "type", "unstructured"); write_dataset(mesh_group, "filename", filename_); // write volume and centroid of each tet From a6144a4ab84fb0975c24ff4772ae92a8bad0434a Mon Sep 17 00:00:00 2001 From: AI-Pranto Date: Thu, 25 Feb 2021 10:46:57 +0600 Subject: [PATCH 07/68] Rename to max_surface_particles --- docs/source/io_formats/settings.rst | 2 +- include/openmc/settings.h | 2 +- openmc/settings.py | 18 +++++++++--------- src/relaxng/settings.rng | 4 ++-- src/settings.cpp | 6 +++--- src/simulation.cpp | 2 +- .../surface_source/inputs_true_write.dat | 2 +- tests/regression_tests/surface_source/test.py | 2 +- tests/unit_tests/test_settings.py | 4 ++-- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 53376b04c..70a05416b 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -751,7 +751,7 @@ This element has the following attributes/sub-elements: *Default*: None - :max_particles: + :max_surface_particles: An integer indicating the maximum number of particles to be banked on specified surfaces per processor. The size of source bank in ``surface_source.h5`` is limited to this value times the number of diff --git a/include/openmc/settings.h b/include/openmc/settings.h index 7030803e9..783c22126 100644 --- a/include/openmc/settings.h +++ b/include/openmc/settings.h @@ -88,7 +88,7 @@ extern RunMode run_mode; //!< Run mode (eigenvalue, fixed src, e extern std::unordered_set sourcepoint_batch; //!< Batches when source should be written extern std::unordered_set statepoint_batch; //!< Batches when state should be written extern std::unordered_set source_write_surf_id; //!< Surface ids where sources will be written -extern int64_t max_particles; //!< maximum number of particles to be banked on surfaces per process +extern int64_t max_surface_particles; //!< maximum number of particles to be banked on surfaces per process extern TemperatureMethod temperature_method; //!< method for choosing temperatures extern double temperature_tolerance; //!< Tolerance in [K] on choosing temperatures extern double temperature_default; //!< Default T in [K] diff --git a/openmc/settings.py b/openmc/settings.py index dc6a799f5..73722c9fe 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -152,8 +152,8 @@ class Settings: :surface_ids: List of surface ids at which crossing particles are to be banked (int) - :max_particles: Maximum number of particles to be banked on surfaces - per process (int) + :max_surface_particles: Maximum number of particles to be banked on + surfaces per process (int) survival_biasing : bool Indicate whether survival biasing is to be used tabular_legendre : dict @@ -604,14 +604,14 @@ class Settings: cv.check_type('surface source writing options', surf_source_write, Mapping) for key, value in surf_source_write.items(): cv.check_value('surface source writing key', key, - ('surface_ids', 'max_particles')) + ('surface_ids', 'max_surface_particles')) if key == 'surface_ids': cv.check_type('surface ids for source banking', value, Iterable, Integral) for surf_id in value: cv.check_greater_than('surface id for source banking', surf_id, 0) - elif key == 'max_particles': + elif key == 'max_surface_particles': cv.check_type('maximum particle banks on surfaces per process', value, Integral) cv.check_greater_than('maximum particle banks on surfaces per process', @@ -954,9 +954,9 @@ class Settings: subelement = ET.SubElement(element, "surface_ids") subelement.text = ' '.join( str(x) for x in self._surf_source_write['surface_ids']) - if 'max_particles' in self._surf_source_write: - subelement = ET.SubElement(element, "max_particles") - subelement.text = str(self._surf_source_write['max_particles']) + if 'max_surface_particles' in self._surf_source_write: + subelement = ET.SubElement(element, "max_surface_particles") + subelement.text = str(self._surf_source_write['max_surface_particles']) def _create_confidence_intervals(self, root): if self._confidence_intervals is not None: @@ -1230,12 +1230,12 @@ class Settings: def _surf_source_write_from_xml_element(self, root): elem = root.find('surf_source_write') if elem is not None: - for key in ('surface_ids', 'max_particles'): + for key in ('surface_ids', 'max_surface_particles'): value = get_text(elem, key) if value is not None: if key == 'surface_ids': value = [int(x) for x in value.split()] - elif key in ('max_particles'): + elif key in ('max_surface_particles'): value = int(value) self.surf_source_write[key] = value diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng index 07b8a6d1d..57b96aac9 100644 --- a/src/relaxng/settings.rng +++ b/src/relaxng/settings.rng @@ -670,10 +670,10 @@ - + - + diff --git a/src/settings.cpp b/src/settings.cpp index 9df3b8c46..fe7fb9fb6 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -101,7 +101,7 @@ RunMode run_mode {RunMode::UNSET}; std::unordered_set sourcepoint_batch; std::unordered_set statepoint_batch; std::unordered_set source_write_surf_id; -int64_t max_particles; +int64_t max_surface_particles; TemperatureMethod temperature_method {TemperatureMethod::NEAREST}; double temperature_tolerance {10.0}; double temperature_default {293.6}; @@ -655,8 +655,8 @@ void read_settings_xml() } // Get maximum number of particles to be banked per surface - if (check_for_node(node_ssw, "max_particles")) { - max_particles = std::stoi(get_node_value(node_ssw, "max_particles")); + if (check_for_node(node_ssw, "max_surface_particles")) { + max_surface_particles = std::stoi(get_node_value(node_ssw, "max_surface_particles")); } } diff --git a/src/simulation.cpp b/src/simulation.cpp index b4795f2e7..21630ad25 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -289,7 +289,7 @@ void allocate_banks() if (settings::surf_source_write) { // Allocate surface source bank - simulation::surf_source_bank.reserve(settings::max_particles); + simulation::surf_source_bank.reserve(settings::max_surface_particles); } } diff --git a/tests/regression_tests/surface_source/inputs_true_write.dat b/tests/regression_tests/surface_source/inputs_true_write.dat index 48dde9900..4718187b4 100644 --- a/tests/regression_tests/surface_source/inputs_true_write.dat +++ b/tests/regression_tests/surface_source/inputs_true_write.dat @@ -24,7 +24,7 @@ 1 - 1000 + 1000 1 diff --git a/tests/regression_tests/surface_source/test.py b/tests/regression_tests/surface_source/test.py index 492ef192e..864693039 100644 --- a/tests/regression_tests/surface_source/test.py +++ b/tests/regression_tests/surface_source/test.py @@ -47,7 +47,7 @@ def model(request): openmc_model.settings.source = pt_src openmc_model.settings.surf_source_write = {'surface_ids': [1], - 'max_particles': 1000} + 'max_surface_particles': 1000} elif surf_source_op == 'read': openmc_model.settings.surf_source_read = {'path': 'surface_source_true.h5'} diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 7a3357d43..34f3d3086 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -21,7 +21,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.surf_source_read = {'path': 'surface_source_1.h5'} - s.surf_source_write = {'surface_ids': [2], 'max_particles': 200} + s.surf_source_write = {'surface_ids': [2], 'max_surface_particles': 200} s.confidence_intervals = True s.ptables = True s.seed = 17 @@ -79,7 +79,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} assert s.surf_source_read == {'path': 'surface_source_1.h5'} - assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} + assert s.surf_source_write == {'surface_ids': [2], 'max_surface_particles': 200} assert s.confidence_intervals assert s.ptables assert s.seed == 17 From fff8e5d1bdefbad58d36f9934b3975e87a2aa693 Mon Sep 17 00:00:00 2001 From: AI-Pranto Date: Thu, 25 Feb 2021 10:51:03 +0600 Subject: [PATCH 08/68] Remove constrain for 'max_surface_particles' value --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index fe7fb9fb6..f5ee10d5c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -656,7 +656,7 @@ void read_settings_xml() // Get maximum number of particles to be banked per surface if (check_for_node(node_ssw, "max_surface_particles")) { - max_surface_particles = std::stoi(get_node_value(node_ssw, "max_surface_particles")); + max_surface_particles = std::stoll(get_node_value(node_ssw, "max_surface_particles")); } } From e54785f1a7aa8aff1d652d3caef36f8aa04cd91f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 26 Feb 2021 14:42:02 -0600 Subject: [PATCH 09/68] Add (currently failing) test with URR (n,gamma) reaction rate check --- tests/unit_tests/test_urr_capture.py | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/unit_tests/test_urr_capture.py diff --git a/tests/unit_tests/test_urr_capture.py b/tests/unit_tests/test_urr_capture.py new file mode 100644 index 000000000..745d62cef --- /dev/null +++ b/tests/unit_tests/test_urr_capture.py @@ -0,0 +1,42 @@ +import openmc +import pytest + + +@pytest.fixture +def th232_model(): + # URR boundaries for Th232 + e_min, e_max = 4000.0, 100000.0 + + model = openmc.model.Model() + th232 = openmc.Material() + th232.add_nuclide('Th232', 1.0) + + surf = openmc.Sphere(r=100.0, boundary_type='reflective') + cell = openmc.Cell(fill=th232, region=-surf) + model.geometry = openmc.Geometry([cell]) + + model.settings.particles = 100 + model.settings.batches = 10 + model.settings.run_mode = 'fixed source' + energies = openmc.stats.Uniform(e_min, e_max) + model.settings.source = openmc.Source(energy=energies) + + tally = openmc.Tally(name='rates') + tally.filters = [openmc.EnergyFilter([e_min, e_max])] + tally.scores = ['(n,gamma)', 'absorption', 'fission'] + model.tallies.append(tally) + return model + + +def test_urr_capture(run_in_tmpdir, th232_model): + # Export and run model + th232_model.export_to_xml() + openmc.run() + + # Get reaction rates from tally + with openmc.StatePoint('statepoint.10.h5') as sp: + t = sp.get_tally(name='rates') + ngamma, absorption, fission = t.mean.flatten() + + # In URR, the (n,gamma) rate should be equal to absorption - fission + assert ngamma == pytest.approx(absorption - fission) From 3987d4273aff666dca769dfafca66dee28cf23f6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 26 Feb 2021 14:44:35 -0600 Subject: [PATCH 10/68] Fix (n,gamma) reaction rate in URR to use sampled xs --- src/nuclide.cpp | 9 ++++++--- src/tallies/tally_scoring.cpp | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index b0d343974..faa2ae25a 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -896,18 +896,21 @@ void Nuclide::calculate_urr_xs(int i_temp, Particle& p) const if (fission < 0.) {fission = 0.;} if (capture < 0.) {capture = 0.;} - // Set elastic, absorption, fission, and total x/s. Note that the total x/s - // is calculated as a sum of partials instead of the table-provided value + // Set elastic, absorption, fission, total, and capture x/s. Note that the + // total x/s is calculated as a sum of partials instead of the table-provided + // value micro.elastic = elastic; micro.absorption = capture + fission; micro.fission = fission; micro.total = elastic + inelastic + capture + fission; + if (simulation::need_depletion_rx) { + micro.reaction[0] = capture; + } // Determine nu-fission cross-section if (fissionable_) { micro.nu_fission = nu(p.E_, EmissionMode::total) * micro.fission; } - } std::pair Nuclide::find_temperature(double T) const diff --git a/src/tallies/tally_scoring.cpp b/src/tallies/tally_scoring.cpp index e936c10d9..91f10f559 100644 --- a/src/tallies/tally_scoring.cpp +++ b/src/tallies/tally_scoring.cpp @@ -479,6 +479,13 @@ double get_nuclide_xs(const Particle& p, int i_nuclide, int score_bin) { const auto& rxn {*nuc.reactions_[m]}; const auto& micro {p.neutron_xs_[i_nuclide]}; + // In the URR, the (n,gamma) cross section is sampled randomly from + // probability tables. Make sure we use the sampled value (which is equal to + // absorption - fission) rather than the dilute average value + if (micro.use_ptable && score_bin == N_GAMMA) { + return micro.absorption - micro.fission; + } + auto i_temp = micro.index_temp; if (i_temp >= 0) { // Can be false due to multipole // Get index on energy grid and interpolation factor From 366c4c79290645d89b796a3fc967b7533a42786e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 26 Feb 2021 16:14:11 -0600 Subject: [PATCH 11/68] Update test results --- .../deplete/last_step_reference_materials.xml | 1296 ++++++++--------- .../deplete/test_reference.h5 | Bin 165832 -> 166224 bytes .../filter_energyfun/results_true.dat | 2 +- .../mgxs_library_no_nuclides/results_true.dat | 4 +- .../mgxs_library_nuclides/results_true.dat | 2 +- .../multipole/results_true.dat | 8 +- .../regression_tests/tallies/results_true.dat | 2 +- tests/unit_tests/test_deplete_resultslist.py | 10 +- 8 files changed, 662 insertions(+), 662 deletions(-) diff --git a/tests/regression_tests/deplete/last_step_reference_materials.xml b/tests/regression_tests/deplete/last_step_reference_materials.xml index 2021b7c1b..10815c9a2 100644 --- a/tests/regression_tests/deplete/last_step_reference_materials.xml +++ b/tests/regression_tests/deplete/last_step_reference_materials.xml @@ -4,1001 +4,1001 @@ - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - - - - - + + + + + + + + + diff --git a/tests/regression_tests/deplete/test_reference.h5 b/tests/regression_tests/deplete/test_reference.h5 index 922f60e5d8b81f5823f232e624afc75174ee3586..7331f405961c893066bc73586454cbe2692049e4 100644 GIT binary patch literal 166224 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 D+7gab literal 165832 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 A-2eap diff --git a/tests/regression_tests/filter_energyfun/results_true.dat b/tests/regression_tests/filter_energyfun/results_true.dat index a88cff496..f6034ec10 100644 --- a/tests/regression_tests/filter_energyfun/results_true.dat +++ b/tests/regression_tests/filter_energyfun/results_true.dat @@ -1,2 +1,2 @@ energyfunction nuclide score mean std. dev. -0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.74e-01 3.55e-03 +0 d2effa26cb3cf2 Am241 ((n,gamma) / (n,gamma)) 1.74e-01 3.52e-03 diff --git a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat index 398c3e783..dd508d0f4 100644 --- a/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_no_nuclides/results_true.dat @@ -184,7 +184,7 @@ nu-diffusion-coefficient 0 1 2 total 0.000000 0.000000 (n,gamma) material group in nuclide mean std. dev. -1 1 1 total 0.019727 0.002262 +1 1 1 total 0.019720 0.002260 0 1 2 total 0.071719 0.006262 (n,a) material group in nuclide mean std. dev. @@ -490,7 +490,7 @@ nu-diffusion-coefficient 0 2 2 total 0.000000 0.000000 (n,gamma) material group in nuclide mean std. dev. -1 2 1 total 0.001569 0.000322 +1 2 1 total 0.001571 0.000323 0 2 2 total 0.005400 0.000618 (n,a) material group in nuclide mean std. dev. diff --git a/tests/regression_tests/mgxs_library_nuclides/results_true.dat b/tests/regression_tests/mgxs_library_nuclides/results_true.dat index a2be43e80..ebe4e856e 100644 --- a/tests/regression_tests/mgxs_library_nuclides/results_true.dat +++ b/tests/regression_tests/mgxs_library_nuclides/results_true.dat @@ -1 +1 @@ -a9999488e2aa2ad0f1d694afedb71fbb56f1d0c8e8abdb6616698505a9d85302bf90586866fdb58eac96f76712a2dfbc884cc03087df7b5d148fb29d14dd2bbb \ No newline at end of file +54a6351f3444c5ea85f972ed05f48c81da2de27a6295a660c321e7c595ec3865e5882fb7fa1f2e4a3bfd616e4b637482e2a16930dd59c3968d04c59d4b1c08b1 \ No newline at end of file diff --git a/tests/regression_tests/multipole/results_true.dat b/tests/regression_tests/multipole/results_true.dat index 2273c03fd..f81ab0a47 100644 --- a/tests/regression_tests/multipole/results_true.dat +++ b/tests/regression_tests/multipole/results_true.dat @@ -5,8 +5,8 @@ tally 1: 2.951721E+00 2.785273E+00 1.554128E+00 -5.349716E-01 -5.732174E-02 +5.352493E-01 +5.738169E-02 4.499834E-01 4.055011E-02 0.000000E+00 @@ -25,8 +25,8 @@ tally 1: 2.628739E+04 2.785273E+00 1.554128E+00 -2.176478E+00 -9.480405E-01 +2.176755E+00 +9.482820E-01 3.574110E+02 2.555993E+04 1.146136E-04 diff --git a/tests/regression_tests/tallies/results_true.dat b/tests/regression_tests/tallies/results_true.dat index 6c56ab67a..5d9d99227 100644 --- a/tests/regression_tests/tallies/results_true.dat +++ b/tests/regression_tests/tallies/results_true.dat @@ -1 +1 @@ -ba4369130ac65812939cb417c7088ae6c8cdec118fd7dce5645b40a20a3ae47072cd72c24aa091ccc09f833a5bfc0bd72407a0dcde7d3120e39379942614a75b \ No newline at end of file +0f036e2c34f3e9cf350d4f6bac6b0c5933d77d0665a5d461a3911c29d8f1d49b74100e680920da286c63538fabb86cbcd17532c6aad6048ee78e6cef75dd76ae \ No newline at end of file diff --git a/tests/unit_tests/test_deplete_resultslist.py b/tests/unit_tests/test_deplete_resultslist.py index 392d5d0d5..be81da137 100644 --- a/tests/unit_tests/test_deplete_resultslist.py +++ b/tests/unit_tests/test_deplete_resultslist.py @@ -22,7 +22,7 @@ def test_get_atoms(res): t_ref = np.array([0.0, 1296000.0, 2592000.0, 3888000.0]) n_ref = np.array( - [6.67473282e+08, 3.76986925e+14, 3.68587383e+14, 3.91338675e+14]) + [6.67473282e+08, 3.76987065e+14, 3.68586723e+14, 3.91338392e+14]) np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(n, n_ref) @@ -48,8 +48,8 @@ def test_get_reaction_rate(res): t, r = res.get_reaction_rate("1", "Xe135", "(n,gamma)") t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - n_ref = [6.67473282e+08, 3.76986925e+14, 3.68587383e+14, 3.91338675e+14] - xs_ref = [3.32282266e-05, 2.76207120e-05, 4.10986677e-05, 3.72453665e-05] + n_ref = [6.67473282e+08, 3.76987065e+14, 3.68586723e+14, 3.91338392e+14] + xs_ref = [3.32282064e-05, 2.76208092e-05, 4.10987995e-05, 3.72454755e-05] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(r, np.array(n_ref) * xs_ref) @@ -60,8 +60,8 @@ def test_get_eigenvalue(res): t, k = res.get_eigenvalue() t_ref = [0.0, 1296000.0, 2592000.0, 3888000.0] - k_ref = [1.16984322, 1.19097427, 1.03012572, 1.20045627] - u_ref = [0.0375587, 0.0347639, 0.07216021, 0.02839642] + k_ref = [1.16984322, 1.19097429, 1.03012517, 1.20045563] + u_ref = [0.0375587, 0.03476389, 0.07215969, 0.02839639] np.testing.assert_allclose(t, t_ref) np.testing.assert_allclose(k[:, 0], k_ref) From 6fdc7657c7f1a618be6649355381a7b4a30fa1b6 Mon Sep 17 00:00:00 2001 From: AI-Pranto Date: Mon, 1 Mar 2021 21:45:42 +0600 Subject: [PATCH 12/68] reflect change only on source side --- openmc/settings.py | 10 +++++----- tests/regression_tests/surface_source/test.py | 2 +- tests/unit_tests/test_settings.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/openmc/settings.py b/openmc/settings.py index 73722c9fe..57155b0df 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -152,7 +152,7 @@ class Settings: :surface_ids: List of surface ids at which crossing particles are to be banked (int) - :max_surface_particles: Maximum number of particles to be banked on + :max_particles: Maximum number of particles to be banked on surfaces per process (int) survival_biasing : bool Indicate whether survival biasing is to be used @@ -604,14 +604,14 @@ class Settings: cv.check_type('surface source writing options', surf_source_write, Mapping) for key, value in surf_source_write.items(): cv.check_value('surface source writing key', key, - ('surface_ids', 'max_surface_particles')) + ('surface_ids', 'max_particles')) if key == 'surface_ids': cv.check_type('surface ids for source banking', value, Iterable, Integral) for surf_id in value: cv.check_greater_than('surface id for source banking', surf_id, 0) - elif key == 'max_surface_particles': + elif key == 'max_particles': cv.check_type('maximum particle banks on surfaces per process', value, Integral) cv.check_greater_than('maximum particle banks on surfaces per process', @@ -954,9 +954,9 @@ class Settings: subelement = ET.SubElement(element, "surface_ids") subelement.text = ' '.join( str(x) for x in self._surf_source_write['surface_ids']) - if 'max_surface_particles' in self._surf_source_write: + if 'max_particles' in self._surf_source_write: subelement = ET.SubElement(element, "max_surface_particles") - subelement.text = str(self._surf_source_write['max_surface_particles']) + subelement.text = str(self._surf_source_write['max_particles']) def _create_confidence_intervals(self, root): if self._confidence_intervals is not None: diff --git a/tests/regression_tests/surface_source/test.py b/tests/regression_tests/surface_source/test.py index 864693039..492ef192e 100644 --- a/tests/regression_tests/surface_source/test.py +++ b/tests/regression_tests/surface_source/test.py @@ -47,7 +47,7 @@ def model(request): openmc_model.settings.source = pt_src openmc_model.settings.surf_source_write = {'surface_ids': [1], - 'max_surface_particles': 1000} + 'max_particles': 1000} elif surf_source_op == 'read': openmc_model.settings.surf_source_read = {'path': 'surface_source_true.h5'} diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 34f3d3086..7a3357d43 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -21,7 +21,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} s.statepoint = {'batches': [50, 150, 500, 1000]} s.surf_source_read = {'path': 'surface_source_1.h5'} - s.surf_source_write = {'surface_ids': [2], 'max_surface_particles': 200} + s.surf_source_write = {'surface_ids': [2], 'max_particles': 200} s.confidence_intervals = True s.ptables = True s.seed = 17 @@ -79,7 +79,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} assert s.surf_source_read == {'path': 'surface_source_1.h5'} - assert s.surf_source_write == {'surface_ids': [2], 'max_surface_particles': 200} + assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} assert s.confidence_intervals assert s.ptables assert s.seed == 17 From 736c5c61fe9101acf42ab856143b00e91c31d034 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 1 Mar 2021 09:50:29 -0600 Subject: [PATCH 13/68] Change how temperatures are selected when temperature_range is given --- src/nuclide.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index b0d343974..7c1396e0f 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -90,9 +90,17 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature) double T_min = n > 0 ? settings::temperature_range[0] : 0.0; double T_max = n > 0 ? settings::temperature_range[1] : INFTY; if (T_max > 0.0) { - for (auto T : temps_available) { - if (T_min <= T && T <= T_max) { - temps_to_read.push_back(std::round(T)); + for (int j = 0; j < temps_available.size() - 1; ++j) { + if ((T_min <= temps_available[j] && temps_available[j] < T_max) || + (T_min <= temps_available[j+1] && temps_available[j+1] < T_max)) { + int T_j = std::round(temps_available[j]); + int T_j1 = std::round(temps_available[j+1]); + if (!contains(temps_to_read, T_j)) { + temps_to_read.push_back(T_j); + } + if (!contains(temps_to_read, T_j1)) { + temps_to_read.push_back(T_j1); + } } } } From 903ed39e332aa0cd8da614003eaad7ef0603deed Mon Sep 17 00:00:00 2001 From: AI-Pranto Date: Mon, 1 Mar 2021 22:53:59 +0600 Subject: [PATCH 14/68] small fix --- tests/unit_tests/test_settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 7a3357d43..429e2824d 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -79,7 +79,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} assert s.surf_source_read == {'path': 'surface_source_1.h5'} - assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} + assert s.surf_source_write == {'surface_ids': [2], 'max_surface_particles': 200} assert s.confidence_intervals assert s.ptables assert s.seed == 17 From 1bad13d5f6e5b7877050183e28ba8c74adba98c9 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 2 Mar 2021 09:40:13 -0600 Subject: [PATCH 15/68] Make sure one-temperature case is handled correctly for temperature range --- src/nuclide.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 7c1396e0f..47f380aac 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -90,14 +90,18 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature) double T_min = n > 0 ? settings::temperature_range[0] : 0.0; double T_max = n > 0 ? settings::temperature_range[1] : INFTY; if (T_max > 0.0) { - for (int j = 0; j < temps_available.size() - 1; ++j) { - if ((T_min <= temps_available[j] && temps_available[j] < T_max) || - (T_min <= temps_available[j+1] && temps_available[j+1] < T_max)) { + // For each interval (T_j, T_j+1), if either T_j or T_j+1 are within the + // temperature range, load data for *both* T_j and T_j+1 + int n = temps_available.size(); + for (int j = 0; j < n; ++j) { + if ((T_min <= temps_available[j] && temps_available[j] < T_max)) { int T_j = std::round(temps_available[j]); - int T_j1 = std::round(temps_available[j+1]); if (!contains(temps_to_read, T_j)) { temps_to_read.push_back(T_j); } + } + if (j < n - 1 && T_min <= temps_available[j+1] && temps_available[j+1] < T_max) { + int T_j1 = std::round(temps_available[j+1]); if (!contains(temps_to_read, T_j1)) { temps_to_read.push_back(T_j1); } From 8c289fc5bdb4f49a66a18db334ebe0baa56a4617 Mon Sep 17 00:00:00 2001 From: AI-Pranto Date: Wed, 3 Mar 2021 00:42:39 +0600 Subject: [PATCH 16/68] cleanup --- docs/source/io_formats/settings.rst | 2 +- openmc/settings.py | 6 +++--- src/relaxng/settings.rng | 4 ++-- src/settings.cpp | 4 ++-- tests/regression_tests/surface_source/inputs_true_write.dat | 2 +- tests/unit_tests/test_settings.py | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/source/io_formats/settings.rst b/docs/source/io_formats/settings.rst index 70a05416b..53376b04c 100644 --- a/docs/source/io_formats/settings.rst +++ b/docs/source/io_formats/settings.rst @@ -751,7 +751,7 @@ This element has the following attributes/sub-elements: *Default*: None - :max_surface_particles: + :max_particles: An integer indicating the maximum number of particles to be banked on specified surfaces per processor. The size of source bank in ``surface_source.h5`` is limited to this value times the number of diff --git a/openmc/settings.py b/openmc/settings.py index 57155b0df..615e6365a 100644 --- a/openmc/settings.py +++ b/openmc/settings.py @@ -955,7 +955,7 @@ class Settings: subelement.text = ' '.join( str(x) for x in self._surf_source_write['surface_ids']) if 'max_particles' in self._surf_source_write: - subelement = ET.SubElement(element, "max_surface_particles") + subelement = ET.SubElement(element, "max_particles") subelement.text = str(self._surf_source_write['max_particles']) def _create_confidence_intervals(self, root): @@ -1230,12 +1230,12 @@ class Settings: def _surf_source_write_from_xml_element(self, root): elem = root.find('surf_source_write') if elem is not None: - for key in ('surface_ids', 'max_surface_particles'): + for key in ('surface_ids', 'max_particles'): value = get_text(elem, key) if value is not None: if key == 'surface_ids': value = [int(x) for x in value.split()] - elif key in ('max_surface_particles'): + elif key in ('max_particles'): value = int(value) self.surf_source_write[key] = value diff --git a/src/relaxng/settings.rng b/src/relaxng/settings.rng index 57b96aac9..07b8a6d1d 100644 --- a/src/relaxng/settings.rng +++ b/src/relaxng/settings.rng @@ -670,10 +670,10 @@ - + - + diff --git a/src/settings.cpp b/src/settings.cpp index f5ee10d5c..41b18942c 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -655,8 +655,8 @@ void read_settings_xml() } // Get maximum number of particles to be banked per surface - if (check_for_node(node_ssw, "max_surface_particles")) { - max_surface_particles = std::stoll(get_node_value(node_ssw, "max_surface_particles")); + if (check_for_node(node_ssw, "max_particles")) { + max_surface_particles = std::stoll(get_node_value(node_ssw, "max_particles")); } } diff --git a/tests/regression_tests/surface_source/inputs_true_write.dat b/tests/regression_tests/surface_source/inputs_true_write.dat index 4718187b4..48dde9900 100644 --- a/tests/regression_tests/surface_source/inputs_true_write.dat +++ b/tests/regression_tests/surface_source/inputs_true_write.dat @@ -24,7 +24,7 @@ 1 - 1000 + 1000 1 diff --git a/tests/unit_tests/test_settings.py b/tests/unit_tests/test_settings.py index 429e2824d..7a3357d43 100644 --- a/tests/unit_tests/test_settings.py +++ b/tests/unit_tests/test_settings.py @@ -79,7 +79,7 @@ def test_export_to_xml(run_in_tmpdir): 'write': True, 'overwrite': True} assert s.statepoint == {'batches': [50, 150, 500, 1000]} assert s.surf_source_read == {'path': 'surface_source_1.h5'} - assert s.surf_source_write == {'surface_ids': [2], 'max_surface_particles': 200} + assert s.surf_source_write == {'surface_ids': [2], 'max_particles': 200} assert s.confidence_intervals assert s.ptables assert s.seed == 17 From 3614246d01abc79e1b45e459c4a3c72dfe2014c6 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Wed, 3 Mar 2021 19:01:41 +0000 Subject: [PATCH 17/68] added VITAMIN-J-42 to group structures --- openmc/mgxs/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index 1b1ad0dce..01007031e 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -20,6 +20,7 @@ GROUP_STRUCTURES = {} .. _XMAS-172: https://www-nds.iaea.org/wimsd/energy.htm .. _SHEM-361: https://www.polymtl.ca/merlin/downloads/FP214.pdf .. _activation: https://fispact.ukaea.uk/wiki/Keyword:GETXS +.. _VITAMIN-J-42: https://www.oecd-nea.org/dbdata/nds_jefreports/jefreport-10.pdf .. _CCFE-709: https://fispact.ukaea.uk/wiki/CCFE-709_group_structure .. _UKAEA-1102: https://fispact.ukaea.uk/wiki/UKAEA-1102_group_structure .. [SAR1990] Sartori, E., OECD/NEA Data Bank: Standard Energy Group Structures @@ -61,6 +62,12 @@ GROUP_STRUCTURES['CASMO-40'] = np.array([ 9.72e-1, 1.02, 1.097, 1.15, 1.3, 1.5, 1.855, 2.1, 2.6, 3.3, 4., 9.877, 1.5968e1, 2.77e1, 4.8052e1, 1.4873e2, 5.53e3, 9.118e3, 1.11e5, 5.e5, 8.21e5, 1.353e6, 2.231e6, 3.679e6, 6.0655e6, 2.e7]) +GROUP_STRUCTURES['VITAMIN-J-42'] = np.array([ + 1.e3, 10.e3, 20.e3, 30.e3, 45.e3, 60.e3, 70.e3, 75.e3, 0.1e6, 0.15e6, + 0.2e6, 0.3e6, 0.4e6, 0.45e6, 0.51e6, 0.512e6, 0.6e6, 0.7e6, 0.8e6, 1.e6, + 1.33e6, 1.34e6, 1.5e6, 1.66e6, 2.e6, 2.5e6, 3.e6, 3.5e6, 4.e6, 4.5e6, 5.e6, + 5.5e6, 6.e6, 6.5e6, 7.e6, 7.5e6, 8.e6, 10.e6, 12.e6, 14.e6, 20.e6, 30.e6, + 50.e6]) GROUP_STRUCTURES['CASMO-70'] = np.array([ 0., 5.e-3, 1.e-2, 1.5e-2, 2.e-2, 2.5e-2, 3.e-2, 3.5e-2, 4.2e-2, 5.e-2, 5.8e-2, 6.7e-2, 8.e-2, 1.e-1, 1.4e-1, 1.8e-1, 2.2e-1, From 9711db7e2ef8d734f73d09d7a481a98d5a702913 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 4 Mar 2021 10:26:26 +0000 Subject: [PATCH 18/68] added vit-j-42 to docstring --- openmc/mgxs/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openmc/mgxs/__init__.py b/openmc/mgxs/__init__.py index 01007031e..00b644050 100644 --- a/openmc/mgxs/__init__.py +++ b/openmc/mgxs/__init__.py @@ -13,8 +13,8 @@ GROUP_STRUCTURES = {} - "XMAS-172_" designed for LWR analysis ([SAR1990]_, [SAN2004]_) - "SHEM-361_" designed for LWR analysis to eliminate self-shielding calculations of thermal resonances ([HFA2005]_, [SAN2007]_, [HEB2008]_) -- activation_ energy group structures "VITAMIN-J-175", "TRIPOLI-315", - "CCFE-709_" and "UKAEA-1102_" +- activation_ energy group structures "VITAMIN-J-42", "VITAMIN-J-175", + "TRIPOLI-315", "CCFE-709_" and "UKAEA-1102_" .. _CASMO: https://www.studsvik.com/SharepointFiles/CASMO-5%20Development%20and%20Applications.pdf .. _XMAS-172: https://www-nds.iaea.org/wimsd/energy.htm From 354c29b302f54c04e4996298abc9854ac22cb8b2 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 4 Mar 2021 15:42:15 -0500 Subject: [PATCH 19/68] add warnings about untrusted OpenMC models --- docs/source/usersguide/basics.rst | 8 ++++++-- docs/source/usersguide/scripts.rst | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/source/usersguide/basics.rst b/docs/source/usersguide/basics.rst index dbf63136c..250255f45 100644 --- a/docs/source/usersguide/basics.rst +++ b/docs/source/usersguide/basics.rst @@ -5,13 +5,13 @@ Basics of Using OpenMC ====================== ---------------- -Creating a Model +Running a Model ---------------- When you build and install OpenMC, you will have an :ref:`scripts_openmc` executable on your system. When you run ``openmc``, the first thing it will do is look for a set of XML_ files that describe the model you want to -simulation. Three of these files are required and another three are optional, as +simulate. Three of these files are required and another three are optional, as described below. .. admonition:: Required @@ -43,6 +43,10 @@ described below. This file gives specifications for producing slice or voxel plots of the geometry. +.. warning:: + + OpenMC models should be treated as code, and it is important to be careful with code from untrusted sources. + eXtensible Markup Language (XML) -------------------------------- diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index c361ebe43..56dfe0d9f 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -13,7 +13,13 @@ Executables and Scripts Once you have a model built (see :ref:`usersguide_basics`), you can either run the openmc executable directly from the directory containing your XML input files, or you can specify as a command-line argument the directory containing -the XML input files. For example, if your XML input files are in the directory +the XML input files. + +.. warning:: + + OpenMC models should be treated as code, and it is important to be careful with code from untrusted sources. + +For example, if your XML input files are in the directory ``/home/username/somemodel/``, one way to run the simulation would be: .. code-block:: sh From 1fa2c0e8893f993f44f78a3e0abdc5e8a54f8a3d Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 4 Mar 2021 17:56:34 -0500 Subject: [PATCH 20/68] separate find_cell into two functions --- include/openmc/geometry.h | 7 ++--- include/openmc/plot.h | 2 +- src/geometry.cpp | 63 +++++++++++++++++---------------------- src/particle.cpp | 13 ++++---- src/volume_calc.cpp | 3 +- 5 files changed, 40 insertions(+), 48 deletions(-) diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 0ad891269..258153502 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -43,14 +43,11 @@ bool check_cell_overlap(Particle& p, bool error=true); //! //! \param p A particle to be located. This function will populate the //! geometry-dependent data fields of the particle. -//! \param use_neighbor_lists If true, neighbor lists will be used to accelerate -//! the geometry search, but this only works if the cell attribute of the -//! particle's lowest coordinate level is valid and meaningful. //! \return True if the particle's location could be found and ascribed to a //! valid geometry coordinate stack. //============================================================================== - -bool find_cell(Particle& p, bool use_neighbor_lists); +bool brute_force_find_cell(Particle& p); +bool neighbor_list_find_cell(Particle& p); // Only usable on surface crossings //============================================================================== //! Move a particle into a new lattice tile. diff --git a/include/openmc/plot.h b/include/openmc/plot.h index a15bc7e38..295a7b874 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -182,7 +182,7 @@ T PlotBase::get_map() const { p.r()[in_i] = xyz[in_i] + in_pixel * x; p.n_coord_ = 1; // local variables - bool found_cell = find_cell(p, 0); + bool found_cell = brute_force_find_cell(p); j = p.n_coord_ - 1; if (level >= 0) { j = level; } if (found_cell) { diff --git a/src/geometry.cpp b/src/geometry.cpp index 4b5c9b47c..819e069af 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -246,44 +246,37 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) //============================================================================== -bool -find_cell(Particle& p, bool use_neighbor_lists) +bool neighbor_list_find_cell(Particle& p) +{ + // Get the cell this particle was in previously. + auto coord_lvl = p.n_coord_ - 1; + auto i_cell = p.coord_[coord_lvl].cell; + Cell& c {*model::cells[i_cell]}; + + // Search for the particle in that cell's neighbor list. Return if we + // found the particle. + bool found = find_cell_inner(p, &c.neighbors_); + if (found) + return found; + + // The particle could not be found in the neighbor list. Try searching all + // cells in this universe, and update the neighbor list if we find a new + // neighboring cell. + found = find_cell_inner(p, nullptr); + if (found) + c.neighbors_.push_back(p.coord_[coord_lvl].cell); + return found; +} + +bool brute_force_find_cell(Particle& p) { - // Determine universe (if not yet set, use root universe). int i_universe = p.coord_[p.n_coord_-1].universe; if (i_universe == C_NONE) { p.coord_[0].universe = model::root_universe; p.n_coord_ = 1; i_universe = model::root_universe; } - - // Reset all the deeper coordinate levels. - for (int i = p.n_coord_; i < p.coord_.size(); i++) { - p.coord_[i].reset(); - } - - if (use_neighbor_lists) { - // Get the cell this particle was in previously. - auto coord_lvl = p.n_coord_ - 1; - auto i_cell = p.coord_[coord_lvl].cell; - Cell& c {*model::cells[i_cell]}; - - // Search for the particle in that cell's neighbor list. Return if we - // found the particle. - bool found = find_cell_inner(p, &c.neighbors_); - if (found) return found; - - // The particle could not be found in the neighbor list. Try searching all - // cells in this universe, and update the neighbor list if we find a new - // neighboring cell. - found = find_cell_inner(p, nullptr); - if (found) c.neighbors_.push_back(p.coord_[coord_lvl].cell); - return found; - - } else { - // Search all cells in this universe for the particle. - return find_cell_inner(p, nullptr); - } + return find_cell_inner(p, nullptr); } //============================================================================== @@ -319,7 +312,7 @@ cross_lattice(Particle& p, const BoundaryInfo& boundary) if (!lat.are_valid_indices(i_xyz)) { // The particle is outside the lattice. Search for it from the base coords. p.n_coord_ = 1; - bool found = find_cell(p, 0); + bool found = brute_force_find_cell(p); if (!found && p.alive_) { p.mark_as_lost(fmt::format("Could not locate particle {} after " "crossing a lattice boundary", p.id_)); @@ -328,13 +321,13 @@ cross_lattice(Particle& p, const BoundaryInfo& boundary) } else { // Find cell in next lattice element. p.coord_[p.n_coord_-1].universe = lat[i_xyz]; - bool found = find_cell(p, 0); + bool found = brute_force_find_cell(p); if (!found) { // A particle crossing the corner of a lattice tile may not be found. In // this case, search for it from the base coords. p.n_coord_ = 1; - bool found = find_cell(p, 0); + bool found = brute_force_find_cell(p); if (!found && p.alive_) { p.mark_as_lost(fmt::format("Could not locate particle {} after " "crossing a lattice boundary", p.id_)); @@ -450,7 +443,7 @@ openmc_find_cell(const double* xyz, int32_t* index, int32_t* instance) p.r() = Position{xyz}; p.u() = {0.0, 0.0, 1.0}; - if (!find_cell(p, false)) { + if (!brute_force_find_cell(p)) { set_errmsg(fmt::format("Could not find cell at position {}.", p.r())); return OPENMC_E_GEOMETRY; } diff --git a/src/particle.cpp b/src/particle.cpp index d03511e67..3273ef4cc 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -156,7 +156,7 @@ Particle::event_calculate_xs() // initiate a search for the current cell. This generally happens at the // beginning of the history and again for any secondary particles if (coord_[n_coord_ - 1].cell == C_NONE) { - if (!find_cell(*this, false)) { + if (!brute_force_find_cell(*this)) { this->mark_as_lost("Could not find the cell containing particle " + std::to_string(id_)); return; @@ -454,7 +454,8 @@ Particle::cross_surface() } #endif - if (find_cell(*this, true)) return; + if (neighbor_list_find_cell(*this)) + return; // ========================================================================== // COULDN'T FIND PARTICLE IN NEIGHBORING CELLS, SEARCH ALL CELLS @@ -462,7 +463,7 @@ Particle::cross_surface() // Remove lower coordinate levels and assignment of surface surface_ = 0; n_coord_ = 1; - bool found = find_cell(*this, false); + bool found = brute_force_find_cell(*this); if (settings::run_mode != RunMode::PLOTTING && (!found)) { // If a cell is still not found, there are two possible causes: 1) there is @@ -476,7 +477,7 @@ Particle::cross_surface() // Couldn't find next cell anywhere! This probably means there is an actual // undefined region in the geometry. - if (!find_cell(*this, false)) { + if (!brute_force_find_cell(*this)) { this->mark_as_lost("After particle " + std::to_string(id_) + " crossed surface " + std::to_string(surf->id_) + " it could not be located in any cell and it did not leak."); @@ -553,7 +554,7 @@ Particle::cross_reflective_bc(const Surface& surf, Direction new_u) // (unless we're using a dagmc model, which has exactly one universe) if (!settings::dagmc) { n_coord_ = 1; - if (!find_cell(*this, true)) { + if (!neighbor_list_find_cell(*this)) { this->mark_as_lost("Couldn't find particle after reflecting from surface " + std::to_string(surf.id_) + "."); return; @@ -601,7 +602,7 @@ Particle::cross_periodic_bc(const Surface& surf, Position new_r, // Figure out what cell particle is in now n_coord_ = 1; - if (!find_cell(*this, true)) { + if (!neighbor_list_find_cell(*this)) { this->mark_as_lost("Couldn't find particle after hitting periodic " "boundary on surface " + std::to_string(surf.id_) + ". The normal vector " "of one periodic surface may need to be reversed."); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 96ef55234..46fc8352a 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -135,7 +135,8 @@ std::vector VolumeCalculation::execute() const p.u() = {0.5, 0.5, 0.5}; // If this location is not in the geometry at all, move on to next block - if (!find_cell(p, false)) continue; + if (!brute_force_find_cell(p)) + continue; if (domain_type_ == TallyDomain::MATERIAL) { if (p.material_ != MATERIAL_VOID) { From 6891e1a2121fbf7bcbe3f520ee27adc445fa5b9c Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 4 Mar 2021 21:46:03 -0500 Subject: [PATCH 21/68] remove accidentally pasted line --- src/geometry.cpp | 264 +++++++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 133 deletions(-) diff --git a/src/geometry.cpp b/src/geometry.cpp index 819e069af..7082f22ff 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -72,7 +72,7 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) // Find which cell of this universe the particle is in. Use the neighbor list // to shorten the search if one was provided. bool found = false; - int32_t i_cell; + int32_t i_cell = C_NONE; if (neighbor_list) { for (auto it = neighbor_list->cbegin(); it != neighbor_list->cend(); ++it) { i_cell = *it; @@ -91,154 +91,152 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) break; } } + } - } else { - int i_universe = p.coord_[p.n_coord_-1].universe; - const auto& univ {*model::universes[i_universe]}; - const auto& cells { - !univ.partitioner_ - ? model::universes[i_universe]->cells_ - : univ.partitioner_->get_cells(p.r_local(), p.u_local()) - }; - for (auto it = cells.cbegin(); it != cells.cend(); it++) { - i_cell = *it; - - // Make sure the search cell is in the same universe. + // Check successively lower coordinate levels until finding material fill + for (;;++p.n_coord_) { + // If neighbor lists did not find a cell, must do brute force search + if (i_cell == C_NONE) { int i_universe = p.coord_[p.n_coord_-1].universe; - if (model::cells[i_cell]->universe_ != i_universe) continue; + const auto& univ {*model::universes[i_universe]}; + const auto& cells { + !univ.partitioner_ + ? model::universes[i_universe]->cells_ + : univ.partitioner_->get_cells(p.r_local(), p.u_local()) + }; - // Check if this cell contains the particle. - Position r {p.r_local()}; - Direction u {p.u_local()}; - auto surf = p.surface_; - if (model::cells[i_cell]->contains(r, u, surf)) { - p.coord_[p.n_coord_-1].cell = i_cell; - found = true; - break; + for (auto it = cells.cbegin(); it != cells.cend(); it++) { + i_cell = *it; + + // Make sure the search cell is in the same universe. + int i_universe = p.coord_[p.n_coord_-1].universe; + if (model::cells[i_cell]->universe_ != i_universe) continue; + + // Check if this cell contains the particle. + Position r {p.r_local()}; + Direction u {p.u_local()}; + auto surf = p.surface_; + if (model::cells[i_cell]->contains(r, u, surf)) { + p.coord_[p.n_coord_-1].cell = i_cell; + found = true; + break; + } } } - } - // Announce the cell that the particle is entering. - if (found && (settings::verbosity >= 10 || p.trace_)) { - auto msg = fmt::format(" Entering cell {}", model::cells[i_cell]->id_); - write_message(msg, 1); - } + // Announce the cell that the particle is entering. + if (found && (settings::verbosity >= 10 || p.trace_)) { + auto msg = fmt::format(" Entering cell {}", model::cells[i_cell]->id_); + write_message(msg, 1); + } - if (found) { - Cell& c {*model::cells[i_cell]}; - if (c.type_ == Fill::MATERIAL) { - //======================================================================= - //! Found a material cell which means this is the lowest coord level. + if (found) { + Cell& c {*model::cells[i_cell]}; + if (c.type_ == Fill::MATERIAL) { + // Found a material cell which means this is the lowest coord level. - // Find the distribcell instance number. - int offset = 0; - if (c.distribcell_index_ >= 0) { - for (int i = 0; i < p.n_coord_; i++) { - const auto& c_i {*model::cells[p.coord_[i].cell]}; - if (c_i.type_ == Fill::UNIVERSE) { - offset += c_i.offset_[c.distribcell_index_]; - } else if (c_i.type_ == Fill::LATTICE) { - auto& lat {*model::lattices[p.coord_[i+1].lattice]}; - int i_xyz[3] {p.coord_[i+1].lattice_x, - p.coord_[i+1].lattice_y, - p.coord_[i+1].lattice_z}; - if (lat.are_valid_indices(i_xyz)) { - offset += lat.offset(c.distribcell_index_, i_xyz); + // Find the distribcell instance number. + int offset = 0; + if (c.distribcell_index_ >= 0) { + for (int i = 0; i < p.n_coord_; i++) { + const auto& c_i {*model::cells[p.coord_[i].cell]}; + if (c_i.type_ == Fill::UNIVERSE) { + offset += c_i.offset_[c.distribcell_index_]; + } else if (c_i.type_ == Fill::LATTICE) { + auto& lat {*model::lattices[p.coord_[i+1].lattice]}; + int i_xyz[3] {p.coord_[i+1].lattice_x, + p.coord_[i+1].lattice_y, + p.coord_[i+1].lattice_z}; + if (lat.are_valid_indices(i_xyz)) { + offset += lat.offset(c.distribcell_index_, i_xyz); + } } } } - } - p.cell_instance_ = offset; + p.cell_instance_ = offset; - // Set the material and temperature. - p.material_last_ = p.material_; - if (c.material_.size() > 1) { - p.material_ = c.material_[p.cell_instance_]; - } else { - p.material_ = c.material_[0]; - } - p.sqrtkT_last_ = p.sqrtkT_; - if (c.sqrtkT_.size() > 1) { - p.sqrtkT_ = c.sqrtkT_[p.cell_instance_]; - } else { - p.sqrtkT_ = c.sqrtkT_[0]; - } - - return true; - - } else if (c.type_ == Fill::UNIVERSE) { - //======================================================================== - //! Found a lower universe, update this coord level then search the next. - - // Set the lower coordinate level universe. - auto& coord {p.coord_[p.n_coord_]}; - coord.universe = c.fill_; - - // Set the position and direction. - coord.r = p.r_local(); - coord.u = p.u_local(); - - // Apply translation. - coord.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - // Update the coordinate level and recurse. - ++p.n_coord_; - return find_cell_inner(p, nullptr); - - } else if (c.type_ == Fill::LATTICE) { - //======================================================================== - //! Found a lower lattice, update this coord level then search the next. - - Lattice& lat {*model::lattices[c.fill_]}; - - // Set the position and direction. - auto& coord {p.coord_[p.n_coord_]}; - coord.r = p.r_local(); - coord.u = p.u_local(); - - // Apply translation. - coord.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - // Determine lattice indices. - auto i_xyz = lat.get_indices(coord.r, coord.u); - - // Get local position in appropriate lattice cell - coord.r = lat.get_local_position(coord.r, i_xyz); - - // Set lattice indices. - coord.lattice = c.fill_; - coord.lattice_x = i_xyz[0]; - coord.lattice_y = i_xyz[1]; - coord.lattice_z = i_xyz[2]; - - // Set the lower coordinate level universe. - if (lat.are_valid_indices(i_xyz)) { - coord.universe = lat[i_xyz]; - } else { - if (lat.outer_ != NO_OUTER_UNIVERSE) { - coord.universe = lat.outer_; + // Set the material and temperature. + p.material_last_ = p.material_; + if (c.material_.size() > 1) { + p.material_ = c.material_[p.cell_instance_]; } else { - warning(fmt::format("Particle {} is outside lattice {} but the " - "lattice has no defined outer universe.", p.id_, lat.id_)); - return false; + p.material_ = c.material_[0]; + } + p.sqrtkT_last_ = p.sqrtkT_; + if (c.sqrtkT_.size() > 1) { + p.sqrtkT_ = c.sqrtkT_[p.cell_instance_]; + } else { + p.sqrtkT_ = c.sqrtkT_[0]; + } + + return true; + + } else if (c.type_ == Fill::UNIVERSE) { + //======================================================================== + //! Found a lower universe, update this coord level then search the next. + + // Set the lower coordinate level universe. + auto& coord {p.coord_[p.n_coord_]}; + coord.universe = c.fill_; + + // Set the position and direction. + coord.r = p.r_local(); + coord.u = p.u_local(); + + // Apply translation. + coord.r -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + } else if (c.type_ == Fill::LATTICE) { + //======================================================================== + //! Found a lower lattice, update this coord level then search the next. + + Lattice& lat {*model::lattices[c.fill_]}; + + // Set the position and direction. + auto& coord {p.coord_[p.n_coord_]}; + coord.r = p.r_local(); + coord.u = p.u_local(); + + // Apply translation. + coord.r -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + // Determine lattice indices. + auto i_xyz = lat.get_indices(coord.r, coord.u); + + // Get local position in appropriate lattice cell + coord.r = lat.get_local_position(coord.r, i_xyz); + + // Set lattice indices. + coord.lattice = c.fill_; + coord.lattice_x = i_xyz[0]; + coord.lattice_y = i_xyz[1]; + coord.lattice_z = i_xyz[2]; + + // Set the lower coordinate level universe. + if (lat.are_valid_indices(i_xyz)) { + coord.universe = lat[i_xyz]; + } else { + if (lat.outer_ != NO_OUTER_UNIVERSE) { + coord.universe = lat.outer_; + } else { + warning(fmt::format("Particle {} is outside lattice {} but the " + "lattice has no defined outer universe.", p.id_, lat.id_)); + return false; + } } } - - // Update the coordinate level and recurse. - ++p.n_coord_; - return find_cell_inner(p, nullptr); } + i_cell = C_NONE; // trip non-neighbor cell search at next iteration } return found; From 2d8c66f0104a50087bf8d637aab99841f0cea3a3 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 4 Mar 2021 22:21:54 -0500 Subject: [PATCH 22/68] treat unfound case correctly --- src/geometry.cpp | 195 ++++++++++++++++++++++++----------------------- 1 file changed, 98 insertions(+), 97 deletions(-) diff --git a/src/geometry.cpp b/src/geometry.cpp index 7082f22ff..ea8892674 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -123,6 +123,9 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) } } } + if (!found) { + return found; + } // Announce the cell that the particle is entering. if (found && (settings::verbosity >= 10 || p.trace_)) { @@ -130,109 +133,107 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) write_message(msg, 1); } - if (found) { - Cell& c {*model::cells[i_cell]}; - if (c.type_ == Fill::MATERIAL) { - // Found a material cell which means this is the lowest coord level. + Cell& c {*model::cells[i_cell]}; + if (c.type_ == Fill::MATERIAL) { + // Found a material cell which means this is the lowest coord level. - // Find the distribcell instance number. - int offset = 0; - if (c.distribcell_index_ >= 0) { - for (int i = 0; i < p.n_coord_; i++) { - const auto& c_i {*model::cells[p.coord_[i].cell]}; - if (c_i.type_ == Fill::UNIVERSE) { - offset += c_i.offset_[c.distribcell_index_]; - } else if (c_i.type_ == Fill::LATTICE) { - auto& lat {*model::lattices[p.coord_[i+1].lattice]}; - int i_xyz[3] {p.coord_[i+1].lattice_x, - p.coord_[i+1].lattice_y, - p.coord_[i+1].lattice_z}; - if (lat.are_valid_indices(i_xyz)) { - offset += lat.offset(c.distribcell_index_, i_xyz); - } + // Find the distribcell instance number. + int offset = 0; + if (c.distribcell_index_ >= 0) { + for (int i = 0; i < p.n_coord_; i++) { + const auto& c_i {*model::cells[p.coord_[i].cell]}; + if (c_i.type_ == Fill::UNIVERSE) { + offset += c_i.offset_[c.distribcell_index_]; + } else if (c_i.type_ == Fill::LATTICE) { + auto& lat {*model::lattices[p.coord_[i + 1].lattice]}; + int i_xyz[3] {p.coord_[i + 1].lattice_x, p.coord_[i + 1].lattice_y, + p.coord_[i + 1].lattice_z}; + if (lat.are_valid_indices(i_xyz)) { + offset += lat.offset(c.distribcell_index_, i_xyz); } } } - p.cell_instance_ = offset; + } + p.cell_instance_ = offset; - // Set the material and temperature. - p.material_last_ = p.material_; - if (c.material_.size() > 1) { - p.material_ = c.material_[p.cell_instance_]; + // Set the material and temperature. + p.material_last_ = p.material_; + if (c.material_.size() > 1) { + p.material_ = c.material_[p.cell_instance_]; + } else { + p.material_ = c.material_[0]; + } + p.sqrtkT_last_ = p.sqrtkT_; + if (c.sqrtkT_.size() > 1) { + p.sqrtkT_ = c.sqrtkT_[p.cell_instance_]; + } else { + p.sqrtkT_ = c.sqrtkT_[0]; + } + + return true; + + } else if (c.type_ == Fill::UNIVERSE) { + //======================================================================== + //! Found a lower universe, update this coord level then search the next. + + // Set the lower coordinate level universe. + auto& coord {p.coord_[p.n_coord_]}; + coord.universe = c.fill_; + + // Set the position and direction. + coord.r = p.r_local(); + coord.u = p.u_local(); + + // Apply translation. + coord.r -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + } else if (c.type_ == Fill::LATTICE) { + //======================================================================== + //! Found a lower lattice, update this coord level then search the next. + + Lattice& lat {*model::lattices[c.fill_]}; + + // Set the position and direction. + auto& coord {p.coord_[p.n_coord_]}; + coord.r = p.r_local(); + coord.u = p.u_local(); + + // Apply translation. + coord.r -= c.translation_; + + // Apply rotation. + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } + + // Determine lattice indices. + auto i_xyz = lat.get_indices(coord.r, coord.u); + + // Get local position in appropriate lattice cell + coord.r = lat.get_local_position(coord.r, i_xyz); + + // Set lattice indices. + coord.lattice = c.fill_; + coord.lattice_x = i_xyz[0]; + coord.lattice_y = i_xyz[1]; + coord.lattice_z = i_xyz[2]; + + // Set the lower coordinate level universe. + if (lat.are_valid_indices(i_xyz)) { + coord.universe = lat[i_xyz]; + } else { + if (lat.outer_ != NO_OUTER_UNIVERSE) { + coord.universe = lat.outer_; } else { - p.material_ = c.material_[0]; - } - p.sqrtkT_last_ = p.sqrtkT_; - if (c.sqrtkT_.size() > 1) { - p.sqrtkT_ = c.sqrtkT_[p.cell_instance_]; - } else { - p.sqrtkT_ = c.sqrtkT_[0]; - } - - return true; - - } else if (c.type_ == Fill::UNIVERSE) { - //======================================================================== - //! Found a lower universe, update this coord level then search the next. - - // Set the lower coordinate level universe. - auto& coord {p.coord_[p.n_coord_]}; - coord.universe = c.fill_; - - // Set the position and direction. - coord.r = p.r_local(); - coord.u = p.u_local(); - - // Apply translation. - coord.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - } else if (c.type_ == Fill::LATTICE) { - //======================================================================== - //! Found a lower lattice, update this coord level then search the next. - - Lattice& lat {*model::lattices[c.fill_]}; - - // Set the position and direction. - auto& coord {p.coord_[p.n_coord_]}; - coord.r = p.r_local(); - coord.u = p.u_local(); - - // Apply translation. - coord.r -= c.translation_; - - // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } - - // Determine lattice indices. - auto i_xyz = lat.get_indices(coord.r, coord.u); - - // Get local position in appropriate lattice cell - coord.r = lat.get_local_position(coord.r, i_xyz); - - // Set lattice indices. - coord.lattice = c.fill_; - coord.lattice_x = i_xyz[0]; - coord.lattice_y = i_xyz[1]; - coord.lattice_z = i_xyz[2]; - - // Set the lower coordinate level universe. - if (lat.are_valid_indices(i_xyz)) { - coord.universe = lat[i_xyz]; - } else { - if (lat.outer_ != NO_OUTER_UNIVERSE) { - coord.universe = lat.outer_; - } else { - warning(fmt::format("Particle {} is outside lattice {} but the " - "lattice has no defined outer universe.", p.id_, lat.id_)); - return false; - } + warning(fmt::format("Particle {} is outside lattice {} but the " + "lattice has no defined outer universe.", + p.id_, lat.id_)); + return false; } } } From 5f5420a0a9105fb0595c2f14a21a4928bc4a8912 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 4 Mar 2021 23:35:24 -0500 Subject: [PATCH 23/68] add optimization for position indexing in surface functions --- include/openmc/position.h | 44 +++++++++++++++++++++++++++++++++++++++ src/surface.cpp | 43 +++++++++++++++++++------------------- 2 files changed, 66 insertions(+), 21 deletions(-) diff --git a/include/openmc/position.h b/include/openmc/position.h index 3046f6163..26cd6bb3f 100644 --- a/include/openmc/position.h +++ b/include/openmc/position.h @@ -51,6 +51,18 @@ struct Position { } } + // Access to x, y, or z by compile time known index (specializations below) + template + const double& get() const + { + throw std::out_of_range {"Index in Position must be between 0 and 2."}; + } + template + double& get() + { + throw std::out_of_range {"Index in Position must be between 0 and 2."}; + } + // Other member functions //! Dot product of two vectors @@ -77,6 +89,38 @@ struct Position { double z = 0.; }; +// Compile-time known member index access functions +template<> +inline const double& Position::get<0>() const +{ + return x; +} +template<> +inline const double& Position::get<1>() const +{ + return y; +} +template<> +inline const double& Position::get<2>() const +{ + return z; +} +template<> +inline double& Position::get<0>() +{ + return x; +} +template<> +inline double& Position::get<1>() +{ + return y; +} +template<> +inline double& Position::get<2>() +{ + return z; +} + // Binary operators inline Position operator+(Position a, Position b) { return a += b; } inline Position operator+(Position a, double b) { return a += b; } diff --git a/src/surface.cpp b/src/surface.cpp index 81d96d83a..69df8f673 100644 --- a/src/surface.cpp +++ b/src/surface.cpp @@ -474,8 +474,8 @@ template double axis_aligned_cylinder_evaluate(Position r, double offset1, double offset2, double radius) { - const double r1 = r[i1] - offset1; - const double r2 = r[i2] - offset2; + const double r1 = r.get() - offset1; + const double r2 = r.get() - offset2; return r1*r1 + r2*r2 - radius*radius; } @@ -486,12 +486,12 @@ template double axis_aligned_cylinder_distance(Position r, Direction u, bool coincident, double offset1, double offset2, double radius) { - const double a = 1.0 - u[i1]*u[i1]; // u^2 + v^2 + const double a = 1.0 - u.get() * u.get(); // u^2 + v^2 if (a == 0.0) return INFTY; - const double r2 = r[i2] - offset1; - const double r3 = r[i3] - offset2; - const double k = r2 * u[i2] + r3 * u[i3]; + const double r2 = r.get() - offset1; + const double r3 = r.get() - offset2; + const double k = r2 * u.get() + r3 * u.get(); const double c = r2*r2 + r3*r3 - radius*radius; const double quad = k*k - a*c; @@ -532,9 +532,9 @@ template Direction axis_aligned_cylinder_normal(Position r, double offset1, double offset2) { Direction u; - u[i2] = 2.0 * (r[i2] - offset1); - u[i3] = 2.0 * (r[i3] - offset2); - u[i1] = 0.0; + u.get() = 2.0 * (r.get() - offset1); + u.get() = 2.0 * (r.get() - offset2); + u.get() = 0.0; return u; } @@ -750,9 +750,9 @@ template double axis_aligned_cone_evaluate(Position r, double offset1, double offset2, double offset3, double radius_sq) { - const double r1 = r[i1] - offset1; - const double r2 = r[i2] - offset2; - const double r3 = r[i3] - offset3; + const double r1 = r.get() - offset1; + const double r2 = r.get() - offset2; + const double r3 = r.get() - offset3; return r2*r2 + r3*r3 - radius_sq*r1*r1; } @@ -764,12 +764,13 @@ axis_aligned_cone_distance(Position r, Direction u, bool coincident, double offset1, double offset2, double offset3, double radius_sq) { - const double r1 = r[i1] - offset1; - const double r2 = r[i2] - offset2; - const double r3 = r[i3] - offset3; - const double a = u[i2]*u[i2] + u[i3]*u[i3] - - radius_sq*u[i1]*u[i1]; - const double k = r2*u[i2] + r3*u[i3] - radius_sq*r1*u[i1]; + const double r1 = r.get() - offset1; + const double r2 = r.get() - offset2; + const double r3 = r.get() - offset3; + const double a = u.get() * u.get() + u.get() * u.get() - + radius_sq * u.get() * u.get(); + const double k = + r2 * u.get() + r3 * u.get() - radius_sq * r1 * u.get(); const double c = r2*r2 + r3*r3 - radius_sq*r1*r1; double quad = k*k - a*c; @@ -818,9 +819,9 @@ axis_aligned_cone_normal(Position r, double offset1, double offset2, double offset3, double radius_sq) { Direction u; - u[i1] = -2.0 * radius_sq * (r[i1] - offset1); - u[i2] = 2.0 * (r[i2] - offset2); - u[i3] = 2.0 * (r[i3] - offset3); + u.get() = -2.0 * radius_sq * (r.get() - offset1); + u.get() = 2.0 * (r.get() - offset2); + u.get() = 2.0 * (r.get() - offset3); return u; } From 9d701af490a96f0a4c44bb2e679adcba03196c4f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 4 Mar 2021 22:52:26 -0600 Subject: [PATCH 24/68] Happy belated new year 2021 --- LICENSE | 2 +- docs/source/conf.py | 2 +- docs/source/license.rst | 2 +- man/man1/openmc.1 | 2 +- src/output.cpp | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index c145d7a0d..848a23845 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2011-2020 Massachusetts Institute of Technology and OpenMC contributors +Copyright (c) 2011-2021 Massachusetts Institute of Technology and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/docs/source/conf.py b/docs/source/conf.py index dea3440a7..7c9ba27d6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -63,7 +63,7 @@ master_doc = 'index' # General information about the project. project = 'OpenMC' -copyright = '2011-2020, Massachusetts Institute of Technology and OpenMC contributors' +copyright = '2011-2021, Massachusetts Institute of Technology and OpenMC contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/docs/source/license.rst b/docs/source/license.rst index e9b71d882..40ab106ad 100644 --- a/docs/source/license.rst +++ b/docs/source/license.rst @@ -4,7 +4,7 @@ License Agreement ================= -Copyright © 2011-2020 Massachusetts Institute of Technology and OpenMC contributors +Copyright © 2011-2021 Massachusetts Institute of Technology and OpenMC contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/man/man1/openmc.1 b/man/man1/openmc.1 index 1b2443c90..8a93af514 100644 --- a/man/man1/openmc.1 +++ b/man/man1/openmc.1 @@ -57,7 +57,7 @@ Indicates the default path to an HDF5 file that contains multi-group cross section libraries if the user has not specified the tag in .I materials.xml\fP. .SH LICENSE -Copyright \(co 2011-2020 Massachusetts Institute of Technology and OpenMC +Copyright \(co 2011-2021 Massachusetts Institute of Technology and OpenMC contributors. .PP Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/src/output.cpp b/src/output.cpp index 3d71c8f7f..89a9070a2 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -74,7 +74,7 @@ void title() // Write version information fmt::print( " | The OpenMC Monte Carlo Code\n" - " Copyright | 2011-2020 MIT and OpenMC contributors\n" + " Copyright | 2011-2021 MIT and OpenMC contributors\n" " License | https://docs.openmc.org/en/latest/license.html\n" " Version | {}.{}.{}{}\n", VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE, VERSION_DEV ? "-dev" : ""); @@ -328,7 +328,7 @@ void print_version() #ifdef GIT_SHA1 fmt::print("Git SHA1: {}\n", GIT_SHA1); #endif - fmt::print("Copyright (c) 2011-2020 Massachusetts Institute of " + fmt::print("Copyright (c) 2011-2021 Massachusetts Institute of " "Technology and OpenMC contributors\nMIT/X license at " "\n"); } From 65399ae03f6a50fe2899a9ab161f76a752a6930e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 4 Mar 2021 23:05:50 -0600 Subject: [PATCH 25/68] Fix description of secondary particles in (n,gamma) docs section --- docs/source/methods/neutron_physics.rst | 9 +++++---- docs/source/methods/photon_physics.rst | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/source/methods/neutron_physics.rst b/docs/source/methods/neutron_physics.rst index e898e63b0..5961283fa 100644 --- a/docs/source/methods/neutron_physics.rst +++ b/docs/source/methods/neutron_physics.rst @@ -76,10 +76,11 @@ absorption cross section (this includes fission), and :math:`\sigma_f` is the total fission cross section. If this condition is met, then the neutron is killed and we proceed to simulate the next neutron from the source bank. -No secondary particles from disappearance reactions such as photons or -alpha-particles are produced or tracked. To truly capture the affects of gamma -heating in a problem, it would be necessary to explicitly track photons -originating from :math:`(n,\gamma)` and other reactions. +Note that photons arising from :math:`(n,\gamma)` and other neutron reactions +are not produced in a microscopically correct manner. Instead, photons are +sampled probabilistically at each neutron collision, regardless of what reaction +actually takes place. This is described in more detail in +:ref:`photon_production`. ------------------ Elastic Scattering diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index f9bdcfa1c..7c4733ff4 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -1004,6 +1004,8 @@ direction of the incident charged particle, which is a reasonable approximation at higher energies when the bremsstrahlung radiation is emitted at small angles. +.. _photon_production: + ----------------- Photon Production ----------------- From 9755fa55af67627fc7e7f9693b2975d1cf7916e5 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 4 Mar 2021 23:25:21 -0600 Subject: [PATCH 26/68] Fix a few docbuild issues --- docs/source/io_formats/index.rst | 1 - docs/source/io_formats/statepoint.rst | 4 ++-- docs/source/methods/cross_sections.rst | 8 ++++---- docs/source/usersguide/cross_sections.rst | 6 +++--- docs/source/usersguide/install.rst | 23 +++++++++++------------ docs/source/usersguide/processing.rst | 8 ++++---- 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/docs/source/io_formats/index.rst b/docs/source/io_formats/index.rst index bc5b0a4a9..7ae22c0a3 100644 --- a/docs/source/io_formats/index.rst +++ b/docs/source/io_formats/index.rst @@ -44,7 +44,6 @@ Output Files statepoint source - surface_source summary depletion_results particle_restart diff --git a/docs/source/io_formats/statepoint.rst b/docs/source/io_formats/statepoint.rst index 92b103ef7..46725fa8b 100644 --- a/docs/source/io_formats/statepoint.rst +++ b/docs/source/io_formats/statepoint.rst @@ -162,5 +162,5 @@ All values are given in seconds and are measured on the master process. source sites between processes for load balancing. - **accumulating tallies** (*double*) -- Time spent communicating tally results and evaluating their statistics. - - **writing statepoints** (*double*) -- Time spent writing statepoint - files + - **writing statepoints** (*double*) -- Time spent writing statepoint + files diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index c360805be..dc42d33de 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -186,10 +186,10 @@ The data governing the interaction of particles with various nuclei or materials are represented using a multi-group library format specific to the OpenMC code. The format is described in the :ref:`mgxs_lib_spec`. The data itself can be prepared via traditional paths or directly from a continuous-energy OpenMC -calculation by use of the Python API as is shown in the -:ref:`notebook_mg_mode_part_i` example notebook. This multi-group library -consists of meta-data (such as the energy group structure) and multiple `xsdata` -objects which contains the required microscopic or macroscopic multi-group data. +calculation by use of the Python API as is shown in an `example notebook +<../examples/mg-mode-part-i.ipynb>`_. This multi-group library consists of +meta-data (such as the energy group structure) and multiple `xsdata` objects +which contains the required microscopic or macroscopic multi-group data. At a minimum, the library must contain the absorption cross section (:math:`\sigma_{a,g}`) and a scattering matrix. If the problem is an eigenvalue diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/cross_sections.rst index 74267c9c0..bc5b3436f 100644 --- a/docs/source/usersguide/cross_sections.rst +++ b/docs/source/usersguide/cross_sections.rst @@ -124,7 +124,7 @@ OpenMC. .. hint:: The :class:`IncidentNeutron` class allows you to view/modify cross sections, secondary angle/energy distributions, probability tables, etc. For a more thorough overview of the capabilities of this class, - see the :ref:`notebook_nuclear_data` example notebook. + see the `example notebook <../examples/nuclear-data.ipynb>`__. Manually Creating a Library from ENDF files ------------------------------------------- @@ -252,8 +252,8 @@ However, if obtained or generated their own library, the user should set the :envvar:`OPENMC_MG_CROSS_SECTIONS` environment variable to the absolute path of the file library expected to used most frequently. -For an example of how to create a multi-group library, see -:ref:`notebook_mg_mode_part_i`. +For an example of how to create a multi-group library, see the `example notebook +<../examples/mg-mode-part-i.ipynb>`__. .. _NJOY: http://www.njoy21.io/ .. _NNDC: https://www.nndc.bnl.gov/endf diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index bc7a2214b..ab2fe2f12 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -12,11 +12,11 @@ Installation and Configuration Installing on Linux/Mac with conda-forge ---------------------------------------- -`Conda `_ is an open source package management -system and environment management system for installing multiple versions of -software packages and their dependencies and switching easily between them. If -you have `conda` installed on your system, OpenMC can be installed via the -`conda-forge` channel. First, add the `conda-forge` channel with: +Conda_ is an open source package management system and environment management +system for installing multiple versions of software packages and their +dependencies and switching easily between them. If you have `conda` installed on +your system, OpenMC can be installed via the `conda-forge` channel. First, add +the `conda-forge` channel with: .. code-block:: sh @@ -25,16 +25,16 @@ you have `conda` installed on your system, OpenMC can be installed via the To list the versions of OpenMC that are available on the `conda-forge` channel, in your terminal window or an Anaconda Prompt run: -.. code-block:: sh +.. code-block:: sh conda search openmc - + OpenMC can then be installed with: .. code-block:: sh conda create -n openmc-env openmc - + This will install OpenMC in a conda environment called `openmc-env`. To activate the environment, run: @@ -424,7 +424,7 @@ distributions. `pandas `_ Pandas is used to generate tally DataFrames as demonstrated in - :ref:`examples_pandas` example notebook. + an `example notebook <../examples/pandas-dataframes.ipynb>`_. `h5py `_ h5py provides Python bindings to the HDF5 library. Since OpenMC outputs @@ -509,7 +509,6 @@ schemas.xml file in your own OpenMC source directory. .. _GNU Emacs: http://www.gnu.org/software/emacs/ .. _validation: https://en.wikipedia.org/wiki/XML_validation .. _RELAX NG: http://relaxng.org/ -.. _NNDC: http://www.nndc.bnl.gov/endf/b7.1/acefiles.html -.. _ctest: http://www.cmake.org/cmake/help/v2.8.12/ctest.html -.. _Conda: https://docs.conda.io/en/latest/ +.. _ctest: https://cmake.org/cmake/help/latest/manual/ctest.1.html +.. _Conda: https://conda.io/en/latest/ .. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 210caca82..9652a51ca 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -34,13 +34,13 @@ as requested; it is used in many of the provided plotting utilities, OpenMC's regression test suite, and can be used in user-created scripts to carry out manipulations of the data. -An :ref:`example IPython notebook ` demonstrates how -to extract data from a statepoint using the Python API. +An `example notebook <../examples/post-processing.ipynb>`_ demonstrates how to +extract data from a statepoint using the Python API. Plotting in 2D -------------- -The :ref:`IPython notebook example ` also demonstrates +The `notebook example <../examples/post-processing.ipynb>`_ also demonstrates how to plot a structured mesh tally in two dimensions using the Python API. One can also use the :ref:`scripts_plot` script which provides an interactive GUI to explore and plot structured mesh tallies for any scores and filter bins. @@ -101,5 +101,5 @@ For eigenvalue problems, OpenMC will store information on the fission source sites in the statepoint file by default. For each source site, the weight, position, sampled direction, and sampled energy are stored. To extract this data from a statepoint file, the ``openmc.statepoint`` module can be used. An -:ref:`example IPython notebook ` demontrates how to +`example notebook <../examples/post-processing.ipynb>`_ demontrates how to analyze and plot source information. From 40ec743caff68db528eb8dbfccae75e8cdae8235 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Fri, 5 Mar 2021 11:10:19 -0500 Subject: [PATCH 27/68] reset coordinates in exhaustive cell search for track output --- src/geometry.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/geometry.cpp b/src/geometry.cpp index ea8892674..84b37dc1a 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -275,6 +275,10 @@ bool brute_force_find_cell(Particle& p) p.n_coord_ = 1; i_universe = model::root_universe; } + // Reset all the deeper coordinate levels. + for (int i = p.n_coord_; i < p.coord_.size(); i++) { + p.coord_[i].reset(); + } return find_cell_inner(p, nullptr); } From a367d2790578a925d99c3f765e7bc44fb840cd7b Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 10:40:38 -0600 Subject: [PATCH 28/68] Include error message in exception when openmc.run fails --- openmc/executor.py | 16 ++++++++++------ tests/unit_tests/test_source_file.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/openmc/executor.py b/openmc/executor.py index 8d208795c..3a3677226 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -25,8 +25,12 @@ def _run(args, output, cwd): # Raise an exception if return status is non-zero if p.returncode != 0: - raise subprocess.CalledProcessError(p.returncode, ' '.join(args), - ''.join(lines)) + # Get error message from output and simplify whitespace + output = ''.join(lines) + _, _, error_msg = output.partition('ERROR: ') + error_msg = ' '.join(error_msg.split()) + + raise RuntimeError(error_msg) def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): @@ -43,7 +47,7 @@ def plot_geometry(output=True, openmc_exec='openmc', cwd='.'): Raises ------ - subprocess.CalledProcessError + RuntimeError If the `openmc` executable returns a non-zero status """ @@ -70,7 +74,7 @@ def plot_inline(plots, openmc_exec='openmc', cwd='.', convert_exec='convert'): Raises ------ - subprocess.CalledProcessError + RuntimeError If the `openmc` executable returns a non-zero status """ @@ -133,7 +137,7 @@ def calculate_volumes(threads=None, output=True, cwd='.', Raises ------ - subprocess.CalledProcessError + RuntimeError If the `openmc` executable returns a non-zero status See Also @@ -188,7 +192,7 @@ def run(particles=None, threads=None, geometry_debug=False, Raises ------ - subprocess.CalledProcessError + RuntimeError If the `openmc` executable returns a non-zero status """ diff --git a/tests/unit_tests/test_source_file.py b/tests/unit_tests/test_source_file.py index 70aa1be4d..8166f5712 100644 --- a/tests/unit_tests/test_source_file.py +++ b/tests/unit_tests/test_source_file.py @@ -73,6 +73,6 @@ def test_wrong_source_attributes(run_in_tmpdir): # When we run the model, it should error out with a message that includes # the names of the wrong attributes - with pytest.raises(subprocess.CalledProcessError) as excinfo: + with pytest.raises(RuntimeError) as excinfo: openmc.run() - assert 'platypus, axolotl, narwhal' in excinfo.value.output + assert 'platypus, axolotl, narwhal' in str(excinfo.value) From 530fb91324423e3395c568b298f8e4f5f36bdcb3 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Mon, 8 Mar 2021 11:54:16 -0500 Subject: [PATCH 29/68] rename brute force to exhaustive --- include/openmc/geometry.h | 2 +- include/openmc/plot.h | 2 +- src/geometry.cpp | 12 ++++++------ src/particle.cpp | 6 +++--- src/volume_calc.cpp | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/openmc/geometry.h b/include/openmc/geometry.h index 258153502..0681952ad 100644 --- a/include/openmc/geometry.h +++ b/include/openmc/geometry.h @@ -46,7 +46,7 @@ bool check_cell_overlap(Particle& p, bool error=true); //! \return True if the particle's location could be found and ascribed to a //! valid geometry coordinate stack. //============================================================================== -bool brute_force_find_cell(Particle& p); +bool exhaustive_find_cell(Particle& p); bool neighbor_list_find_cell(Particle& p); // Only usable on surface crossings //============================================================================== diff --git a/include/openmc/plot.h b/include/openmc/plot.h index 295a7b874..7b66c6696 100644 --- a/include/openmc/plot.h +++ b/include/openmc/plot.h @@ -182,7 +182,7 @@ T PlotBase::get_map() const { p.r()[in_i] = xyz[in_i] + in_pixel * x; p.n_coord_ = 1; // local variables - bool found_cell = brute_force_find_cell(p); + bool found_cell = exhaustive_find_cell(p); j = p.n_coord_ - 1; if (level >= 0) { j = level; } if (found_cell) { diff --git a/src/geometry.cpp b/src/geometry.cpp index 84b37dc1a..8e6519fa8 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -95,7 +95,7 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) // Check successively lower coordinate levels until finding material fill for (;;++p.n_coord_) { - // If neighbor lists did not find a cell, must do brute force search + // If neighbor lists did not find a cell, must do exhaustive search if (i_cell == C_NONE) { int i_universe = p.coord_[p.n_coord_-1].universe; const auto& univ {*model::universes[i_universe]}; @@ -267,7 +267,7 @@ bool neighbor_list_find_cell(Particle& p) return found; } -bool brute_force_find_cell(Particle& p) +bool exhaustive_find_cell(Particle& p) { int i_universe = p.coord_[p.n_coord_-1].universe; if (i_universe == C_NONE) { @@ -315,7 +315,7 @@ cross_lattice(Particle& p, const BoundaryInfo& boundary) if (!lat.are_valid_indices(i_xyz)) { // The particle is outside the lattice. Search for it from the base coords. p.n_coord_ = 1; - bool found = brute_force_find_cell(p); + bool found = exhaustive_find_cell(p); if (!found && p.alive_) { p.mark_as_lost(fmt::format("Could not locate particle {} after " "crossing a lattice boundary", p.id_)); @@ -324,13 +324,13 @@ cross_lattice(Particle& p, const BoundaryInfo& boundary) } else { // Find cell in next lattice element. p.coord_[p.n_coord_-1].universe = lat[i_xyz]; - bool found = brute_force_find_cell(p); + bool found = exhaustive_find_cell(p); if (!found) { // A particle crossing the corner of a lattice tile may not be found. In // this case, search for it from the base coords. p.n_coord_ = 1; - bool found = brute_force_find_cell(p); + bool found = exhaustive_find_cell(p); if (!found && p.alive_) { p.mark_as_lost(fmt::format("Could not locate particle {} after " "crossing a lattice boundary", p.id_)); @@ -446,7 +446,7 @@ openmc_find_cell(const double* xyz, int32_t* index, int32_t* instance) p.r() = Position{xyz}; p.u() = {0.0, 0.0, 1.0}; - if (!brute_force_find_cell(p)) { + if (!exhaustive_find_cell(p)) { set_errmsg(fmt::format("Could not find cell at position {}.", p.r())); return OPENMC_E_GEOMETRY; } diff --git a/src/particle.cpp b/src/particle.cpp index 3273ef4cc..2e2413174 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -156,7 +156,7 @@ Particle::event_calculate_xs() // initiate a search for the current cell. This generally happens at the // beginning of the history and again for any secondary particles if (coord_[n_coord_ - 1].cell == C_NONE) { - if (!brute_force_find_cell(*this)) { + if (!exhaustive_find_cell(*this)) { this->mark_as_lost("Could not find the cell containing particle " + std::to_string(id_)); return; @@ -463,7 +463,7 @@ Particle::cross_surface() // Remove lower coordinate levels and assignment of surface surface_ = 0; n_coord_ = 1; - bool found = brute_force_find_cell(*this); + bool found = exhaustive_find_cell(*this); if (settings::run_mode != RunMode::PLOTTING && (!found)) { // If a cell is still not found, there are two possible causes: 1) there is @@ -477,7 +477,7 @@ Particle::cross_surface() // Couldn't find next cell anywhere! This probably means there is an actual // undefined region in the geometry. - if (!brute_force_find_cell(*this)) { + if (!exhaustive_find_cell(*this)) { this->mark_as_lost("After particle " + std::to_string(id_) + " crossed surface " + std::to_string(surf->id_) + " it could not be located in any cell and it did not leak."); diff --git a/src/volume_calc.cpp b/src/volume_calc.cpp index 46fc8352a..7d6739bc0 100644 --- a/src/volume_calc.cpp +++ b/src/volume_calc.cpp @@ -135,7 +135,7 @@ std::vector VolumeCalculation::execute() const p.u() = {0.5, 0.5, 0.5}; // If this location is not in the geometry at all, move on to next block - if (!brute_force_find_cell(p)) + if (!exhaustive_find_cell(p)) continue; if (domain_type_ == TallyDomain::MATERIAL) { From 6cb5b8b1492b47e3a5f1c7e2fbd6049e61bd5112 Mon Sep 17 00:00:00 2001 From: Shikhar Kumar Date: Mon, 8 Mar 2021 20:17:44 -0500 Subject: [PATCH 30/68] Address @smharper comments --- include/openmc/capi.h | 2 +- include/openmc/mesh.h | 2 + openmc/lib/mesh.py | 4 ++ src/mesh.cpp | 116 +++++++++++++++++------------------------- 4 files changed, 54 insertions(+), 70 deletions(-) diff --git a/include/openmc/capi.h b/include/openmc/capi.h index 6bde7b670..a36f0e280 100644 --- a/include/openmc/capi.h +++ b/include/openmc/capi.h @@ -84,7 +84,7 @@ extern "C" { int openmc_id_map(const void* slice, int32_t* data_out); int openmc_property_map(const void* slice, double* data_out); int openmc_rectilinear_mesh_get_grid(int32_t index, double** grid_x, int* nx, - double** grid_y, int * ny, double** grid_z, int* nz); + double** grid_y, int* ny, double** grid_z, int* nz); int openmc_rectilinear_mesh_set_grid(int32_t index, const double* grid_x, const int nx, const double* grid_y, const int ny, const double* grid_z, const int nz); diff --git a/include/openmc/mesh.h b/include/openmc/mesh.h index b2d919d30..7dfb4c520 100644 --- a/include/openmc/mesh.h +++ b/include/openmc/mesh.h @@ -239,6 +239,8 @@ public: void to_hdf5(hid_t group) const override; std::vector> grid_; + + int set_grid(); }; #ifdef DAGMC diff --git a/openmc/lib/mesh.py b/openmc/lib/mesh.py index 79404c628..dc528573a 100644 --- a/openmc/lib/mesh.py +++ b/openmc/lib/mesh.py @@ -190,13 +190,16 @@ class RegularMesh(Mesh): class RectilinearMesh(Mesh): """RectilinearMesh stored internally. + This class exposes a mesh that is stored internally in the OpenMC library. To obtain a view of a mesh with a given ID, use the :data:`openmc.lib.meshes` mapping. + Parameters ---------- index : int Index in the `meshes` array. + Attributes ---------- id : int @@ -209,6 +212,7 @@ class RectilinearMesh(Mesh): The upper-right corner of the structrued mesh. width : numpy.ndarray The width of mesh cells in each direction. + """ mesh_type = 'rectilinear' diff --git a/src/mesh.cpp b/src/mesh.cpp index a4b93e9af..7176f8485 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -888,21 +888,9 @@ RectilinearMesh::RectilinearMesh(pugi::xml_node node) grid_[1] = get_node_array(node, "y_grid"); grid_[2] = get_node_array(node, "z_grid"); - shape_ = {static_cast(grid_[0].size()) - 1, - static_cast(grid_[1].size()) - 1, - static_cast(grid_[2].size()) - 1}; - - for (const auto& g : grid_) { - if (g.size() < 2) fatal_error("x-, y-, and z- grids for rectilinear meshes " - "must each have at least 2 points"); - for (int i = 1; i < g.size(); ++i) { - if (g[i] <= g[i-1]) fatal_error("Values in for x-, y-, and z- grids for " - "rectilinear meshes must be sorted and unique."); - } + if (int err = set_grid()) { + fatal_error(openmc_err_msg); } - - lower_left_ = {grid_[0].front(), grid_[1].front(), grid_[2].front()}; - upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()}; } double RectilinearMesh::positive_grid_boundary(int* ijk, int i) const @@ -915,6 +903,33 @@ double RectilinearMesh::negative_grid_boundary(int* ijk, int i) const return grid_[i][ijk[i] - 1]; } +int RectilinearMesh::set_grid() +{ + shape_ = {static_cast(grid_[0].size()) - 1, + static_cast(grid_[1].size()) - 1, + static_cast(grid_[2].size()) - 1}; + + for (const auto& g : grid_) { + if (g.size() < 2) { + set_errmsg("x-, y-, and z- grids for rectilinear meshes " + "must each have at least 2 points"); + return OPENMC_E_INVALID_ARGUMENT; + } + for (int i = 1; i < g.size(); ++i) { + if (g[i] <= g[i-1]) { + set_errmsg("Values in for x-, y-, and z- grids for " + "rectilinear meshes must be sorted and unique."); + return OPENMC_E_INVALID_ARGUMENT; + } + } + } + + lower_left_ = {grid_[0].front(), grid_[1].front(), grid_[2].front()}; + upper_right_ = {grid_[0].back(), grid_[1].back(), grid_[2].back()}; + + return 0; +} + void RectilinearMesh::surface_bins_crossed(const Particle& p, std::vector& bins) const { @@ -1144,27 +1159,15 @@ check_mesh(int32_t index) return 0; } +template int -check_mesh_type(int32_t index, const std::string& mesh_compare_type) +check_mesh_type(int32_t index) { if (int err = check_mesh(index)) return err; - if (mesh_compare_type == "regular") { - RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); - if (!mesh) { - set_errmsg("This function is only valid for regular meshes."); - return OPENMC_E_INVALID_TYPE; - } - } - else if (mesh_compare_type == "rectilinear") { - RectilinearMesh* mesh = dynamic_cast(model::meshes[index].get()); - if (!mesh) { - set_errmsg("This function is only valid for rectilinear meshes."); - return OPENMC_E_INVALID_TYPE; - } - } - else { - set_errmsg("Mesh type " + mesh_compare_type + " is not supported."); + T* mesh = dynamic_cast(model::meshes[index].get()); + if (!mesh) { + set_errmsg("This function is not valid for input mesh."); return OPENMC_E_INVALID_TYPE; } return 0; @@ -1183,8 +1186,7 @@ openmc_mesh_get_type(int32_t index, char* type) RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); if (mesh) { std::strcpy(type, "regular"); - } - else { + } else { RectilinearMesh* mesh = dynamic_cast(model::meshes[index].get()); if (mesh) { std::strcpy(type, "rectilinear"); @@ -1202,11 +1204,9 @@ openmc_extend_meshes(int32_t n, const char* type, int32_t* index_start, std::string mesh_type; for (int i = 0; i < n; ++i) { - if (std::strcmp(type, "regular") == 0) - { + if (std::strcmp(type, "regular") == 0) { model::meshes.push_back(std::make_unique()); - } else if (std::strcmp(type, "rectilinear") == 0) - { + } else if (std::strcmp(type, "rectilinear") == 0) { model::meshes.push_back(std::make_unique()); } else { throw std::runtime_error{"Unknown mesh type: " + std::string(type)}; @@ -1253,7 +1253,7 @@ openmc_mesh_set_id(int32_t index, int32_t id) extern "C" int openmc_regular_mesh_get_dimension(int32_t index, int** dims, int* n) { - if (int err = check_mesh_type(index, "regular")) return err; + if (int err = check_mesh_type(index)) return err; RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); *dims = mesh->shape_.data(); *n = mesh->n_dimension_; @@ -1264,7 +1264,7 @@ openmc_regular_mesh_get_dimension(int32_t index, int** dims, int* n) extern "C" int openmc_regular_mesh_set_dimension(int32_t index, int n, const int* dims) { - if (int err = check_mesh_type(index, "regular")) return err; + if (int err = check_mesh_type(index)) return err; RegularMesh* mesh = dynamic_cast(model::meshes[index].get()); // Copy dimension @@ -1279,7 +1279,7 @@ extern "C" int openmc_regular_mesh_get_params(int32_t index, double** ll, double** ur, double** width, int* n) { - if (int err = check_mesh_type(index, "regular")) return err; + if (int err = check_mesh_type(index)) return err; RegularMesh* m = dynamic_cast(model::meshes[index].get()); if (m->lower_left_.dimension() == 0) { @@ -1299,7 +1299,7 @@ extern "C" int openmc_regular_mesh_set_params(int32_t index, int n, const double* ll, const double* ur, const double* width) { - if (int err = check_mesh_type(index, "regular")) return err; + if (int err = check_mesh_type(index)) return err; RegularMesh* m = dynamic_cast(model::meshes[index].get()); std::vector shape = {static_cast(n)}; @@ -1326,9 +1326,9 @@ openmc_regular_mesh_set_params(int32_t index, int n, const double* ll, //! Get the rectilinear mesh grid extern "C" int openmc_rectilinear_mesh_get_grid(int32_t index, double** grid_x, int* nx, - double** grid_y, int * ny, double** grid_z, int* nz) + double** grid_y, int* ny, double** grid_z, int* nz) { - if (int err = check_mesh_type(index, "rectilinear")) return err; + if (int err = check_mesh_type(index)) return err; RectilinearMesh* m = dynamic_cast(model::meshes[index].get()); if (m->lower_left_.dimension() == 0) { @@ -1346,13 +1346,13 @@ openmc_rectilinear_mesh_get_grid(int32_t index, double** grid_x, int* nx, return 0; } -//! Set the regular mesh parameters +//! Set the rectilienar mesh parameters extern "C" int openmc_rectilinear_mesh_set_grid(int32_t index, const double* grid_x, const int nx, const double* grid_y, const int ny, const double* grid_z, const int nz) { - if (int err = check_mesh_type(index, "rectilinear")) return err; + if (int err = check_mesh_type(index)) return err; RectilinearMesh* m = dynamic_cast(model::meshes[index].get()); m->n_dimension_ = 3; @@ -1368,30 +1368,8 @@ openmc_rectilinear_mesh_set_grid(int32_t index, const double* grid_x, m->grid_[2].push_back(grid_z[i]); } - m->shape_ = {static_cast(m->grid_[0].size()) - 1, - static_cast(m->grid_[1].size()) - 1, - static_cast(m->grid_[2].size()) - 1}; - - for (const auto& g : m->grid_) { - if (g.size() < 2) { - set_errmsg("x-, y-, and z- grids for rectilinear meshes " - "must each have at least 2 points"); - return OPENMC_E_INVALID_ARGUMENT; - } - for (int i = 1; i < g.size(); ++i) { - if (g[i] <= g[i-1]) { - std::cout << g[i] << " " << g[i-1] << "\n"; - set_errmsg("Values in for x-, y-, and z- grids for " - "rectilinear meshes must be sorted and unique."); - return OPENMC_E_INVALID_ARGUMENT; - } - } - } - - m->lower_left_ = {m->grid_[0].front(), m->grid_[1].front(), m->grid_[2].front()}; - m->upper_right_ = {m->grid_[0].back(), m->grid_[1].back(), m->grid_[2].back()}; - - return 0; + int err = m->set_grid(); + return err; } #ifdef DAGMC From 9933b7f0f83085785febd12dad44cafe01bbd780 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 9 Mar 2021 13:03:34 -0600 Subject: [PATCH 31/68] Update determination of temperatures to load given temperature range --- src/nuclide.cpp | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index 47f380aac..17508fa19 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -83,29 +83,25 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature) } // Determine actual temperatures to read -- start by checking whether a - // temperature range was given, in which case all temperatures in the range - // are loaded irrespective of what temperatures actually appear in the model + // temperature range was given (indicated by T_max > 0), in which case all + // temperatures in the range are loaded irrespective of what temperatures + // actually appear in the model std::vector temps_to_read; int n = temperature.size(); double T_min = n > 0 ? settings::temperature_range[0] : 0.0; double T_max = n > 0 ? settings::temperature_range[1] : INFTY; if (T_max > 0.0) { - // For each interval (T_j, T_j+1), if either T_j or T_j+1 are within the - // temperature range, load data for *both* T_j and T_j+1 - int n = temps_available.size(); - for (int j = 0; j < n; ++j) { - if ((T_min <= temps_available[j] && temps_available[j] < T_max)) { - int T_j = std::round(temps_available[j]); - if (!contains(temps_to_read, T_j)) { - temps_to_read.push_back(T_j); - } - } - if (j < n - 1 && T_min <= temps_available[j+1] && temps_available[j+1] < T_max) { - int T_j1 = std::round(temps_available[j+1]); - if (!contains(temps_to_read, T_j1)) { - temps_to_read.push_back(T_j1); - } - } + // Determine first available temperature below or equal to T_min + auto T_min_it = std::upper_bound(temps_available.begin(), temps_available.end(), T_min); + if (T_min_it != temps_available.begin()) --T_min_it; + + // Determine first available temperature above or equal to T_max + auto T_max_it = std::lower_bound(temps_available.begin(), temps_available.end(), T_max); + if (T_max_it != temps_available.end()) ++T_max_it; + + // Add corresponding temperatures to vector + for (auto it = T_min_it; it != T_max_it; ++it) { + temps_to_read.push_back(std::round(*it)); } } From d19512b454765518af3c6ae0c89828da029f274d Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 9 Mar 2021 13:31:53 -0600 Subject: [PATCH 32/68] Account for exceptions/segfaults in _run function --- openmc/executor.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openmc/executor.py b/openmc/executor.py index 3a3677226..008290c18 100644 --- a/openmc/executor.py +++ b/openmc/executor.py @@ -27,7 +27,12 @@ def _run(args, output, cwd): if p.returncode != 0: # Get error message from output and simplify whitespace output = ''.join(lines) - _, _, error_msg = output.partition('ERROR: ') + if 'ERROR: ' in output: + _, _, error_msg = output.partition('ERROR: ') + elif 'what()' in output: + _, _, error_msg = output.partition('what(): ') + else: + error_msg = 'OpenMC aborted unexpectedly.' error_msg = ' '.join(error_msg.split()) raise RuntimeError(error_msg) From 2cf90cb79fd88eab95f02ff8a01cf159fdabdc31 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 10 Mar 2021 13:10:15 +0000 Subject: [PATCH 33/68] documented Spack installation in quickinstall --- docs/source/quickinstall.rst | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index e45c552af..616a6eeae 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -95,6 +95,38 @@ This will take several minutes to run depending on your internet download speed. .. _options: https://docs.docker.com/engine/reference/commandline/run/ .. _mounting volumes: https://docs.docker.com/storage/volumes/ +---------------------------------- +Installing from Source using Spack +---------------------------------- + +`Spack`_ is a package management tool designed to support multiple versions and +configurations of software on a wide variety of platforms and environments. +Please following Spack's `setup guide`_ to configure the Spack system. + +To install the latest OpenMC with GNU 9.3.0 without MPI: + +.. code-block:: sh + + spack install openmc~mpi %gcc@9.3.0 + +To install the latest OpenMC with the GNU 9.3.0 compiler and OpenMPI: + +.. code-block:: sh + + spack install openmc+mpi ^openmpi %gcc@9.3.0 + +To install the Python API for OpenMC using GNU 9.3.0 compiler, OpenMPI and latest Python 3.7: + +.. code-block:: sh + + spack install py-openmc+mpi ^openmpi ^python@3.7.0:3.7.99 %gcc@9.3.0 + +Once installed, environment/lmod modules can be generated or Spack's `load` feature +can be used to access the installed packages. + +.. _Spack: https://spack.readthedocs.io/en/latest/ +.. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html + --------------------------------------- Installing from Source on Ubuntu 15.04+ --------------------------------------- From ae53953baee634f258fb754a0912970b34d27a45 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Wed, 10 Mar 2021 13:10:48 +0000 Subject: [PATCH 34/68] documented Spack installation in install docs --- docs/source/usersguide/install.rst | 300 +++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index ab2fe2f12..fa24149ae 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -74,6 +74,306 @@ are no longer supported. .. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto +---------------------------------- +Installing from Source using Spack +---------------------------------- + +`Spack`_ is a package management tool designed to support multiple versions and +configurations of software on a wide variety of platforms and environments. +Please following Spack's `setup guide`_ to configure the Spack system. + +The OpenMC Spack recipe has been configured with variants that match most options +provided in the CMakeLists.txt file. To see a list of these variants and other +information: + +.. code-block:: sh + + spack info openmc + + CMakePackage: openmc + + Description: + The OpenMC project aims to provide a fully-featured Monte Carlo particle + transport code based on modern methods. It is a constructive solid + geometry, continuous-energy transport code that uses ACE format cross + sections. The project started under the Computational Reactor Physics + Group at MIT. + + Homepage: http://openmc.readthedocs.io/ + + Tags: + None + + Preferred version: + 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 + + Safe versions: + develop [git] https://github.com/openmc-dev/openmc.git on branch develop + master [git] https://github.com/openmc-dev/openmc.git on branch master + 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 + 0.11.0 https://github.com/openmc-dev/openmc/tarball/v0.11.0 + 0.10.0 https://github.com/openmc-dev/openmc/tarball/v0.10.0 + + Variants: + Name [Default] Allowed values Description + =========================== ==================== ================================== + + build_type [RelWithDebInfo] Debug, Release, CMake build type + RelWithDebInfo, + MinSizeRel + debug [off] on, off Enable debug flags + ipo [off] on, off CMake interprocedural optimization + mpi [off] on, off Enable MPI support + openmp [on] on, off Enable OpenMP support + optimize [off] on, off Enable optimization flags + + Installation Phases: + cmake build install + + Build Dependencies: + cmake git hdf5 mpi + + Link Dependencies: + hdf5 mpi + + Run Dependencies: + None + + Virtual Packages: + None + +.. note:: + + It should be noted that by default OpenMC builds with ``-O2 -g`` flags which + are equivalent to a CMake build type of `RelwithDebInfo`. In addition, MPI + is OFF while OpenMP is ON. + +Assuming one had configured Spack with a GNU 9.3.0 compiler, to build OpenMC +with optimization, OpenMP support and OpenMPI, use: + +.. code-block:: sh + + spack install openmc+mpi+optimize+openmp ^openmpi %gcc@9.3.0 + +Although the OpenMP variant is by default ON, one can explicitly write it in +the Spack specification. If a user wanted OpenMC without any MPI or OpenMP support, +the variants can be deactivated: + +.. code-block:: sh + + spack install openmc~mpi+optimize~openmp + +The Python API for OpenMC can be installed in a similar fashion. Looking at the +information page presented with Spack: + +.. code-block:: sh + + spack info py-openmc + + PythonPackage: py-openmc + + Description: + The OpenMC project aims to provide a fully-featured Monte Carlo particle + transport code based on modern methods. It is a constructive solid + geometry, continuous-energy transport code that uses ACE format cross + sections. The project started under the Computational Reactor Physics + Group at MIT. + + Homepage: http://openmc.readthedocs.io/ + + Tags: + None + + Preferred version: + 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 + + Safe versions: + develop [git] https://github.com/openmc-dev/openmc.git on branch develop + master [git] https://github.com/openmc-dev/openmc.git on branch master + 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 + 0.11.0 https://github.com/openmc-dev/openmc/tarball/v0.11.0 + + Variants: + Name [Default] Allowed values Description + ============== ============== ================== + + mpi [off] on, off Enable MPI support + + Installation Phases: + build install + + Build Dependencies: + git openmc py-cython py-h5py py-ipython py-lxml py-matplotlib py-mpi4py py-numpy py-pandas py-scipy py-setuptools py-uncertainties python + + Link Dependencies: + python + + Run Dependencies: + openmc py-h5py py-ipython py-lxml py-matplotlib py-mpi4py py-numpy py-pandas py-scipy py-uncertainties python + + Virtual Packages: + None + +The only variant for the Python API is MPI. To configure a serial version of the Python API against a serial version of OpenMC: + +.. code-block:: + + spack install py-openmc + +.. caution:: + + When installing any Spack package, dependencies are assumed to be at configured defaults unless otherwise specfied in the + specification on the command line. In the above example, assuming the default options weren't changed in Spack's package + configuration, py-openmc will want to link against a non-optimized openmc. Even if you have a built an optimized openmc, + it will rebuild openmc with optimization OFF. Thus, if you are trying to link against dependencies that were configured + different than defaults, ^openmc[variants] will have to be present on command line. + +To build a parallel version of py-openmc that links against a parallel version of openmc that was used in the previous example: + +.. code-block:: + + spack install py-openmc+mpi ^openmc+optimize ^openmpi + +.. note:: + + If py-openmc is given the +mpi variant, it is automatically passed to openmc, no need to specify explicitly. + +.. tip:: + + When installing py-openmc it will use Spack's preferred Python. For example, assuming Spack's preferred Python + is 3.8.7, to build py-openmc against the latest Python 3.7 instead, ``^python@3.7.0:3.7.99`` should be added to the + specification on the command line. + +A useful tool in Spack is to look at the dependency tree before installation. This can be observed using +Spack's `spec` tool: + +.. code-block:: + + spack spec py-openmc+mpi ^openmc+optimize %gcc@9.3.0 + + Input spec + -------------------------------- + py-openmc+mpi + ^openmc%gcc@9.3.0+optimize + + Concretized + -------------------------------- + py-openmc@0.12.0%gcc@9.3.0+mpi arch=linux-centos7-x86_64 + ^git@2.29.0%gcc@9.3.0+svn~tcltk arch=linux-centos7-x86_64 + ^autoconf@2.69%gcc@9.3.0 arch=linux-centos7-x86_64 + ^m4@1.4.18%gcc@9.3.0+sigsegv patches=3877ab548f88597ab2327a2230ee048d2d07ace1062efe81fc92e91b7f39cd00,fc9b61654a3ba1a8d6cd78ce087e7c96366c290bc8d2c299f09828d793b853c8 arch=linux-centos7-x86_64 + ^libsigsegv@2.12%gcc@9.3.0 arch=linux-centos7-x86_64 + ^perl@5.32.0%gcc@9.3.0+cpanm+shared+threads arch=linux-centos7-x86_64 + ^berkeley-db@18.1.40%gcc@9.3.0 arch=linux-centos7-x86_64 + ^gdbm@1.18.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^readline@8.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^ncurses@6.2%gcc@9.3.0~symlinks+termlib arch=linux-centos7-x86_64 + ^pkgconf@1.7.3%gcc@9.3.0 arch=linux-centos7-x86_64 + ^automake@1.16.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^curl@7.72.0%gcc@9.3.0~darwinssl~gssapi~libssh~libssh2~nghttp2 arch=linux-centos7-x86_64 + ^libidn2@2.3.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libunistring@0.9.10%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libiconv@1.16%gcc@9.3.0 arch=linux-centos7-x86_64 + ^openssl@1.1.1h%gcc@9.3.0+systemcerts arch=linux-centos7-x86_64 + ^zlib@1.2.11%gcc@9.3.0+optimize+pic+shared arch=linux-centos7-x86_64 + ^expat@2.2.10%gcc@9.3.0+libbsd arch=linux-centos7-x86_64 + ^libbsd@0.10.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^gettext@0.21%gcc@9.3.0+bzip2+curses+git~libunistring+libxml2+tar+xz arch=linux-centos7-x86_64 + ^bzip2@1.0.8%gcc@9.3.0+shared arch=linux-centos7-x86_64 + ^diffutils@3.7%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libxml2@2.9.10%gcc@9.3.0~python arch=linux-centos7-x86_64 + ^xz@5.2.5%gcc@9.3.0~pic arch=linux-centos7-x86_64 + ^tar@1.32%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libtool@2.4.6%gcc@9.3.0 arch=linux-centos7-x86_64 + ^openssh@8.4p1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libedit@3.1-20191231%gcc@9.3.0 arch=linux-centos7-x86_64 + ^pcre2@10.35%gcc@9.3.0~jit+multibyte arch=linux-centos7-x86_64 + ^perl-alien-svn@1.8.11.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^apr@1.6.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^apr-util@1.6.1%gcc@9.3.0+crypto~gdbm~odbc~pgsql~sqlite arch=linux-centos7-x86_64 + ^perl-module-build@0.4224%gcc@9.3.0 arch=linux-centos7-x86_64 + ^sqlite@3.33.0%gcc@9.3.0+column_metadata+fts~functions~rtree arch=linux-centos7-x86_64 + ^openmc@0.12.0%gcc@9.3.0~debug~ipo+mpi+openmp+optimize build_type=Release arch=linux-centos7-x86_64 + ^cmake@3.18.4%gcc@9.3.0~doc+ncurses+openssl+ownlibs~qt patches=bf695e3febb222da2ed94b3beea600650e4318975da90e4a71d6f31a6d5d8c3d arch=linux-centos7-x86_64 + ^hdf5@1.10.7%gcc@9.3.0+cxx~debug+fortran+hl+java+mpi+pic+shared+szip+threadsafe api=none arch=linux-centos7-x86_64 + ^libszip@2.1.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^numactl@2.0.14%gcc@9.3.0 patches=4e1d78cbbb85de625bad28705e748856033eaafab92a66dffd383a3d7e00cc94 arch=linux-centos7-x86_64 + ^openjdk@11.0.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^openmpi@3.1.6%gcc@9.3.0~atomics~cuda~cxx~cxx_exceptions+gpfs~java~legacylaunchers~lustre~memchecker~pmi~singularity~sqlite3+static+thread_multiple+vt+wrapper-rpath fabrics=ofi,verbs schedulers=none arch=linux-centos7-x86_64 + ^hwloc@1.11.11%gcc@9.3.0~cairo~cuda~gl~libudev+libxml2~netloc~nvml+pci+shared arch=linux-centos7-x86_64 + ^libpciaccess@0.16%gcc@9.3.0 arch=linux-centos7-x86_64 + ^util-macros@1.19.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libfabric@1.11.0%gcc@9.3.0~kdreg fabrics=shm,tcp,udp,verbs arch=linux-centos7-x86_64 + ^rdma-core@20%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 + ^libnl@3.3.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^bison@3.6.4%gcc@9.3.0 arch=linux-centos7-x86_64 + ^help2man@1.47.11%gcc@9.3.0 arch=linux-centos7-x86_64 + ^flex@2.6.4%gcc@9.3.0+lex patches=09c22e5c6fef327d3e48eb23f0d610dcd3a35ab9207f12e0f875701c677978d3 arch=linux-centos7-x86_64 + ^findutils@4.6.0%gcc@9.3.0 patches=84b916c0bf8c51b7e7b28417692f0ad3e7030d1f3c248ba77c42ede5c1c5d11e,bd9e4e5cc280f9753ae14956c4e4aa17fe7a210f55dd6c84aa60b12d106d47a2 arch=linux-centos7-x86_64 + ^texinfo@6.5%gcc@9.3.0 patches=12f6edb0c6b270b8c8dba2ce17998c580db01182d871ee32b7b6e4129bd1d23a,1732115f651cff98989cb0215d8f64da5e0f7911ebf0c13b064920f088f2ffe1 arch=linux-centos7-x86_64 + ^py-docutils@0.15.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-setuptools@50.3.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^python@3.8.7%gcc@9.3.0+bz2+ctypes+dbm~debug+libxml2+lzma~nis~optimizations+pic+pyexpat+pythoncmd+readline+shared+sqlite3+ssl~tix~tkinter~ucs4+uuid+zlib patches=0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87 arch=linux-centos7-x86_64 + ^libffi@3.3%gcc@9.3.0 patches=26f26c6f29a7ce9bf370ad3ab2610f99365b4bdd7b82e7c31df41a3370d685c0 arch=linux-centos7-x86_64 + ^uuid@1.6.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-cython@0.29.21%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-h5py@2.10.0%gcc@9.3.0+mpi arch=linux-centos7-x86_64 + ^py-mpi4py@3.0.3%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-numpy@1.19.4%gcc@9.3.0+blas+lapack arch=linux-centos7-x86_64 + ^openblas@0.3.12%gcc@9.3.0~consistent_fpcsr~ilp64+pic+shared threads=none arch=linux-centos7-x86_64 + ^py-pkgconfig@1.5.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-nose@1.3.7%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-six@1.14.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-ipython@7.18.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-backcall@0.1.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-decorator@4.4.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-jedi@0.13.3%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-parso@0.6.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pexpect@4.7.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-ptyprocess@0.6.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pickleshare@0.7.5%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-prompt-toolkit@2.0.9%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-wcwidth@0.1.7%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pygments@2.6.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-traitlets@5.0.4%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-ipython-genutils@0.2.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-lxml@4.5.2%gcc@9.3.0~cssselect~html5~htmlsoup arch=linux-centos7-x86_64 + ^libxslt@1.1.33%gcc@9.3.0+crypto~python arch=linux-centos7-x86_64 + ^libgcrypt@1.8.5%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libgpg-error@1.37%gcc@9.3.0 arch=linux-centos7-x86_64 + ^gawk@5.0.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^gmp@6.1.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^mpfr@4.0.2%gcc@9.3.0 patches=3f80b836948aa96f8d1cb9cc7f3f55973f19285482a96f9a4e1623d460bcccf0 arch=linux-centos7-x86_64 + ^autoconf-archive@2019.01.06%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-matplotlib@3.3.3%gcc@9.3.0~animation~fonts+image~latex~movies backend=agg arch=linux-centos7-x86_64 + ^freetype@2.10.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^libpng@1.6.37%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-certifi@2020.6.20%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-cycler@0.10.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-kiwisolver@1.1.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pillow@7.2.0%gcc@9.3.0~freetype~imagequant+jpeg~jpeg2000~lcms~tiff~webp~webpmux~xcb+zlib arch=linux-centos7-x86_64 + ^libjpeg-turbo@2.0.4%gcc@9.3.0 arch=linux-centos7-x86_64 + ^nasm@2.15.05%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pyparsing@2.4.7%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-python-dateutil@2.8.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-setuptools-scm@4.1.2%gcc@9.3.0+toml arch=linux-centos7-x86_64 + ^py-toml@0.10.2%gcc@9.3.0 arch=linux-centos7-x86_64 + ^qhull@2020.1%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 + ^py-pandas@1.1.4%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-bottleneck@1.2.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-numexpr@2.7.0%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pytz@2020.1%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-scipy@1.5.4%gcc@9.3.0 arch=linux-centos7-x86_64 + ^py-pybind11@2.5.0%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 + ^py-uncertainties@3.1.4%gcc@9.3.0~docs~optional arch=linux-centos7-x86_64 + ^py-future@0.18.2%gcc@9.3.0 arch=linux-centos7-x86_64 + +Once installed, environment/lmod modules can be generated or Spack's `load` feature +can be used to access the installed packages. + +.. _Spack: https://spack.readthedocs.io/en/latest/ +.. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html + .. _install_source: ---------------------- From d120785bf629d744ad4635fe2d0010fcb84bd0d4 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Wed, 10 Mar 2021 17:03:22 -0500 Subject: [PATCH 35/68] pre c99 compatible endf.c parsing --- openmc/data/endf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openmc/data/endf.c b/openmc/data/endf.c index 6e9c1f5b4..936fd3bbb 100644 --- a/openmc/data/endf.c +++ b/openmc/data/endf.c @@ -22,7 +22,8 @@ double cfloat_endf(const char* buffer, int n) // limit n to 11 characters n = n > 11 ? 11 : n; - for (int i = 0; i < n; ++i) { + int i; + for (i = 0; i < n; ++i) { char c = buffer[i]; // Skip whitespace characters From 8589d39d8eaad83ce9a856d81b42245d57bb1e18 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Thu, 11 Mar 2021 09:37:06 -0500 Subject: [PATCH 36/68] improve clarity of phdf5 error message --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3e0ff23cb..8c92751e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,9 @@ endif() find_package(HDF5 REQUIRED COMPONENTS C HL) if(HDF5_IS_PARALLEL) if(NOT MPI_ENABLED) - message(FATAL_ERROR "Parallel HDF5 must be used with MPI.") + message(FATAL_ERROR "Parallel HDF5 was detected, but the detected compiler,\ + ${CMAKE_CXX_COMPILER}, does not support MPI. An MPI-capable compiler must \ + be used with parallel HDF5.") endif() message(STATUS "Using parallel HDF5") endif() From 1ee9f3e79f486d5a6a032482f30f586e5c6003a0 Mon Sep 17 00:00:00 2001 From: Bryan Herman <63422190+bryanherman@users.noreply.github.com> Date: Fri, 12 Mar 2021 07:12:29 -0500 Subject: [PATCH 37/68] Update docs/source/usersguide/install.rst fixed reference link syntax Co-authored-by: Paul Romano --- docs/source/usersguide/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index fa24149ae..0c03391d5 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -78,7 +78,7 @@ are no longer supported. Installing from Source using Spack ---------------------------------- -`Spack`_ is a package management tool designed to support multiple versions and +Spack_ is a package management tool designed to support multiple versions and configurations of software on a wide variety of platforms and environments. Please following Spack's `setup guide`_ to configure the Spack system. From 80685b44961683cc445b8f6a52825578a7bfbd16 Mon Sep 17 00:00:00 2001 From: Bryan Herman <63422190+bryanherman@users.noreply.github.com> Date: Fri, 12 Mar 2021 07:12:56 -0500 Subject: [PATCH 38/68] Update docs/source/usersguide/install.rst fixed grammar Co-authored-by: Paul Romano --- docs/source/usersguide/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 0c03391d5..a0d12976f 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -236,7 +236,7 @@ To build a parallel version of py-openmc that links against a parallel version o .. note:: - If py-openmc is given the +mpi variant, it is automatically passed to openmc, no need to specify explicitly. + If py-openmc is given the +mpi variant, it is automatically passed to openmc so there is no need to specify it explicitly. .. tip:: From 4b6f69d0a995a660dcc05882373053a729be627b Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Mar 2021 12:29:54 +0000 Subject: [PATCH 39/68] addressed comments in quick install --- docs/source/quickinstall.rst | 20 +++++--------------- docs/source/usersguide/install.rst | 2 ++ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 616a6eeae..e2010a788 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -101,26 +101,16 @@ Installing from Source using Spack `Spack`_ is a package management tool designed to support multiple versions and configurations of software on a wide variety of platforms and environments. -Please following Spack's `setup guide`_ to configure the Spack system. +Please follow Spack's `setup guide`_ to configure the Spack system. -To install the latest OpenMC with GNU 9.3.0 without MPI: +To install the latest OpenMC with the Python API, use the following command: .. code-block:: sh - spack install openmc~mpi %gcc@9.3.0 - -To install the latest OpenMC with the GNU 9.3.0 compiler and OpenMPI: - -.. code-block:: sh - - spack install openmc+mpi ^openmpi %gcc@9.3.0 - -To install the Python API for OpenMC using GNU 9.3.0 compiler, OpenMPI and latest Python 3.7: - -.. code-block:: sh - - spack install py-openmc+mpi ^openmpi ^python@3.7.0:3.7.99 %gcc@9.3.0 + spack install py-openmc +For more information about customizations including MPI, see the +:ref:`detailed installation instructions using Spack `. Once installed, environment/lmod modules can be generated or Spack's `load` feature can be used to access the installed packages. diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index a0d12976f..13bbc1ad4 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -74,6 +74,8 @@ are no longer supported. .. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto +.. _install-spack: + ---------------------------------- Installing from Source using Spack ---------------------------------- From eae122675ddadcfd546ed7c81b141edc5d2b2ad2 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Mar 2021 12:38:15 +0000 Subject: [PATCH 40/68] fixed additional typo in quickinstall --- docs/source/quickinstall.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index e2010a788..7906206db 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -140,7 +140,7 @@ All OpenMC source code is hosted on `GitHub `_. If you have `git `_, the `gcc `_ compiler suite, `CMake `_, and `HDF5 `_ -installed, you can download and install OpenMC be entering the following +installed, you can download and install OpenMC by entering the following commands in a terminal: .. code-block:: sh From 879da99e2e02c20fa3cf7a6d909fd1e1b39d5c77 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Mar 2021 12:43:44 +0000 Subject: [PATCH 41/68] removed spack command output in install docs --- docs/source/usersguide/install.rst | 213 ----------------------------- 1 file changed, 213 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 13bbc1ad4..abd8a4a0a 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -92,58 +92,6 @@ information: spack info openmc - CMakePackage: openmc - - Description: - The OpenMC project aims to provide a fully-featured Monte Carlo particle - transport code based on modern methods. It is a constructive solid - geometry, continuous-energy transport code that uses ACE format cross - sections. The project started under the Computational Reactor Physics - Group at MIT. - - Homepage: http://openmc.readthedocs.io/ - - Tags: - None - - Preferred version: - 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 - - Safe versions: - develop [git] https://github.com/openmc-dev/openmc.git on branch develop - master [git] https://github.com/openmc-dev/openmc.git on branch master - 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 - 0.11.0 https://github.com/openmc-dev/openmc/tarball/v0.11.0 - 0.10.0 https://github.com/openmc-dev/openmc/tarball/v0.10.0 - - Variants: - Name [Default] Allowed values Description - =========================== ==================== ================================== - - build_type [RelWithDebInfo] Debug, Release, CMake build type - RelWithDebInfo, - MinSizeRel - debug [off] on, off Enable debug flags - ipo [off] on, off CMake interprocedural optimization - mpi [off] on, off Enable MPI support - openmp [on] on, off Enable OpenMP support - optimize [off] on, off Enable optimization flags - - Installation Phases: - cmake build install - - Build Dependencies: - cmake git hdf5 mpi - - Link Dependencies: - hdf5 mpi - - Run Dependencies: - None - - Virtual Packages: - None - .. note:: It should be noted that by default OpenMC builds with ``-O2 -g`` flags which @@ -172,50 +120,6 @@ information page presented with Spack: spack info py-openmc - PythonPackage: py-openmc - - Description: - The OpenMC project aims to provide a fully-featured Monte Carlo particle - transport code based on modern methods. It is a constructive solid - geometry, continuous-energy transport code that uses ACE format cross - sections. The project started under the Computational Reactor Physics - Group at MIT. - - Homepage: http://openmc.readthedocs.io/ - - Tags: - None - - Preferred version: - 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 - - Safe versions: - develop [git] https://github.com/openmc-dev/openmc.git on branch develop - master [git] https://github.com/openmc-dev/openmc.git on branch master - 0.12.0 [git] https://github.com/openmc-dev/openmc.git at tag v0.12.0 - 0.11.0 https://github.com/openmc-dev/openmc/tarball/v0.11.0 - - Variants: - Name [Default] Allowed values Description - ============== ============== ================== - - mpi [off] on, off Enable MPI support - - Installation Phases: - build install - - Build Dependencies: - git openmc py-cython py-h5py py-ipython py-lxml py-matplotlib py-mpi4py py-numpy py-pandas py-scipy py-setuptools py-uncertainties python - - Link Dependencies: - python - - Run Dependencies: - openmc py-h5py py-ipython py-lxml py-matplotlib py-mpi4py py-numpy py-pandas py-scipy py-uncertainties python - - Virtual Packages: - None - The only variant for the Python API is MPI. To configure a serial version of the Python API against a serial version of OpenMC: .. code-block:: @@ -253,123 +157,6 @@ Spack's `spec` tool: spack spec py-openmc+mpi ^openmc+optimize %gcc@9.3.0 - Input spec - -------------------------------- - py-openmc+mpi - ^openmc%gcc@9.3.0+optimize - - Concretized - -------------------------------- - py-openmc@0.12.0%gcc@9.3.0+mpi arch=linux-centos7-x86_64 - ^git@2.29.0%gcc@9.3.0+svn~tcltk arch=linux-centos7-x86_64 - ^autoconf@2.69%gcc@9.3.0 arch=linux-centos7-x86_64 - ^m4@1.4.18%gcc@9.3.0+sigsegv patches=3877ab548f88597ab2327a2230ee048d2d07ace1062efe81fc92e91b7f39cd00,fc9b61654a3ba1a8d6cd78ce087e7c96366c290bc8d2c299f09828d793b853c8 arch=linux-centos7-x86_64 - ^libsigsegv@2.12%gcc@9.3.0 arch=linux-centos7-x86_64 - ^perl@5.32.0%gcc@9.3.0+cpanm+shared+threads arch=linux-centos7-x86_64 - ^berkeley-db@18.1.40%gcc@9.3.0 arch=linux-centos7-x86_64 - ^gdbm@1.18.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^readline@8.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^ncurses@6.2%gcc@9.3.0~symlinks+termlib arch=linux-centos7-x86_64 - ^pkgconf@1.7.3%gcc@9.3.0 arch=linux-centos7-x86_64 - ^automake@1.16.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^curl@7.72.0%gcc@9.3.0~darwinssl~gssapi~libssh~libssh2~nghttp2 arch=linux-centos7-x86_64 - ^libidn2@2.3.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libunistring@0.9.10%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libiconv@1.16%gcc@9.3.0 arch=linux-centos7-x86_64 - ^openssl@1.1.1h%gcc@9.3.0+systemcerts arch=linux-centos7-x86_64 - ^zlib@1.2.11%gcc@9.3.0+optimize+pic+shared arch=linux-centos7-x86_64 - ^expat@2.2.10%gcc@9.3.0+libbsd arch=linux-centos7-x86_64 - ^libbsd@0.10.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^gettext@0.21%gcc@9.3.0+bzip2+curses+git~libunistring+libxml2+tar+xz arch=linux-centos7-x86_64 - ^bzip2@1.0.8%gcc@9.3.0+shared arch=linux-centos7-x86_64 - ^diffutils@3.7%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libxml2@2.9.10%gcc@9.3.0~python arch=linux-centos7-x86_64 - ^xz@5.2.5%gcc@9.3.0~pic arch=linux-centos7-x86_64 - ^tar@1.32%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libtool@2.4.6%gcc@9.3.0 arch=linux-centos7-x86_64 - ^openssh@8.4p1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libedit@3.1-20191231%gcc@9.3.0 arch=linux-centos7-x86_64 - ^pcre2@10.35%gcc@9.3.0~jit+multibyte arch=linux-centos7-x86_64 - ^perl-alien-svn@1.8.11.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^apr@1.6.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^apr-util@1.6.1%gcc@9.3.0+crypto~gdbm~odbc~pgsql~sqlite arch=linux-centos7-x86_64 - ^perl-module-build@0.4224%gcc@9.3.0 arch=linux-centos7-x86_64 - ^sqlite@3.33.0%gcc@9.3.0+column_metadata+fts~functions~rtree arch=linux-centos7-x86_64 - ^openmc@0.12.0%gcc@9.3.0~debug~ipo+mpi+openmp+optimize build_type=Release arch=linux-centos7-x86_64 - ^cmake@3.18.4%gcc@9.3.0~doc+ncurses+openssl+ownlibs~qt patches=bf695e3febb222da2ed94b3beea600650e4318975da90e4a71d6f31a6d5d8c3d arch=linux-centos7-x86_64 - ^hdf5@1.10.7%gcc@9.3.0+cxx~debug+fortran+hl+java+mpi+pic+shared+szip+threadsafe api=none arch=linux-centos7-x86_64 - ^libszip@2.1.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^numactl@2.0.14%gcc@9.3.0 patches=4e1d78cbbb85de625bad28705e748856033eaafab92a66dffd383a3d7e00cc94 arch=linux-centos7-x86_64 - ^openjdk@11.0.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^openmpi@3.1.6%gcc@9.3.0~atomics~cuda~cxx~cxx_exceptions+gpfs~java~legacylaunchers~lustre~memchecker~pmi~singularity~sqlite3+static+thread_multiple+vt+wrapper-rpath fabrics=ofi,verbs schedulers=none arch=linux-centos7-x86_64 - ^hwloc@1.11.11%gcc@9.3.0~cairo~cuda~gl~libudev+libxml2~netloc~nvml+pci+shared arch=linux-centos7-x86_64 - ^libpciaccess@0.16%gcc@9.3.0 arch=linux-centos7-x86_64 - ^util-macros@1.19.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libfabric@1.11.0%gcc@9.3.0~kdreg fabrics=shm,tcp,udp,verbs arch=linux-centos7-x86_64 - ^rdma-core@20%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 - ^libnl@3.3.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^bison@3.6.4%gcc@9.3.0 arch=linux-centos7-x86_64 - ^help2man@1.47.11%gcc@9.3.0 arch=linux-centos7-x86_64 - ^flex@2.6.4%gcc@9.3.0+lex patches=09c22e5c6fef327d3e48eb23f0d610dcd3a35ab9207f12e0f875701c677978d3 arch=linux-centos7-x86_64 - ^findutils@4.6.0%gcc@9.3.0 patches=84b916c0bf8c51b7e7b28417692f0ad3e7030d1f3c248ba77c42ede5c1c5d11e,bd9e4e5cc280f9753ae14956c4e4aa17fe7a210f55dd6c84aa60b12d106d47a2 arch=linux-centos7-x86_64 - ^texinfo@6.5%gcc@9.3.0 patches=12f6edb0c6b270b8c8dba2ce17998c580db01182d871ee32b7b6e4129bd1d23a,1732115f651cff98989cb0215d8f64da5e0f7911ebf0c13b064920f088f2ffe1 arch=linux-centos7-x86_64 - ^py-docutils@0.15.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-setuptools@50.3.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^python@3.8.7%gcc@9.3.0+bz2+ctypes+dbm~debug+libxml2+lzma~nis~optimizations+pic+pyexpat+pythoncmd+readline+shared+sqlite3+ssl~tix~tkinter~ucs4+uuid+zlib patches=0d98e93189bc278fbc37a50ed7f183bd8aaf249a8e1670a465f0db6bb4f8cf87 arch=linux-centos7-x86_64 - ^libffi@3.3%gcc@9.3.0 patches=26f26c6f29a7ce9bf370ad3ab2610f99365b4bdd7b82e7c31df41a3370d685c0 arch=linux-centos7-x86_64 - ^uuid@1.6.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-cython@0.29.21%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-h5py@2.10.0%gcc@9.3.0+mpi arch=linux-centos7-x86_64 - ^py-mpi4py@3.0.3%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-numpy@1.19.4%gcc@9.3.0+blas+lapack arch=linux-centos7-x86_64 - ^openblas@0.3.12%gcc@9.3.0~consistent_fpcsr~ilp64+pic+shared threads=none arch=linux-centos7-x86_64 - ^py-pkgconfig@1.5.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-nose@1.3.7%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-six@1.14.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-ipython@7.18.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-backcall@0.1.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-decorator@4.4.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-jedi@0.13.3%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-parso@0.6.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pexpect@4.7.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-ptyprocess@0.6.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pickleshare@0.7.5%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-prompt-toolkit@2.0.9%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-wcwidth@0.1.7%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pygments@2.6.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-traitlets@5.0.4%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-ipython-genutils@0.2.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-lxml@4.5.2%gcc@9.3.0~cssselect~html5~htmlsoup arch=linux-centos7-x86_64 - ^libxslt@1.1.33%gcc@9.3.0+crypto~python arch=linux-centos7-x86_64 - ^libgcrypt@1.8.5%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libgpg-error@1.37%gcc@9.3.0 arch=linux-centos7-x86_64 - ^gawk@5.0.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^gmp@6.1.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^mpfr@4.0.2%gcc@9.3.0 patches=3f80b836948aa96f8d1cb9cc7f3f55973f19285482a96f9a4e1623d460bcccf0 arch=linux-centos7-x86_64 - ^autoconf-archive@2019.01.06%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-matplotlib@3.3.3%gcc@9.3.0~animation~fonts+image~latex~movies backend=agg arch=linux-centos7-x86_64 - ^freetype@2.10.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^libpng@1.6.37%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-certifi@2020.6.20%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-cycler@0.10.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-kiwisolver@1.1.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pillow@7.2.0%gcc@9.3.0~freetype~imagequant+jpeg~jpeg2000~lcms~tiff~webp~webpmux~xcb+zlib arch=linux-centos7-x86_64 - ^libjpeg-turbo@2.0.4%gcc@9.3.0 arch=linux-centos7-x86_64 - ^nasm@2.15.05%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pyparsing@2.4.7%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-python-dateutil@2.8.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-setuptools-scm@4.1.2%gcc@9.3.0+toml arch=linux-centos7-x86_64 - ^py-toml@0.10.2%gcc@9.3.0 arch=linux-centos7-x86_64 - ^qhull@2020.1%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 - ^py-pandas@1.1.4%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-bottleneck@1.2.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-numexpr@2.7.0%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pytz@2020.1%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-scipy@1.5.4%gcc@9.3.0 arch=linux-centos7-x86_64 - ^py-pybind11@2.5.0%gcc@9.3.0~ipo build_type=Release arch=linux-centos7-x86_64 - ^py-uncertainties@3.1.4%gcc@9.3.0~docs~optional arch=linux-centos7-x86_64 - ^py-future@0.18.2%gcc@9.3.0 arch=linux-centos7-x86_64 - Once installed, environment/lmod modules can be generated or Spack's `load` feature can be used to access the installed packages. From c3aeb41057cff1eca42e30f41a53ab40909e9f77 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Mar 2021 13:25:34 +0000 Subject: [PATCH 42/68] addressed comments in install docs --- docs/source/usersguide/install.rst | 47 ++++++++++++------------------ 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index abd8a4a0a..274d4c66a 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -82,11 +82,11 @@ Installing from Source using Spack Spack_ is a package management tool designed to support multiple versions and configurations of software on a wide variety of platforms and environments. -Please following Spack's `setup guide`_ to configure the Spack system. +Please follow Spack's `setup guide`_ to configure the Spack system. The OpenMC Spack recipe has been configured with variants that match most options provided in the CMakeLists.txt file. To see a list of these variants and other -information: +information use: .. code-block:: sh @@ -98,29 +98,18 @@ information: are equivalent to a CMake build type of `RelwithDebInfo`. In addition, MPI is OFF while OpenMP is ON. -Assuming one had configured Spack with a GNU 9.3.0 compiler, to build OpenMC -with optimization, OpenMP support and OpenMPI, use: - -.. code-block:: sh - - spack install openmc+mpi+optimize+openmp ^openmpi %gcc@9.3.0 - -Although the OpenMP variant is by default ON, one can explicitly write it in -the Spack specification. If a user wanted OpenMC without any MPI or OpenMP support, -the variants can be deactivated: - -.. code-block:: sh - - spack install openmc~mpi+optimize~openmp - -The Python API for OpenMC can be installed in a similar fashion. Looking at the -information page presented with Spack: +It is recommended to install OpenMC with the Python API. Information about this +Spack recipe can be found with the following command: .. code-block:: sh spack info py-openmc -The only variant for the Python API is MPI. To configure a serial version of the Python API against a serial version of OpenMC: +.. note:: + + The only variant for the Python API is ``mpi``. + +The most basic installation of OpenMC can be accomplished by entering the following command: .. code-block:: @@ -130,32 +119,34 @@ The only variant for the Python API is MPI. To configure a serial version of the When installing any Spack package, dependencies are assumed to be at configured defaults unless otherwise specfied in the specification on the command line. In the above example, assuming the default options weren't changed in Spack's package - configuration, py-openmc will want to link against a non-optimized openmc. Even if you have a built an optimized openmc, + configuration, py-openmc will link against a non-optimized non-MPI openmc. Even if an optimized openmc was built separately, it will rebuild openmc with optimization OFF. Thus, if you are trying to link against dependencies that were configured - different than defaults, ^openmc[variants] will have to be present on command line. + different than defaults, ``^openmc[variants]`` will have to be present in the command. -To build a parallel version of py-openmc that links against a parallel version of openmc that was used in the previous example: +For a more performant build of OpenMC with optimization turned ON and MPI provided by OpenMPI, the following command can be +used: -.. code-block:: +.. code-block:: sh spack install py-openmc+mpi ^openmc+optimize ^openmpi .. note:: - If py-openmc is given the +mpi variant, it is automatically passed to openmc so there is no need to specify it explicitly. + ``+mpi`` is automatically forwarded to OpenMC. .. tip:: - When installing py-openmc it will use Spack's preferred Python. For example, assuming Spack's preferred Python + When installing py-openmc, it will use Spack's preferred Python. For example, assuming Spack's preferred Python is 3.8.7, to build py-openmc against the latest Python 3.7 instead, ``^python@3.7.0:3.7.99`` should be added to the - specification on the command line. + specification on the command line. Additionally, a compiler type and version can be specified at the end of the + command using ``%gcc@``, ``%intel@``, etc. A useful tool in Spack is to look at the dependency tree before installation. This can be observed using Spack's `spec` tool: .. code-block:: - spack spec py-openmc+mpi ^openmc+optimize %gcc@9.3.0 + spack spec py-openmc+mpi ^openmc+optimize Once installed, environment/lmod modules can be generated or Spack's `load` feature can be used to access the installed packages. From 8ee728c51100e6d0c7843b29148c7804089a0628 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 12 Mar 2021 07:26:44 -0600 Subject: [PATCH 43/68] Prevent pugixml and fmt from being found via PATH --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c92751e2..9694d60b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,13 +54,13 @@ endif() #=============================================================================== # If not found, we just pull appropriate versions from github and build them. -find_package(fmt QUIET) +find_package(fmt QUIET NO_SYSTEM_ENVIRONMENT_PATH) if(fmt_FOUND) message(STATUS "Found fmt: ${fmt_DIR} (version ${fmt_VERSION})") else() message(STATUS "Did not find fmt, will use submodule instead") endif() -find_package(pugixml QUIET) +find_package(pugixml QUIET NO_SYSTEM_ENVIRONMENT_PATH) if(pugixml_FOUND) message(STATUS "Found pugixml: ${pugixml_DIR}") else() From 7e46bb8cde1c50e935f2268b476095eea546baa8 Mon Sep 17 00:00:00 2001 From: Bryan Herman Date: Fri, 12 Mar 2021 14:03:02 +0000 Subject: [PATCH 44/68] fixed Spack hyperlink syntax --- docs/source/quickinstall.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 7906206db..2b815734a 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -99,7 +99,7 @@ This will take several minutes to run depending on your internet download speed. Installing from Source using Spack ---------------------------------- -`Spack`_ is a package management tool designed to support multiple versions and +Spack_ is a package management tool designed to support multiple versions and configurations of software on a wide variety of platforms and environments. Please follow Spack's `setup guide`_ to configure the Spack system. From 0ef1f959783a7dee2a4f4de50ad738591dbb12bf Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Mar 2021 08:02:53 -0600 Subject: [PATCH 45/68] Fix broken links, use https over http, replace redirects --- docs/source/devguide/styleguide.rst | 2 +- docs/source/devguide/tests.rst | 4 +-- docs/source/devguide/user-input.rst | 6 ++-- docs/source/devguide/workflow.rst | 8 +++--- docs/source/index.rst | 4 +-- docs/source/io_formats/data_wmp.rst | 2 +- docs/source/io_formats/mgxs_library.rst | 2 +- docs/source/methods/cross_sections.rst | 8 +++--- docs/source/methods/geometry.rst | 2 +- docs/source/methods/neutron_physics.rst | 14 +++++----- docs/source/methods/parallelization.rst | 20 ++++++------- docs/source/methods/photon_physics.rst | 2 +- docs/source/pythonapi/index.rst | 6 ++-- docs/source/quickinstall.rst | 18 ++++++------ docs/source/usersguide/beginners.rst | 9 +++--- docs/source/usersguide/cross_sections.rst | 2 +- docs/source/usersguide/geometry.rst | 2 +- docs/source/usersguide/install.rst | 34 +++++++++++------------ docs/source/usersguide/materials.rst | 22 +++++++-------- docs/source/usersguide/parallel.rst | 6 ++-- docs/source/usersguide/plots.rst | 4 +-- docs/source/usersguide/processing.rst | 2 +- docs/source/usersguide/scripts.rst | 6 ++-- examples/jupyter/cad-based-geometry.ipynb | 6 ++-- examples/jupyter/mdgxs-part-i.ipynb | 6 ++-- examples/jupyter/mdgxs-part-ii.ipynb | 2 +- examples/jupyter/mg-mode-part-i.ipynb | 4 +-- examples/jupyter/mgxs-part-i.ipynb | 6 ++-- examples/jupyter/mgxs-part-ii.ipynb | 8 +++--- examples/jupyter/mgxs-part-iii.ipynb | 6 ++-- examples/jupyter/nuclear-data.ipynb | 2 +- examples/jupyter/post-processing.ipynb | 2 +- examples/jupyter/search.ipynb | 2 +- 33 files changed, 115 insertions(+), 114 deletions(-) diff --git a/docs/source/devguide/styleguide.rst b/docs/source/devguide/styleguide.rst index a46744a3f..bfe2ac35f 100644 --- a/docs/source/devguide/styleguide.rst +++ b/docs/source/devguide/styleguide.rst @@ -215,7 +215,7 @@ Documentation ------------- Classes, structs, and functions are to be annotated for the `Doxygen -`_ documentation generation tool. Use the ``\`` form of +`_ documentation generation tool. Use the ``\`` form of Doxygen commands, e.g., ``\brief`` instead of ``@brief``. ------ diff --git a/docs/source/devguide/tests.rst b/docs/source/devguide/tests.rst index b26f5c762..d55713062 100644 --- a/docs/source/devguide/tests.rst +++ b/docs/source/devguide/tests.rst @@ -14,7 +14,7 @@ functions/classes in the OpenMC Python API. Prerequisites ------------- -- The test suite relies on the third-party `pytest `_ +- The test suite relies on the third-party `pytest `_ package. To run either or both the regression and unit test suites, it is assumed that you have OpenMC fully installed, i.e., the :ref:`scripts_openmc` executable is available on your :envvar:`PATH` and the :mod:`openmc` Python @@ -46,7 +46,7 @@ To execute the test suite, go to the ``tests/`` directory and run:: pytest If you want to collect information about source line coverage in the Python API, -you must have the `pytest-cov `_ plugin +you must have the `pytest-cov `_ plugin installed and run:: pytest --cov=../openmc --cov-report=html diff --git a/docs/source/devguide/user-input.rst b/docs/source/devguide/user-input.rst index db0bcdad8..0bde0fb05 100644 --- a/docs/source/devguide/user-input.rst +++ b/docs/source/devguide/user-input.rst @@ -65,7 +65,7 @@ developer or send a message to the `developers mailing list`_. .. _property attribute: https://docs.python.org/3.6/library/functions.html#property .. _XML Schema Part 2: http://www.w3.org/TR/xmlschema-2/ .. _boolean: http://www.w3.org/TR/xmlschema-2/#boolean -.. _RELAX NG: http://relaxng.org/ -.. _compact syntax: http://relaxng.org/compact-tutorial-20030326.html -.. _trang: http://www.thaiopensource.com/relaxng/trang.html +.. _RELAX NG: https://relaxng.org/ +.. _compact syntax: https://relaxng.org/compact-tutorial-20030326.html +.. _trang: https://relaxng.org/jclark/trang.html .. _developers mailing list: https://groups.google.com/forum/?fromgroups=#!forum/openmc-dev diff --git a/docs/source/devguide/workflow.rst b/docs/source/devguide/workflow.rst index 976e9b117..3d8d2e827 100644 --- a/docs/source/devguide/workflow.rst +++ b/docs/source/devguide/workflow.rst @@ -122,11 +122,11 @@ can interfere with virtual environments. .. _git: http://git-scm.com/ .. _GitHub: https://github.com/ -.. _git flow: http://nvie.com/git-model -.. _valgrind: http://valgrind.org/ +.. _git flow: https://nvie.com/git-model +.. _valgrind: https://www.valgrind.org/ .. _style guide: https://docs.openmc.org/en/latest/devguide/styleguide.html -.. _pull request: https://help.github.com/articles/using-pull-requests +.. _pull request: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests .. _openmc-dev/openmc: https://github.com/openmc-dev/openmc -.. _paid plan: https://github.com/plans +.. _paid plan: https://github.com/pricing .. _Bitbucket: https://bitbucket.org .. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/index.rst b/docs/source/index.rst index ce3c5f77e..3b2f5b053 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -13,8 +13,8 @@ programming model. OpenMC was originally developed by members of the `Computational Reactor Physics Group `_ at the `Massachusetts Institute of Technology -`_ starting in 2011. Various universities, laboratories, and -other organizations now contribute to the development of OpenMC. For more +`_ starting in 2011. Various universities, laboratories, +and other organizations now contribute to the development of OpenMC. For more information on OpenMC, feel free to post a message on the `OpenMC Discourse Forum `_. diff --git a/docs/source/io_formats/data_wmp.rst b/docs/source/io_formats/data_wmp.rst index e7261f3fb..c93eb2776 100644 --- a/docs/source/io_formats/data_wmp.rst +++ b/docs/source/io_formats/data_wmp.rst @@ -49,4 +49,4 @@ Windowed Multipole Library Format windows[i, 1] are, respectively, the indexes (1-based) of the first and last pole in window i. -.. _h5py: http://docs.h5py.org/en/latest/ +.. _h5py: https://docs.h5py.org/en/latest/ diff --git a/docs/source/io_formats/mgxs_library.rst b/docs/source/io_formats/mgxs_library.rst index 4129ed4ba..f7f5387a4 100644 --- a/docs/source/io_formats/mgxs_library.rst +++ b/docs/source/io_formats/mgxs_library.rst @@ -21,7 +21,7 @@ nuclides or materials. The current version of the multi-group library file format is 1.0. -.. _HDF5: http://www.hdfgroup.org/HDF5/ +.. _HDF5: https://www.hdfgroup.org/solutions/hdf5/ .. _mgxs_lib_spec: diff --git a/docs/source/methods/cross_sections.rst b/docs/source/methods/cross_sections.rst index dc42d33de..c6d1f44f4 100644 --- a/docs/source/methods/cross_sections.rst +++ b/docs/source/methods/cross_sections.rst @@ -269,12 +269,12 @@ or even isotropic scattering. .. _logarithmic mapping technique: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-14-24530.pdf -.. _Hwang: http://www.ans.org/pubs/journals/nse/a_16381 +.. _Hwang: https://doi.org/10.13182/NSE87-A16381 .. _Josey: https://doi.org/10.1016/j.jcp.2015.08.013 .. _WMP Library: https://github.com/mit-crpg/WMP_Library -.. _MCNP: http://mcnp.lanl.gov +.. _MCNP: https://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi -.. _NJOY: http://t2.lanl.gov/codes.shtml -.. _ENDF/B data: http://www.nndc.bnl.gov/endf +.. _NJOY: https://www.njoy21.io/NJOY21/ +.. _ENDF/B data: https://www.nndc.bnl.gov/endf/b8.0/ .. _Leppanen: https://doi.org/10.1016/j.anucene.2009.03.019 .. _algorithms: http://ab-initio.mit.edu/wiki/index.php/Faddeeva_Package diff --git a/docs/source/methods/geometry.rst b/docs/source/methods/geometry.rst index 38d9eb172..04e3456c4 100644 --- a/docs/source/methods/geometry.rst +++ b/docs/source/methods/geometry.rst @@ -962,6 +962,6 @@ surface is known as in :ref:`reflection`. .. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry .. _surfaces: https://en.wikipedia.org/wiki/Surface -.. _MCNP: http://mcnp.lanl.gov +.. _MCNP: https://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _Monte Carlo Performance benchmark: https://github.com/mit-crpg/benchmarks/tree/master/mc-performance/openmc diff --git a/docs/source/methods/neutron_physics.rst b/docs/source/methods/neutron_physics.rst index 5961283fa..774575b46 100644 --- a/docs/source/methods/neutron_physics.rst +++ b/docs/source/methods/neutron_physics.rst @@ -1695,7 +1695,7 @@ another. .. _SIGMA1 method: https://doi.org/10.13182/NSE76-1 -.. _scaled interpolation: http://www.ans.org/pubs/journals/nse/a_26575 +.. _scaled interpolation: https://doi.org/10.13182/NSE73-A26575 .. _probability table method: https://doi.org/10.13182/NSE72-3 @@ -1703,23 +1703,23 @@ another. .. _Foderaro: http://hdl.handle.net/1721.1/1716 -.. _OECD: http://www.oecd-nea.org/tools/abstract/detail/NEA-1792 +.. _OECD: https://www.oecd-nea.org/tools/abstract/detail/NEA-1792 .. _NJOY: https://www.njoy21.io/NJOY2016/ -.. _PREPRO: http://www-nds.iaea.org/ndspub/endf/prepro/ +.. _PREPRO: https://www-nds.iaea.org/ndspub/endf/prepro/ .. _endf102: https://www.oecd-nea.org/dbdata/data/manual-endf/endf102.pdf -.. _Monte Carlo Sampler: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-9721.pdf +.. _Monte Carlo Sampler: https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-09721-MS -.. _LA-UR-14-27694: http://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-14-27694 +.. _LA-UR-14-27694: https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-14-27694 -.. _MC21: http://www.osti.gov/bridge/servlets/purl/903083-HT5p1o/903083.pdf +.. _MC21: https://www.osti.gov/biblio/903083 .. _Romano: https://doi.org/10.1016/j.cpc.2014.11.001 -.. _Sutton and Brown: http://www.osti.gov/bridge/product.biblio.jsp?osti_id=307911 +.. _Sutton and Brown: https://www.osti.gov/biblio/307911 .. _lectures: https://laws.lanl.gov/vhosts/mcnp.lanl.gov/pdf_files/la-ur-05-4983.pdf diff --git a/docs/source/methods/parallelization.rst b/docs/source/methods/parallelization.rst index 5bf090a2b..29748807a 100644 --- a/docs/source/methods/parallelization.rst +++ b/docs/source/methods/parallelization.rst @@ -251,8 +251,8 @@ depending on how many nodes are communicating and the size of the message. Using multiple algorithms allows one to minimize latency for small messages and minimize bandwidth for long messages. -We will focus here on the implementation of broadcast in the MPICH2_ -implementation. For short messages, MPICH2 uses a `binomial tree`_ algorithm. In +We will focus here on the implementation of broadcast in the MPICH_ +implementation. For short messages, MPICH uses a `binomial tree`_ algorithm. In this algorithm, the root process sends the data to one node in the first step, and then in the subsequent, both the root and the other node can send the data to other nodes. Thus, it takes a total of :math:`\lceil \log_2 p \rceil` steps @@ -266,7 +266,7 @@ to complete the communication. The time to complete the communication is This algorithm works well for short messages since the latency term scales logarithmically with the number of nodes. However, for long messages, an algorithm that has lower bandwidth has been proposed by Barnett_ and implemented -in MPICH2. Rather than using a binomial tree, the broadcast is divided into a +in MPICH. Rather than using a binomial tree, the broadcast is divided into a scatter and an allgather. The time to complete the scatter is :math:` \log_2 p \: \alpha + \frac{p-1}{p} N\beta` using a binomial tree algorithm. The allgather is performed using a ring algorithm that completes in :math:`p-1) \alpha + @@ -613,7 +613,7 @@ is actually independent of the number of nodes: .. _Brissenden and Garlick: https://doi.org/10.1016/0306-4549(86)90095-2 -.. _MPICH2: http://www.mcs.anl.gov/mpi/mpich +.. _MPICH: http://www.mpich.org .. _binomial tree: https://www.mcs.anl.gov/~thakur/papers/ijhpca-coll.pdf @@ -629,19 +629,19 @@ is actually independent of the number of nodes: .. _message-passing interface: https://en.wikipedia.org/wiki/Message_Passing_Interface -.. _PVM: http://www.csm.ornl.gov/pvm/pvm_home.html +.. _PVM: https://www.csm.ornl.gov/pvm/pvm_home.html -.. _MPI: http://www.mcs.anl.gov/research/projects/mpi/ +.. _MPI: https://www.mcs.anl.gov/research/projects/mpi/ .. _embarrassingly parallel: https://en.wikipedia.org/wiki/Embarrassingly_parallel -.. _sends: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Send.html +.. _sends: https://www.mpich.org//static/docs/latest/www3/MPI_Send.html -.. _broadcasts: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Bcast.html +.. _broadcasts: https://www.mpich.org//static/docs/latest/www3/MPI_Bcast.html -.. _scatter: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Scatter.html +.. _scatter: https://www.mpich.org//static/docs/latest/www3/MPI_Scatter.html -.. _allgather: http://www.mcs.anl.gov/research/projects/mpi/www/www3/MPI_Allgather.html +.. _allgather: https://www.mpich.org//static/docs/latest/www3/MPI_Allgather.html .. _Cauchy distribution: https://en.wikipedia.org/wiki/Cauchy_distribution diff --git a/docs/source/methods/photon_physics.rst b/docs/source/methods/photon_physics.rst index 7c4733ff4..bc912b943 100644 --- a/docs/source/methods/photon_physics.rst +++ b/docs/source/methods/photon_physics.rst @@ -1069,6 +1069,6 @@ emitted photon. .. _Kaltiaisenaho: https://aaltodoc.aalto.fi/bitstream/handle/123456789/21004/master_Kaltiaisenaho_Toni_2016.pdf -.. _Salvat: http://www.oecd-nea.org/globalsearch/download.php?doc=77434 +.. _Salvat: https://www.oecd-nea.org/globalsearch/download.php?doc=77434 .. _Sternheimer: https://doi.org/10.1103/PhysRevB.26.6067 diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 7f67f95ce..0656e6c80 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -7,8 +7,8 @@ Python API OpenMC includes a rich Python API that enables programmatic pre- and post-processing. The easiest way to begin using the API is to take a look at the :ref:`examples`. This assumes that you are already familiar with Python and -common third-party packages such as `NumPy `_. If you -have never used Python before, the prospect of learning a new code *and* a +common third-party packages such as `NumPy `_. If you have +never used Python before, the prospect of learning a new code *and* a programming language might sound daunting. However, you should keep in mind that there are many substantial benefits to using the Python API, including: @@ -28,7 +28,7 @@ there are many substantial benefits to using the Python API, including: For those new to Python, there are many good tutorials available online. We recommend going through the modules from `Codecademy `_ and/or the `Scipy lectures -`_. +`_. The full API documentation serves to provide more information on a given module or class. diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 2b815734a..5078b5d64 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -12,7 +12,7 @@ OpenMC, see :ref:`usersguide_install` in the User's Manual. Installing on Linux/Mac with conda-forge ---------------------------------------- -`Conda `_ is an open source package management +`Conda `_ is an open source package management system and environment management system for installing multiple versions of software packages and their dependencies and switching easily between them. If you have `conda` installed on your system, OpenMC can be installed via the @@ -25,16 +25,16 @@ you have `conda` installed on your system, OpenMC can be installed via the To list the versions of OpenMC that are available on the `conda-forge` channel, in your terminal window or an Anaconda Prompt run: -.. code-block:: sh +.. code-block:: sh conda search openmc - + OpenMC can then be installed with: .. code-block:: sh conda create -n openmc-env openmc - + This will install OpenMC in a conda environment called `openmc-env`. To activate the environment, run: @@ -69,7 +69,7 @@ Now OpenMC should be recognized within the repository and can be installed: Binary packages from this PPA may exist for earlier versions of Ubuntu, but they are no longer supported. -.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging +.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/ubuntu/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto ------------------------------------------- @@ -112,7 +112,7 @@ To install the latest OpenMC with the Python API, use the following command: For more information about customizations including MPI, see the :ref:`detailed installation instructions using Spack `. Once installed, environment/lmod modules can be generated or Spack's `load` feature -can be used to access the installed packages. +can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ .. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html @@ -139,9 +139,9 @@ Installing from Source on Linux or Mac OS X All OpenMC source code is hosted on `GitHub `_. If you have `git `_, the `gcc `_ compiler suite, -`CMake `_, and `HDF5 `_ -installed, you can download and install OpenMC by entering the following -commands in a terminal: +`CMake `_, and `HDF5 +`_ installed, you can download and +install OpenMC be entering the following commands in a terminal: .. code-block:: sh diff --git a/docs/source/usersguide/beginners.rst b/docs/source/usersguide/beginners.rst index 257458fa9..eef927b84 100644 --- a/docs/source/usersguide/beginners.rst +++ b/docs/source/usersguide/beginners.rst @@ -113,7 +113,7 @@ or Mac OS X (also Unix-derived), `this tutorial commonly-used commands. To reap the full benefits of OpenMC, you should also have basic proficiency in -the use of `Python `_, as OpenMC includes a rich Python +the use of `Python `_, as OpenMC includes a rich Python API that offers many usability improvements over dealing with raw XML input files. @@ -126,8 +126,9 @@ at the git documentation website. The `OpenMC source code`_ and documentation are hosted at `GitHub`_. In order to receive updates to the code directly, submit `bug reports`_, and perform other development tasks, you may want to sign up for a free account on GitHub. Once you have an account, you can follow `these -instructions `_ on how to set up -your computer for using GitHub. +instructions +`_ on +how to set up your computer for using GitHub. If you are new to nuclear engineering, you may want to review the NRC's `Reactor Concepts Manual`_. This manual describes the basics of nuclear power for @@ -149,7 +150,7 @@ and `Volume II`_. You may also find it helpful to review the following terms: .. _discretization: https://en.wikipedia.org/wiki/Discretization .. _constructive solid geometry: https://en.wikipedia.org/wiki/Constructive_solid_geometry .. _git: http://git-scm.com/ -.. _git tutorials: http://git-scm.com/documentation +.. _git tutorials: https://git-scm.com/doc .. _Reactor Concepts Manual: http://www.tayloredge.com/periodic/trivia/ReactorConcepts.pdf .. _Volume I: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v1 .. _Volume II: https://www.standards.doe.gov/standards-documents/1000/1019-bhdbk-1993-v2 diff --git a/docs/source/usersguide/cross_sections.rst b/docs/source/usersguide/cross_sections.rst index bc5b3436f..58bf1a293 100644 --- a/docs/source/usersguide/cross_sections.rst +++ b/docs/source/usersguide/cross_sections.rst @@ -260,7 +260,7 @@ For an example of how to create a multi-group library, see the `example notebook .. _MCNP: https://mcnp.lanl.gov .. _Serpent: http://montecarlo.vtt.fi .. _ENDF/B: https://www.nndc.bnl.gov/endf/b7.1/acefiles.html -.. _JEFF: http://www.oecd-nea.org/dbdata/jeff/jeff33/ +.. _JEFF: https://www.oecd-nea.org/dbdata/jeff/jeff33/ .. _TENDL: https://tendl.web.psi.ch/tendl_2017/tendl2017.html .. _Seltzer and Berger: https://doi.org/10.1016/0092-640X(86)90014-8 .. _NIST ESTAR database: https://physics.nist.gov/PhysRefData/Star/Text/ESTAR.html diff --git a/docs/source/usersguide/geometry.rst b/docs/source/usersguide/geometry.rst index 18bdf94b5..c8e5fd99f 100644 --- a/docs/source/usersguide/geometry.rst +++ b/docs/source/usersguide/geometry.rst @@ -261,7 +261,7 @@ lowest-level cell at that location:: As you are building a geometry, it is also possible to display a plot of single universe using the :meth:`Universe.plot` method. This method requires that you -have `matplotlib `_ installed. +have `matplotlib `_ installed. .. _usersguide_lattices: diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 274d4c66a..549f8b1b0 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -71,7 +71,7 @@ Now OpenMC should be recognized within the repository and can be installed: Binary packages from this PPA may exist for earlier versions of Ubuntu, but they are no longer supported. -.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/staging +.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/ubuntu/staging .. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto .. _install-spack: @@ -242,9 +242,9 @@ Prerequisites .. _gcc: https://gcc.gnu.org/ -.. _CMake: http://www.cmake.org -.. _OpenMPI: http://www.open-mpi.org -.. _MPICH: http://www.mpich.org +.. _CMake: https://cmake.org +.. _OpenMPI: https://www.open-mpi.org +.. _MPICH: https://www.mpich.org .. _HDF5: https://www.hdfgroup.org/solutions/hdf5/ .. _DAGMC: https://svalinn.github.io/DAGMC/index.html @@ -256,10 +256,10 @@ directly from GitHub or, if you have the git_ version control software installed on your computer, you can use git to obtain the source code. The latter method has the benefit that it is easy to receive updates directly from the GitHub repository. GitHub has a good set of `instructions -`_ for how to set up git to work -with GitHub since this involves setting up ssh_ keys. With git installed and -setup, the following command will download the full source code from the GitHub -repository:: +`_ for +how to set up git to work with GitHub since this involves setting up ssh_ keys. +With git installed and setup, the following command will download the full +source code from the GitHub repository:: git clone --recurse-submodules https://github.com/openmc-dev/openmc.git @@ -408,8 +408,8 @@ Compiling on Windows 10 Recent versions of Windows 10 include a subsystem for Linux that allows one to run Bash within Ubuntu running in Windows. First, follow the installation guide -`here `_ to get -Bash on Ubuntu on Windows setup. Once you are within bash, obtain the necessary +`here `_ to get Bash +on Ubuntu on Windows setup. Once you are within bash, obtain the necessary :ref:`prerequisites ` via ``apt``. Finally, follow the :ref:`instructions for compiling on linux `. @@ -494,7 +494,7 @@ distributions. .. admonition:: Required :class: error - `NumPy `_ + `NumPy `_ NumPy is used extensively within the Python API for its powerful N-dimensional array. @@ -502,7 +502,7 @@ distributions. SciPy's special functions, sparse matrices, and spatial data structures are used for several optional features in the API. - `pandas `_ + `pandas `_ Pandas is used to generate tally DataFrames as demonstrated in an `example notebook <../examples/pandas-dataframes.ipynb>`_. @@ -511,14 +511,14 @@ distributions. various HDF5 files, h5py is needed to provide access to data within these files from Python. - `Matplotlib `_ + `Matplotlib `_ Matplotlib is used to providing plotting functionality in the API like the :meth:`Universe.plot` method and the :func:`openmc.plot_xs` function. `uncertainties `_ Uncertainties are used for decay data in the :mod:`openmc.data` module. - `lxml `_ + `lxml `_ lxml is used for the :ref:`scripts_validate` script and various other parts of the Python API. @@ -530,11 +530,11 @@ distributions. parallel runs. This package is needed if you plan on running depletion simulations in parallel using MPI. - `Cython `_ + `Cython `_ Cython is used for resonance reconstruction for ENDF data converted to :class:`openmc.data.IncidentNeutron`. - `vtk `_ + `vtk `_ The Python VTK bindings are needed to convert voxel and track files to VTK format. @@ -588,7 +588,7 @@ schemas.xml file in your own OpenMC source directory. .. _GNU Emacs: http://www.gnu.org/software/emacs/ .. _validation: https://en.wikipedia.org/wiki/XML_validation -.. _RELAX NG: http://relaxng.org/ +.. _RELAX NG: https://relaxng.org/ .. _ctest: https://cmake.org/cmake/help/latest/manual/ctest.1.html .. _Conda: https://conda.io/en/latest/ .. _pip: https://pip.pypa.io/en/stable/ diff --git a/docs/source/usersguide/materials.rst b/docs/source/usersguide/materials.rst index 85584a6c4..8b43f1a2a 100644 --- a/docs/source/usersguide/materials.rst +++ b/docs/source/usersguide/materials.rst @@ -45,7 +45,7 @@ This method can also accept case-insensitive element names such as :: mat.add_element('aluminium', 1.0) - + Internally, OpenMC stores data on the atomic masses and natural abundances of all known isotopes and then uses this data to determine what isotopes should be added to the material. When the material is later exported to XML for use by the @@ -105,7 +105,7 @@ you would need to add hydrogen and oxygen to a material and then assign the Naming Conventions ------------------ -OpenMC uses the GND_ naming convention for nuclides, metastable states, and +OpenMC uses the GNDS_ naming convention for nuclides, metastable states, and compounds: :Nuclides: ``SymA`` where "A" is the mass number (e.g., ``Fe56``) @@ -122,7 +122,7 @@ compounds: ENDF/B-VII.1! If you are adding an element via :meth:`Material.add_element`, just use ``Sym``. -.. _GND: https://www.oecd-nea.org/science/wpec/sg38/Meetings/2016_May/tlh4gnd-main.pdf +.. _GNDS: https://www.oecd-nea.org/jcms/pl_39689/specifications-for-the-generalised-nuclear-database-structure-gnds ----------- Temperature @@ -160,26 +160,26 @@ Material Mixtures ----------------- In OpenMC it is possible to mix any number of materials to create a new material -with the correct nuclide composition and density. The +with the correct nuclide composition and density. The :meth:`Material.mix_materials` method takes a list of materials and -a list of their mixing fractions. Mixing fractions can be provided as atomic +a list of their mixing fractions. Mixing fractions can be provided as atomic fractions, weight fractions, or volume fractions. The fraction type -can be specified by passing 'ao', 'wo', or 'vo' as the third argument, respectively. -For example, assuming the required materials have already been defined, a MOX +can be specified by passing 'ao', 'wo', or 'vo' as the third argument, respectively. +For example, assuming the required materials have already been defined, a MOX material with 3% plutonium oxide by weight could be created using the following: :: mox = openmc.Material.mix_materials([uo2, puo2], [0.97, 0.03], 'wo') -It should be noted that, if mixing fractions are specifed as atomic or weight +It should be noted that, if mixing fractions are specifed as atomic or weight fractions, the supplied fractions should sum to one. If the fractions are specified -as volume fractions, and the sum of the fractions is less than one, then the remaining -fraction is set as void material. +as volume fractions, and the sum of the fractions is less than one, then the remaining +fraction is set as void material. .. warning:: Materials with :math:`S(\alpha,\beta)` thermal scattering data cannot be used in :meth:`Material.mix_materials`. However, thermal - scattering data can be added to a material created by + scattering data can be added to a material created by :meth:`Material.mix_materials`. -------------------- diff --git a/docs/source/usersguide/parallel.rst b/docs/source/usersguide/parallel.rst index 1cbb09401..be4189604 100644 --- a/docs/source/usersguide/parallel.rst +++ b/docs/source/usersguide/parallel.rst @@ -8,8 +8,8 @@ If you are running a simulation on a computer with multiple cores, multiple sockets, or multiple nodes (i.e., a cluster), you can benefit from the fact that OpenMC is able to use all available hardware resources if configured correctly. OpenMC is capable of using both distributed-memory (`MPI -`_) and shared-memory (`OpenMP -`_) parallelism. If you are on a single-socket +`_) and shared-memory (`OpenMP +`_) parallelism. If you are on a single-socket workstation or a laptop, using shared-memory parallelism is likely sufficient. On a multi-socket node, cluster, or supercomputer, chances are you will need to use both distributed-memory (across nodes) and shared-memory @@ -49,7 +49,7 @@ Distributed-Memory Parallelism (MPI) MPI defines a library specification for message-passing between processes. There are two major implementations of MPI, `OpenMPI `_ and -`MPICH `_. Both implementations are known to work with +`MPICH `_. Both implementations are known to work with OpenMC; there is no obvious reason to prefer one over the other. Building OpenMC with support for MPI requires that you have one of these implementations installed on your system. For instructions on obtaining MPI, see diff --git a/docs/source/usersguide/plots.rst b/docs/source/usersguide/plots.rst index a10f370d6..109a8b549 100644 --- a/docs/source/usersguide/plots.rst +++ b/docs/source/usersguide/plots.rst @@ -97,7 +97,7 @@ derivatives: ``sudo apt install imagemagick``). Images are then converted like: convert myplot.ppm myplot.png -Alternatively, if you're working within a `Jupyter `_ +Alternatively, if you're working within a `Jupyter `_ 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. @@ -122,7 +122,7 @@ should be three items long, e.g.:: The voxel plot data is written to an :ref:`HDF5 file `. The voxel file can subsequently be converted into a standard mesh format that can be viewed in -`ParaView `_, `VisIt +`ParaView `_, `VisIt `_, etc. This typically will compress the size of the file significantly. The provided :ref:`scripts_voxel` script can convert the HDF5 voxel file to VTK formats. Once diff --git a/docs/source/usersguide/processing.rst b/docs/source/usersguide/processing.rst index 9652a51ca..e61ba33bf 100644 --- a/docs/source/usersguide/processing.rst +++ b/docs/source/usersguide/processing.rst @@ -54,7 +54,7 @@ Getting Data into MATLAB There is currently no front-end utility to dump tally data to MATLAB files, but the process is straightforward. First extract the data using the Python API via ``openmc.statepoint`` and then use the `Scipy MATLAB IO routines -`_ to save to a MAT +`_ to save to a MAT file. Note that all arrays that are accessible in a statepoint are already in NumPy arrays that can be reshaped and dumped to MATLAB in one step. diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index 56dfe0d9f..b10e74f93 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -110,8 +110,8 @@ otherwise. This script generates an HDF5 file called ``compton_profiles.h5`` that contains Compton profile data using an existing data library from `Geant4 -`_. Note that OpenMC includes this data file by default -so it should not be necessary in practice to generate it yourself. +`_. Note that OpenMC includes this data file by +default so it should not be necessary in practice to generate it yourself. .. _scripts_depletion_chain: @@ -250,7 +250,7 @@ Message Description When OpenMC generates :ref:`voxel plots `, they are in an :ref:`HDF5 format ` that is not terribly useful by itself. The ``openmc-voxel-to-vtk`` script converts a voxel HDF5 file to a `VTK -`_ file. To run this script, you will need to have the VTK +`_ file. To run this script, you will need to have the VTK Python bindings installed. To convert a voxel file, simply provide the path to the file: diff --git a/examples/jupyter/cad-based-geometry.ipynb b/examples/jupyter/cad-based-geometry.ipynb index 5f84ae5e5..9fa56f6fb 100644 --- a/examples/jupyter/cad-based-geometry.ipynb +++ b/examples/jupyter/cad-based-geometry.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Using CAD-Based Geometries\n", - "In this notebook we'll be exploring how to use CAD-based geometries in OpenMC via the [DagMC](https://svalinn.github.io/DAGMC/index.html) toolkit. The models we'll be using in this notebook have already been created using [Trelis](https://www.csimsoft.com/trelis) and faceted into a surface mesh represented as `.h5m` files in the [Mesh Oriented DatABase](https://press3.mcs.anl.gov/sigma/moab-library/) format. We'll be retrieving these files using the function below.\n", + "In this notebook we'll be exploring how to use CAD-based geometries in OpenMC via the [DagMC](https://svalinn.github.io/DAGMC/index.html) toolkit. The models we'll be using in this notebook have already been created using [Trelis](https://coreform.com/products/trelisnew/) and faceted into a surface mesh represented as `.h5m` files in the [Mesh Oriented DatABase](https://sigma.mcs.anl.gov/moab-library/) format. We'll be retrieving these files using the function below.\n", "\n" ] }, @@ -93,7 +93,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's download the DAGMC model. These models come in the form of triangle surface meshes stored using the the Mesh Oriented datABase ([MOAB](https://press3.mcs.anl.gov/sigma/moab-library/)) in an HDF5 file with the extension `.h5m`. An example of a coarse triangle mesh looks like:" + "Let's download the DAGMC model. These models come in the form of triangle surface meshes stored using the the Mesh Oriented datABase ([MOAB](https://sigma.mcs.anl.gov/moab-library/)) in an HDF5 file with the extension `.h5m`. An example of a coarse triangle mesh looks like:" ] }, { @@ -234,7 +234,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note:** Applying tally filters in DagMC models requires prior knowledge of the model. Here, we know that the fuel cell's volume ID in the CAD sofware is 1. To identify cells without use of CAD software, load them into the [OpenMC plotter](https://github.com/openmc/plotter) where cell, material, and volume IDs can be identified for native both OpenMC and DagMC geometries." + "**Note:** Applying tally filters in DagMC models requires prior knowledge of the model. Here, we know that the fuel cell's volume ID in the CAD sofware is 1. To identify cells without use of CAD software, load them into the [OpenMC plotter](https://github.com/openmc-dev/plotter) where cell, material, and volume IDs can be identified for native both OpenMC and DagMC geometries." ] }, { diff --git a/examples/jupyter/mdgxs-part-i.ipynb b/examples/jupyter/mdgxs-part-i.ipynb index cda79d30a..ea1dd7bcd 100644 --- a/examples/jupyter/mdgxs-part-i.ipynb +++ b/examples/jupyter/mdgxs-part-i.ipynb @@ -89,7 +89,7 @@ "\n", "$$\\nu_d \\sigma_{n,x,k,g} = \\frac{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\nu_d \\sigma_{f,x}(\\mathbf{r},E')\\Phi(\\mathbf{r},E')}{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\Phi(\\mathbf{r},E')}$$\n", "\n", - "This scalar flux-weighted average microscopic cross section is computed by `openmc.mgxs` for only the delayed-nu-fission and delayed neutron fraction reaction type at the moment. These double integrals are stochastically computed with OpenMC's tally system - in particular, [filters](https://docs.openmc.org/en/stable/usersguide/tallies.html#filters) on the energy range and spatial zone (material, cell, universe, or mesh) define the bounds of integration for both numerator and denominator." + "This scalar flux-weighted average microscopic cross section is computed by `openmc.mgxs` for only the delayed-nu-fission and delayed neutron fraction reaction type at the moment. These double integrals are stochastically computed with OpenMC's tally system - in particular, [filters](../usersguide/tallies.rst#filters) on the energy range and spatial zone (material, cell, universe, or mesh) define the bounds of integration for both numerator and denominator." ] }, { @@ -702,7 +702,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Since the `openmc.mgxs` module uses [tally arithmetic](https://mit-crpg.github.io/openmc/pythonapi/examples/tally-arithmetic.html) under-the-hood, the cross section is stored as a \"derived\" `Tally` object. This means that it can be queried and manipulated using all of the same methods supported for the `Tally` class in the OpenMC Python API. For example, we can construct a [Pandas](http://pandas.pydata.org/) `DataFrame` of the multi-group cross section data." + "Since the `openmc.mgxs` module uses [tally arithmetic](tally-arithmetic.ipynb) under-the-hood, the cross section is stored as a \"derived\" `Tally` object. This means that it can be queried and manipulated using all of the same methods supported for the `Tally` class in the OpenMC Python API. For example, we can construct a [Pandas](https://pandas.pydata.org/) `DataFrame` of the multi-group cross section data." ] }, { @@ -1077,7 +1077,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](https://mit-crpg.github.io/openmc/pythonapi/examples/tally-arithmetic.html) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to compute the delayed neutron precursor concentrations using the `Beta`, `DelayedNuFissionXS`, and `DecayRate` objects. The delayed neutron precursor concentrations are modeled using the following equations:\n", + "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](tally-arithmetic.ipynb) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to compute the delayed neutron precursor concentrations using the `Beta`, `DelayedNuFissionXS`, and `DecayRate` objects. The delayed neutron precursor concentrations are modeled using the following equations:\n", "\n", "$$\\frac{\\partial}{\\partial t} C_{k,d} (t) = \\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\beta_{k,d} (t) \\nu_d \\sigma_{f,x}(\\mathbf{r},E',t)\\Phi(\\mathbf{r},E',t) - \\lambda_{d} C_{k,d} (t) $$\n", "\n", diff --git a/examples/jupyter/mdgxs-part-ii.ipynb b/examples/jupyter/mdgxs-part-ii.ipynb index bb9538456..1c996c82a 100644 --- a/examples/jupyter/mdgxs-part-ii.ipynb +++ b/examples/jupyter/mdgxs-part-ii.ipynb @@ -652,7 +652,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](https://mit-crpg.github.io/openmc/pythonapi/examples/tally-arithmetic.html) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to compute the delayed neutron precursor concentrations using the `Beta` and `DelayedNuFissionXS` objects. The delayed neutron precursor concentrations are modeled using the following equations:\n", + "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](tally-arithmetic.ipynb) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to compute the delayed neutron precursor concentrations using the `Beta` and `DelayedNuFissionXS` objects. The delayed neutron precursor concentrations are modeled using the following equations:\n", "\n", "$$\\frac{\\partial}{\\partial t} C_{k,d} (t) = \\int_{0}^{\\infty}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r} \\beta_{k,d} (t) \\nu_d \\sigma_{f,x}(\\mathbf{r},E',t)\\Phi(\\mathbf{r},E',t) - \\lambda_{d} C_{k,d} (t) $$\n", "\n", diff --git a/examples/jupyter/mg-mode-part-i.ipynb b/examples/jupyter/mg-mode-part-i.ipynb index a4f3822f2..c393ecc41 100644 --- a/examples/jupyter/mg-mode-part-i.ipynb +++ b/examples/jupyter/mg-mode-part-i.ipynb @@ -5,7 +5,7 @@ "metadata": {}, "source": [ "# Multigroup Mode Part I: Introduction\n", - "This Notebook illustrates the usage of OpenMC's multi-group calculational mode with the Python API. This example notebook creates and executes the 2-D [C5G7](https://www.oecd-nea.org/science/docs/2003/nsc-doc2003-16.pdf) benchmark model using the `openmc.MGXSLibrary` class to create the supporting data library on the fly." + "This Notebook illustrates the usage of OpenMC's multi-group calculational mode with the Python API. This example notebook creates and executes the 2-D [C5G7](https://www.oecd-nea.org/jcms/pl_17882) benchmark model using the `openmc.MGXSLibrary` class to create the supporting data library on the fly." ] }, { @@ -38,7 +38,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We will now create the multi-group library using data directly from Appendix A of the [C5G7](https://www.oecd-nea.org/science/docs/2003/nsc-doc2003-16.pdf) benchmark documentation. All of the data below will be created at 294K, consistent with the benchmark.\n", + "We will now create the multi-group library using data directly from Appendix A of the [C5G7](https://www.oecd-nea.org/jcms/pl_17882) benchmark documentation. All of the data below will be created at 294K, consistent with the benchmark.\n", "\n", "This notebook will first begin by setting the group structure and building the groupwise data for UO2. As you can see, the cross sections are input in the order of increasing groups (or decreasing energy).\n", "\n", diff --git a/examples/jupyter/mgxs-part-i.ipynb b/examples/jupyter/mgxs-part-i.ipynb index 62a9b70b1..3d5711adc 100644 --- a/examples/jupyter/mgxs-part-i.ipynb +++ b/examples/jupyter/mgxs-part-i.ipynb @@ -88,7 +88,7 @@ "\n", "$$\\sigma_{n,x,k,g} = \\frac{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\sigma_{n,x}(\\mathbf{r},E')\\Phi(\\mathbf{r},E')}{\\int_{E_{g}}^{E_{g-1}}\\mathrm{d}E'\\int_{\\mathbf{r} \\in V_{k}}\\mathrm{d}\\mathbf{r}\\Phi(\\mathbf{r},E')}$$\n", "\n", - "This scalar flux-weighted average microscopic cross section is computed by `openmc.mgxs` for most multi-group cross sections, including total, absorption, and fission reaction types. These double integrals are stochastically computed with OpenMC's tally system - in particular, [filters](http://openmc.readthedocs.io/en/latest/usersguide/tallies.html#filters) on the energy range and spatial zone (material, cell or universe) define the bounds of integration for both numerator and denominator." + "This scalar flux-weighted average microscopic cross section is computed by `openmc.mgxs` for most multi-group cross sections, including total, absorption, and fission reaction types. These double integrals are stochastically computed with OpenMC's tally system - in particular, [filters](../usersguide/tallies.rst#filters) on the energy range and spatial zone (material, cell or universe) define the bounds of integration for both numerator and denominator." ] }, { @@ -712,7 +712,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Since the `openmc.mgxs` module uses [tally arithmetic](http://openmc.readthedocs.io/en/latest/examples/tally-arithmetic.html) under-the-hood, the cross section is stored as a \"derived\" `Tally` object. This means that it can be queried and manipulated using all of the same methods supported for the `Tally` class in the OpenMC Python API. For example, we can construct a [Pandas](http://pandas.pydata.org/) `DataFrame` of the multi-group cross section data." + "Since the `openmc.mgxs` module uses [tally arithmetic](../examples/tally-arithmetic.rst) under-the-hood, the cross section is stored as a \"derived\" `Tally` object. This means that it can be queried and manipulated using all of the same methods supported for the `Tally` class in the OpenMC Python API. For example, we can construct a [Pandas](https://pandas.pydata.org/) `DataFrame` of the multi-group cross section data." ] }, { @@ -817,7 +817,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](http://openmc.readthedocs.io/en/latest/examples/tally-arithmetic.html) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to confirm that the `TotalXS` is equal to the sum of the `AbsorptionXS` and `ScatterXS` objects." + "Finally, we illustrate how one can leverage OpenMC's [tally arithmetic](../examples/tally-arithmetic.rst) data processing feature with `MGXS` objects. The `openmc.mgxs` module uses tally arithmetic to compute multi-group cross sections with automated uncertainty propagation. Each `MGXS` object includes an `xs_tally` attribute which is a \"derived\" `Tally` based on the tallies needed to compute the cross section type of interest. These derived tallies can be used in subsequent tally arithmetic operations. For example, we can use tally artithmetic to confirm that the `TotalXS` is equal to the sum of the `AbsorptionXS` and `ScatterXS` objects." ] }, { diff --git a/examples/jupyter/mgxs-part-ii.ipynb b/examples/jupyter/mgxs-part-ii.ipynb index dd5b21a55..495f01211 100644 --- a/examples/jupyter/mgxs-part-ii.ipynb +++ b/examples/jupyter/mgxs-part-ii.ipynb @@ -9,12 +9,12 @@ "\n", "* Creation of multi-group cross sections on a **heterogeneous geometry**\n", "* Calculation of cross sections on a **nuclide-by-nuclide basis**\n", - "* The use of **[tally precision triggers](http://docs.openmc.org/en/latest/io_formats/settings.html#trigger-element)** with multi-group cross sections\n", + "* The use of **[tally precision triggers](../io_formats/settings.rst#trigger-element)** with multi-group cross sections\n", "* Built-in features for **energy condensation** in downstream data processing\n", "* The use of the **`openmc.data`** module to plot continuous-energy vs. multi-group cross sections\n", "* **Validation** of multi-group cross sections with **[OpenMOC](https://mit-crpg.github.io/OpenMOC/)**\n", "\n", - "**Note:** This Notebook was created using [OpenMOC](https://mit-crpg.github.io/OpenMOC/) to verify the multi-group cross-sections generated by OpenMC. You must install [OpenMOC](https://mit-crpg.github.io/OpenMOC/) on your system in order to run this Notebook in its entirety. In addition, this Notebook illustrates the use of [Pandas](http://pandas.pydata.org/) `DataFrames` to containerize multi-group cross section data." + "**Note:** This Notebook was created using [OpenMOC](https://mit-crpg.github.io/OpenMOC/) to verify the multi-group cross-sections generated by OpenMC. You must install [OpenMOC](https://mit-crpg.github.io/OpenMOC/) on your system in order to run this Notebook in its entirety. In addition, this Notebook illustrates the use of [Pandas](https://pandas.pydata.org/) `DataFrames` to containerize multi-group cross section data." ] }, { @@ -264,7 +264,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we showcase the use of OpenMC's [tally precision trigger](http://openmc.readthedocs.io/en/latest/io_formats/settings.html#trigger-element) feature in conjunction with the `openmc.mgxs` module. In particular, we will assign a tally trigger of 1E-2 on the standard deviation for each of the tallies used to compute multi-group cross sections." + "Next, we showcase the use of OpenMC's [tally precision trigger](../io_formats/settings.rst#trigger-element) feature in conjunction with the `openmc.mgxs` module. In particular, we will assign a tally trigger of 1E-2 on the standard deviation for each of the tallies used to compute multi-group cross sections." ] }, { @@ -745,7 +745,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Although a printed report is nice, it is not scalable or flexible. Let's extract the microscopic cross section data for the moderator as a [Pandas](http://pandas.pydata.org/) `DataFrame` ." + "Although a printed report is nice, it is not scalable or flexible. Let's extract the microscopic cross section data for the moderator as a [Pandas](https://pandas.pydata.org/) `DataFrame` ." ] }, { diff --git a/examples/jupyter/mgxs-part-iii.ipynb b/examples/jupyter/mgxs-part-iii.ipynb index b05dbabd5..d75255d73 100644 --- a/examples/jupyter/mgxs-part-iii.ipynb +++ b/examples/jupyter/mgxs-part-iii.ipynb @@ -12,7 +12,7 @@ "* **Validation** of multi-group cross sections with **[OpenMOC](https://mit-crpg.github.io/OpenMOC/)**\n", "* Steady-state pin-by-pin **fission rates comparison** between OpenMC and [OpenMOC](https://mit-crpg.github.io/OpenMOC/)\n", "\n", - "**Note:** This Notebook was created using [OpenMOC](https://mit-crpg.github.io/OpenMOC/) to verify the multi-group cross-sections generated by OpenMC. You must install [OpenMOC](https://mit-crpg.github.io/OpenMOC/) on your system to run this Notebook in its entirety. In addition, this Notebook illustrates the use of [Pandas](http://pandas.pydata.org/) `DataFrames` to containerize multi-group cross section data." + "**Note:** This Notebook was created using [OpenMOC](https://mit-crpg.github.io/OpenMOC/) to verify the multi-group cross-sections generated by OpenMC. You must install [OpenMOC](https://mit-crpg.github.io/OpenMOC/) on your system to run this Notebook in its entirety. In addition, this Notebook illustrates the use of [Pandas](https://pandas.pydata.org/) `DataFrames` to containerize multi-group cross section data." ] }, { @@ -811,7 +811,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The `NuFissionXS` object supports all of the methods described previously in the `openmc.mgxs` tutorials, such as [Pandas](http://pandas.pydata.org/) `DataFrames`:\n", + "The `NuFissionXS` object supports all of the methods described previously in the `openmc.mgxs` tutorials, such as [Pandas](https://pandas.pydata.org/) `DataFrames`:\n", "Note that since so few histories were simulated, we should expect a few division-by-error errors as some tallies have not yet scored any results." ] }, @@ -994,7 +994,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The HDF5 store will contain the numerical multi-group cross section data indexed by domain, nuclide and cross section type. Some data workflows may be optimized by storing and retrieving binary representations of the `MGXS` objects in the `Library`. This feature is supported through the `Library.dump_to_file(...)` and `Library.load_from_file(...)` routines which use Python's [`pickle`](https://docs.python.org/2/library/pickle.html) module. This is illustrated as follows." + "The HDF5 store will contain the numerical multi-group cross section data indexed by domain, nuclide and cross section type. Some data workflows may be optimized by storing and retrieving binary representations of the `MGXS` objects in the `Library`. This feature is supported through the `Library.dump_to_file(...)` and `Library.load_from_file(...)` routines which use Python's [`pickle`](https://docs.python.org/3/library/pickle.html) module. This is illustrated as follows." ] }, { diff --git a/examples/jupyter/nuclear-data.ipynb b/examples/jupyter/nuclear-data.ipynb index c63c24a17..30d2c09f0 100644 --- a/examples/jupyter/nuclear-data.ipynb +++ b/examples/jupyter/nuclear-data.ipynb @@ -900,7 +900,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If had [Cython](http://cython.org/) installed when you built/installed OpenMC, you should be able to evaluate resonant cross sections from ENDF data directly, i.e., OpenMC will reconstruct resonances behind the scenes for you." + "If had [Cython](https://cython.org/) installed when you built/installed OpenMC, you should be able to evaluate resonant cross sections from ENDF data directly, i.e., OpenMC will reconstruct resonances behind the scenes for you." ] }, { diff --git a/examples/jupyter/post-processing.ipynb b/examples/jupyter/post-processing.ipynb index d31af5831..8d9e10966 100644 --- a/examples/jupyter/post-processing.ipynb +++ b/examples/jupyter/post-processing.ipynb @@ -567,7 +567,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "The statepoint file actually stores the sum and sum-of-squares for each tally bin from which the mean and variance can be calculated as described [here](http://openmc.readthedocs.io/en/latest/methods/tallies.html#variance). The sum and sum-of-squares can be accessed using the ``sum`` and ``sum_sq`` properties:" + "The statepoint file actually stores the sum and sum-of-squares for each tally bin from which the mean and variance can be calculated as described [here](../methods/tallies.rst#variance). The sum and sum-of-squares can be accessed using the ``sum`` and ``sum_sq`` properties:" ] }, { diff --git a/examples/jupyter/search.ipynb b/examples/jupyter/search.ipynb index a71272641..d2163f8e2 100644 --- a/examples/jupyter/search.ipynb +++ b/examples/jupyter/search.ipynb @@ -36,7 +36,7 @@ "\n", "To perform the search we will use the `openmc.search_for_keff` function. This function requires a different function be defined which creates an parametrized model to analyze. This model is required to be stored in an `openmc.model.Model` object. The first parameter of this function will be modified during the search process for our critical eigenvalue.\n", "\n", - "Our model will be a pin-cell from the [Multi-Group Mode Part II](http://docs.openmc.org/en/latest/examples/mg-mode-part-ii.html) assembly, except this time the entire model building process will be contained within a function, and the Boron concentration will be parametrized." + "Our model will be a pin-cell from the [Multi-Group Mode Part II](mg-mode-part-ii.ipynb) assembly, except this time the entire model building process will be contained within a function, and the Boron concentration will be parametrized." ] }, { From 28b37011b0b5a62289904b383b2baaf858809e67 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Mar 2021 14:33:54 -0600 Subject: [PATCH 46/68] Add release notes for 0.12.1 and a bunch of versionadded tags --- docs/source/releasenotes/0.12.1.rst | 130 ++++++++++++++++++++++++++++ docs/source/releasenotes/index.rst | 1 + openmc/data/data.py | 4 +- openmc/data/multipole.py | 2 + openmc/deplete/chain.py | 2 + openmc/deplete/results_list.py | 2 + openmc/mgxs/mgxs.py | 6 ++ 7 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 docs/source/releasenotes/0.12.1.rst diff --git a/docs/source/releasenotes/0.12.1.rst b/docs/source/releasenotes/0.12.1.rst new file mode 100644 index 000000000..23f12f62e --- /dev/null +++ b/docs/source/releasenotes/0.12.1.rst @@ -0,0 +1,130 @@ +==================== +What's New in 0.12.1 +==================== + +.. currentmodule:: openmc + +------- +Summary +------- + +This release of OpenMC includes an assortment of new features and many bug fixes. +In particular, the :mod:`openmc.deplete` module has been heavily tested which +has resulted in a number of usability improvements, bug fixes, and other +enhancements. Energy deposition calculations, particularly for coupled +neutron-photon simulations, have been improved as well. + +Improvements in modeling capabilities continue to be added to the code, +including the ability to rotate surfaces in the Python API, several new +"composite" surfaces, a variety of new methods on :class:`openmc.Material`, +unstructured mesh tallies that leverage the existing DAGMC infrastructure, +effective dose coefficients from ICRP-116, and a new cell instance tally +filter. + +------------ +New Features +------------ + +- Boundary conditions have been refactored and generalized. Rotational periodic + boundary conditions can now be applied to any N-fold symmetric geometry. +- External source distributions have been refactored and extended. Users writing + their own C++ custom sources need to write a class that derives from + ``openmc::Source``. These changes have enabled new functionality, such as: + + - Mixing more than one custom source library together + - Mixing a normal source with a custom source + - Using a file-based source for fixed source simulations + - Using a file-based source for eigenvalue simulations even when the number of + particles doesn't match + +- New capability to read and write a source file based on particles that cross a + surface (known as a "surface source"). +- Various improvements related to depletion: + + - Reactions used in a depletion chain can now be configured through the + `reactions` argument to :meth:`openmc.deplete.Chain.from_endf`. + - Specifying a power of zero during a depletion simulation no longer results + in an unnecessary transport solve. + - Reaction rates can be computed either directly or using multigroup flux + tallies that are used to collapse reaction rates afterward. This is enabled + through the `reaction_rate_mode` and `reaction_rate_opts` to + :class:`openmc.deplete.Operator`. + - Depletion results can be used to create a new :class:`openmc.Materials` + object using the :meth:`openmc.deplete.ResultsList.export_to_materials` + method. + +- Multigroup current and diffusion cross sections can be generated through the + :class:`openmc.mgxs.Current` and :class:`openmc.mgxs.DiffusionCoefficient` + classes. +- Added :func:`openmc.data.isotopes` function that returns a list of naturally + occurring isotopes for a given element. +- Windowed multipole libraries can now be generated directly from the Python API + using :meth:`openmc.data.WindowedMultipole.from_endf`. +- The new :func:`openmc.write_source_file` function allows source files to be + generated programmatically. + + +--------- +Bug Fixes +--------- + +- `Proper detection of MPI wrappers `_ +- `Fix related to declaration order of maps/vectors `_ +- `Check for existence of decay rate attribute `_ +- `Small updates to deal with JEFF 3.3 data `_ +- `Fix for depletion chain generation `_ +- `Fix call to superclass constructor in MeshPlotter `_ +- `Fix for data crossover in VTK files `_ +- `Make sure reaction names are recognized as valid tally scores `_ +- `Fix bug related to logging of particle restarts `_ +- `Examine if region exists before removing redundant surfaces `_ +- `Fix plotting of individual universe levels `_ +- `Mixed materials should inhereit depletable attribute `_ +- `Fix typo in energy units in dose coefficients `_ +- `Fixes for large tally cases `_ +- `Fix verification of volume calculation results `_ +- `Fix calculation of decay energy for depletion chains `_ +- `Fix pointers in CartesianIndependent `_ +- `Ensure correct initialization of members for RegularMesh `_ +- `Add missing import in depletion module `_ +- `Fixed several bugs related to decay-rate `_ +- `Fix how depletion operator distributes burnable materials `_ +- `Fix assignment of elemental carbon in JEFF 3.3 `_ +- `Fix typo in RectangularParallelepiped.__pos__ `_ +- `Fix temperature tolerance with S(a,b) data `_ +- `Fix sampling or normal distribution `_ +- `Fix for SharedArray relaxed memory ordering `_ +- `Check for proper format of source files `_ +- `Ensure (n,gamma) reaction rate tally uses sampled cross section `_ +- `Fix for temperature range behavior `_ + +------------ +Contributors +------------ + +This release contains new contributions from the following people: + +- `Andrew Davis `_ +- `Guillaume Giudicelli `_ +- `Sterling Harper `_ +- `Bryan Herman `_ +- `Yue Jin `_ +- `Andrew Johnson `_ +- `Miriam Kreher `_ +- `Shikhar Kumar `_ +- `Jingang Liang `_ +- `Amanda Lund `_ +- `Adam Nelson `_ +- `April Novak `_ +- `YoungHui Park `_ +- `Ariful Islam Pranto `_ +- `Ron Rahaman `_ +- `Gavin Ridley `_ +- `Paul Romano `_ +- `Jonathan Shimwell `_ +- `Dan Short `_ +- `Patrick Shriwise `_ +- `Roy Stogner `_ +- `John Tramm `_ +- `Cyrus Wyett `_ +- `Jiankai Yu `_ diff --git a/docs/source/releasenotes/index.rst b/docs/source/releasenotes/index.rst index e562ecb52..61bac06f1 100644 --- a/docs/source/releasenotes/index.rst +++ b/docs/source/releasenotes/index.rst @@ -7,6 +7,7 @@ Release Notes .. toctree:: :maxdepth: 1 + 0.12.1 0.12.0 0.11.0 0.10.0 diff --git a/openmc/data/data.py b/openmc/data/data.py index db9972afe..edfdfdae7 100644 --- a/openmc/data/data.py +++ b/openmc/data/data.py @@ -402,7 +402,9 @@ def gnd_name(Z, A, m=0): def isotopes(element): - """Return naturally-occurring isotopes and their abundances + """Return naturally occurring isotopes and their abundances + + .. versionadded:: 0.12.1 Parameters ---------- diff --git a/openmc/data/multipole.py b/openmc/data/multipole.py index 5e8e3086c..17948d50c 100644 --- a/openmc/data/multipole.py +++ b/openmc/data/multipole.py @@ -1018,6 +1018,8 @@ class WindowedMultipole(EqualityMixin): def from_endf(cls, endf_file, log=False, vf_options=None, wmp_options=None): """Generate windowed multipole neutron data from an ENDF evaluation. + .. versionadded:: 0.12.1 + Parameters ---------- endf_file : str diff --git a/openmc/deplete/chain.py b/openmc/deplete/chain.py index 06d7ced58..0c39a92e3 100644 --- a/openmc/deplete/chain.py +++ b/openmc/deplete/chain.py @@ -326,6 +326,8 @@ class Chain: `["(n,2n)", "(n,gamma)"]`. Note that fission is always included if it is present. A complete listing of transmutation reactions can be found in :data:`openmc.deplete.chain.REACTIONS`. + + .. versionadded:: 0.12.1 progress : bool, optional Flag to print status messages during processing. Does not effect warning messages diff --git a/openmc/deplete/results_list.py b/openmc/deplete/results_list.py index c0639d53f..2dc486feb 100644 --- a/openmc/deplete/results_list.py +++ b/openmc/deplete/results_list.py @@ -302,6 +302,8 @@ class ResultsList(list): def export_to_materials(self, burnup_index, nuc_with_data=None) -> Materials: """Return openmc.Materials object based on results at a given step + .. versionadded:: 0.12.1 + Parameters ---------- burn_index : int diff --git a/openmc/mgxs/mgxs.py b/openmc/mgxs/mgxs.py index 0f1bf56a1..89b223a35 100644 --- a/openmc/mgxs/mgxs.py +++ b/openmc/mgxs/mgxs.py @@ -3020,6 +3020,8 @@ class DiffusionCoefficient(TransportXS): To incorporate the effect of scattering multiplication in the above relation, the `nu` parameter can be set to `True`. + .. versionadded:: 0.12.1 + Parameters ---------- domain : openmc.Material or openmc.Cell or openmc.Universe or openmc.RegularMesh @@ -6610,6 +6612,8 @@ class MeshSurfaceMGXS(MGXS): .. note:: Users should instantiate the subclasses of this abstract class. + .. versionadded:: 0.12.1 + Parameters ---------- domain : openmc.RegularMesh @@ -6992,6 +6996,8 @@ class Current(MeshSurfaceMGXS): \frac{\int_{r \in S} dS \int_{E_g}^{E_{g-1}} dE \; J(r, E)}{\int_{r \in S} dS \int_{E_g}^{E_{g-1}} dE}. + .. versionadded:: 0.12.1 + Parameters ---------- domain : openmc.RegularMesh From ec93c742530ef1b09d2140217edf06e077cea7fe Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Mar 2021 14:48:47 -0600 Subject: [PATCH 47/68] Move C API notebook to examples/jupyter directory --- docs/source/examples/capi.ipynb | 1 + docs/source/examples/CAPI.ipynb => examples/jupyter/capi.ipynb | 0 2 files changed, 1 insertion(+) create mode 120000 docs/source/examples/capi.ipynb rename docs/source/examples/CAPI.ipynb => examples/jupyter/capi.ipynb (100%) diff --git a/docs/source/examples/capi.ipynb b/docs/source/examples/capi.ipynb new file mode 120000 index 000000000..f69a37093 --- /dev/null +++ b/docs/source/examples/capi.ipynb @@ -0,0 +1 @@ +../../../examples/jupyter/capi.ipynb \ No newline at end of file diff --git a/docs/source/examples/CAPI.ipynb b/examples/jupyter/capi.ipynb similarity index 100% rename from docs/source/examples/CAPI.ipynb rename to examples/jupyter/capi.ipynb From 26484bf3b5dd07558035e043ffe320fada01e749 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Mar 2021 14:50:43 -0600 Subject: [PATCH 48/68] Add header block on C API notebook --- docs/source/examples/index.rst | 2 +- examples/jupyter/capi.ipynb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/source/examples/index.rst b/docs/source/examples/index.rst index 7dbc90140..38bc51e26 100644 --- a/docs/source/examples/index.rst +++ b/docs/source/examples/index.rst @@ -19,7 +19,7 @@ General Usage post-processing pandas-dataframes tally-arithmetic - CAPI + capi expansion-filters search nuclear-data diff --git a/examples/jupyter/capi.ipynb b/examples/jupyter/capi.ipynb index eab0604da..e696d6b01 100644 --- a/examples/jupyter/capi.ipynb +++ b/examples/jupyter/capi.ipynb @@ -4,6 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "# Using the C/C++ API\n", "This notebook shows how to use the OpenMC C/C++ API through the openmc.lib module. This module is particularly useful for multiphysics coupling because it allows you to update the density of materials and the temperatures of cells in memory, without stopping the simulation.\n", "\n", "Warning: these bindings are still somewhat experimental and may be subject to change in future versions of OpenMC." @@ -466,7 +467,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.0" + "version": "3.8.3" } }, "nbformat": 4, From bbb510a9bacc625430164283a35a60fae2822b8f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Fri, 5 Mar 2021 14:59:48 -0600 Subject: [PATCH 49/68] Avoid warning about TriggerMetric not_active --- include/openmc/error.h | 2 +- src/tallies/trigger.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/openmc/error.h b/include/openmc/error.h index 4c4efe6a7..053783446 100644 --- a/include/openmc/error.h +++ b/include/openmc/error.h @@ -10,7 +10,7 @@ #include "openmc/capi.h" #include "openmc/settings.h" -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(__clang__) #define UNREACHABLE() __builtin_unreachable() #else #define UNREACHABLE() (void)0 diff --git a/src/tallies/trigger.cpp b/src/tallies/trigger.cpp index 56f5c4c56..40eb3290f 100644 --- a/src/tallies/trigger.cpp +++ b/src/tallies/trigger.cpp @@ -87,6 +87,8 @@ check_tally_triggers(double& ratio, int& tally_id, int& score) case TriggerMetric::relative_error: uncertainty = rel_err; break; + case TriggerMetric::not_active: + UNREACHABLE(); } // Compute the uncertainty / threshold ratio. From 952bd36197dfdb5636da684b595e5a91e114be53 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 06:53:40 -0600 Subject: [PATCH 50/68] Remove PPA install instructions --- docs/source/quickinstall.rst | 30 ---------- docs/source/usersguide/install.rst | 96 +++++++++++------------------- 2 files changed, 35 insertions(+), 91 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 5078b5d64..c996ecf29 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -42,36 +42,6 @@ the environment, run: conda activate openmc-env --------------------------------- -Installing on Ubuntu through PPA --------------------------------- - -For users with Ubuntu 15.04 or later, a binary package for OpenMC is available -through a `Personal Package Archive`_ (PPA) and can be installed through the -`APT package manager`_. First, add the following PPA to the repository sources: - -.. code-block:: sh - - sudo apt-add-repository ppa:paulromano/staging - -Next, resynchronize the package index files: - -.. code-block:: sh - - sudo apt update - -Now OpenMC should be recognized within the repository and can be installed: - -.. code-block:: sh - - sudo apt install openmc - -Binary packages from this PPA may exist for earlier versions of Ubuntu, but they -are no longer supported. - -.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/ubuntu/staging -.. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto - ------------------------------------------- Installing on Linux/Mac/Windows with Docker ------------------------------------------- diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 549f8b1b0..46a427aed 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -42,38 +42,6 @@ the environment, run: conda activate openmc-env -.. _install_ppa: - ------------------------------ -Installing on Ubuntu with PPA ------------------------------ - -For users with Ubuntu 15.04 or later, a binary package for OpenMC is available -through a `Personal Package Archive`_ (PPA) and can be installed through the -`APT package manager`_. First, add the following PPA to the repository sources: - -.. code-block:: sh - - sudo apt-add-repository ppa:paulromano/staging - -Next, resynchronize the package index files: - -.. code-block:: sh - - sudo apt update - -Now OpenMC should be recognized within the repository and can be installed: - -.. code-block:: sh - - sudo apt install openmc - -Binary packages from this PPA may exist for earlier versions of Ubuntu, but they -are no longer supported. - -.. _Personal Package Archive: https://launchpad.net/~paulromano/+archive/ubuntu/staging -.. _APT package manager: https://help.ubuntu.com/community/AptGet/Howto - .. _install-spack: ---------------------------------- @@ -84,9 +52,9 @@ Spack_ is a package management tool designed to support multiple versions and configurations of software on a wide variety of platforms and environments. Please follow Spack's `setup guide`_ to configure the Spack system. -The OpenMC Spack recipe has been configured with variants that match most options -provided in the CMakeLists.txt file. To see a list of these variants and other -information use: +The OpenMC Spack recipe has been configured with variants that match most +options provided in the CMakeLists.txt file. To see a list of these variants and +other information use: .. code-block:: sh @@ -109,7 +77,8 @@ Spack recipe can be found with the following command: The only variant for the Python API is ``mpi``. -The most basic installation of OpenMC can be accomplished by entering the following command: +The most basic installation of OpenMC can be accomplished by entering the +following command: .. code-block:: @@ -117,14 +86,17 @@ The most basic installation of OpenMC can be accomplished by entering the follow .. caution:: - When installing any Spack package, dependencies are assumed to be at configured defaults unless otherwise specfied in the - specification on the command line. In the above example, assuming the default options weren't changed in Spack's package - configuration, py-openmc will link against a non-optimized non-MPI openmc. Even if an optimized openmc was built separately, - it will rebuild openmc with optimization OFF. Thus, if you are trying to link against dependencies that were configured - different than defaults, ``^openmc[variants]`` will have to be present in the command. + When installing any Spack package, dependencies are assumed to be at + configured defaults unless otherwise specfied in the specification on the + command line. In the above example, assuming the default options weren't + changed in Spack's package configuration, py-openmc will link against a + non-optimized non-MPI openmc. Even if an optimized openmc was built + separately, it will rebuild openmc with optimization OFF. Thus, if you are + trying to link against dependencies that were configured different than + defaults, ``^openmc[variants]`` will have to be present in the command. -For a more performant build of OpenMC with optimization turned ON and MPI provided by OpenMPI, the following command can be -used: +For a more performant build of OpenMC with optimization turned ON and MPI +provided by OpenMPI, the following command can be used: .. code-block:: sh @@ -136,20 +108,22 @@ used: .. tip:: - When installing py-openmc, it will use Spack's preferred Python. For example, assuming Spack's preferred Python - is 3.8.7, to build py-openmc against the latest Python 3.7 instead, ``^python@3.7.0:3.7.99`` should be added to the - specification on the command line. Additionally, a compiler type and version can be specified at the end of the - command using ``%gcc@``, ``%intel@``, etc. + When installing py-openmc, it will use Spack's preferred Python. For + example, assuming Spack's preferred Python is 3.8.7, to build py-openmc + against the latest Python 3.7 instead, ``^python@3.7.0:3.7.99`` should be + added to the specification on the command line. Additionally, a compiler + type and version can be specified at the end of the command using + ``%gcc@``, ``%intel@``, etc. -A useful tool in Spack is to look at the dependency tree before installation. This can be observed using -Spack's `spec` tool: +A useful tool in Spack is to look at the dependency tree before installation. +This can be observed using Spack's `spec` tool: .. code-block:: spack spec py-openmc+mpi ^openmc+optimize -Once installed, environment/lmod modules can be generated or Spack's `load` feature -can be used to access the installed packages. +Once installed, environment/lmod modules can be generated or Spack's `load` +feature can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ .. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html @@ -449,16 +423,16 @@ section library along with windowed multipole data. Please refer to our Installing Python API --------------------- -If you installed OpenMC using :ref:`Conda ` or :ref:`PPA -`, no further steps are necessary in order to use OpenMC's -:ref:`Python API `. However, if you are :ref:`installing from source -`, the Python API is not installed by default when ``make -install`` is run because in many situations it doesn't make sense to install a -Python package in the same location as the ``openmc`` executable (for example, -if you are installing the package into a `virtual environment -`_). The easiest way to install -the :mod:`openmc` Python package is to use pip_, which is included by default in -Python 3.4+. From the root directory of the OpenMC distribution/repository, run: +If you installed OpenMC using :ref:`Conda `, no further steps are +necessary in order to use OpenMC's :ref:`Python API `. However, if +you are :ref:`installing from source `, the Python API is not +installed by default when ``make install`` is run because in many situations it +doesn't make sense to install a Python package in the same location as the +``openmc`` executable (for example, if you are installing the package into a +`virtual environment `_). The +easiest way to install the :mod:`openmc` Python package is to use pip_, which is +included by default in Python 3.4+. From the root directory of the OpenMC +distribution/repository, run: .. code-block:: sh From ce22b1905c2c372dc9181a18f08656d5c0ea068c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 07:00:32 -0600 Subject: [PATCH 51/68] Add Docker instructions to regular installation section --- docs/source/quickinstall.rst | 14 +++++++++----- docs/source/usersguide/install.rst | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index c996ecf29..f0acf3593 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -47,18 +47,22 @@ Installing on Linux/Mac/Windows with Docker ------------------------------------------- OpenMC can be easily deployed using `Docker `_ on any -Windows, Mac or Linux system. With Docker running, execute the following -command in the shell to download and run a `Docker image`_ with the most recent release of OpenMC from `DockerHub `_ called ``openmc/openmc:v0.10.0``: +Windows, Mac, or Linux system. With Docker running, execute the following command +in the shell to download and run a `Docker image`_ with the most recent release +of OpenMC from `DockerHub `_ called +``openmc/openmc:v0.10.0``: .. code-block:: sh docker run openmc/openmc:v0.10.0 -This will take several minutes to run depending on your internet download speed. The command will place you in an interactive shell running in a `Docker container`_ with OpenMC installed. +This will take several minutes to run depending on your internet download speed. +The command will place you in an interactive shell running in a `Docker +container`_ with OpenMC installed. .. note:: The ``docker run`` command supports many `options`_ for spawning - containers -- including `mounting volumes`_ from the host - filesystem -- which many users will find useful. + containers including `mounting volumes`_ from the host filesystem, + which many users will find useful. .. _Docker image: https://docs.docker.com/engine/reference/commandline/images/ .. _Docker container: https://www.docker.com/resources/what-container diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index 46a427aed..d5c64d842 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -42,6 +42,33 @@ the environment, run: conda activate openmc-env +------------------------------------------- +Installing on Linux/Mac/Windows with Docker +------------------------------------------- + +OpenMC can be easily deployed using `Docker `_ on any +Windows, Mac, or Linux system. With Docker running, execute the following +command in the shell to download and run a `Docker image`_ with the most recent +release of OpenMC from `DockerHub `_ called +``openmc/openmc:v0.10.0``: + +.. code-block:: sh + + docker run openmc/openmc:v0.10.0 + +This will take several minutes to run depending on your internet download speed. +The command will place you in an interactive shell running in a `Docker +container`_ with OpenMC installed. + +.. note:: The ``docker run`` command supports many `options`_ for spawning + containers including `mounting volumes`_ from the host filesystem, + which many users will find useful. + +.. _Docker image: https://docs.docker.com/engine/reference/commandline/images/ +.. _Docker container: https://www.docker.com/resources/what-container +.. _options: https://docs.docker.com/engine/reference/commandline/run/ +.. _mounting volumes: https://docs.docker.com/storage/volumes/ + .. _install-spack: ---------------------------------- @@ -128,6 +155,7 @@ feature can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ .. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html + .. _install_source: ---------------------- From b66664cbf6a31dd8a9f9a2bb90fe9804eeaecad0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 08:03:31 -0600 Subject: [PATCH 52/68] Elaborate on choice of number of particles/batches in docs --- docs/source/quickinstall.rst | 14 ++--- docs/source/usersguide/settings.rst | 81 +++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index f0acf3593..726611ac5 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -91,13 +91,13 @@ can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ .. _setup guide: https://spack.readthedocs.io/en/latest/getting_started.html ---------------------------------------- -Installing from Source on Ubuntu 15.04+ ---------------------------------------- +-------------------------------- +Installing from Source on Ubuntu +-------------------------------- To build OpenMC from source, several :ref:`prerequisites ` are -needed. If you are using Ubuntu 15.04 or higher, all prerequisites can be -installed directly from the package manager. +needed. If you are using Ubuntu or higher, all prerequisites can be installed +directly from the package manager: .. code-block:: sh @@ -136,8 +136,8 @@ should specify an installation directory where you have write access, e.g. The :mod:`openmc` Python package must be installed separately. The easiest way to install it is using `pip `_, which is -included by default in Python 2.7 and Python 3.4+. From the root directory of -the OpenMC distribution/repository, run: +included by default in Python 3.4+. From the root directory of the OpenMC +distribution/repository, run: .. code-block:: sh diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index df22328e3..1f9636d39 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -54,9 +54,9 @@ If you don't specify a run mode, the default run mode is 'eigenvalue'. .. _usersguide_particles: -------------------- -Number of Particles -------------------- +------------ +Run Strategy +------------ For a fixed source simulation, the total number of source particle histories simulated is broken up into a number of *batches*, each corresponding to a @@ -88,6 +88,79 @@ for accumulating tallies. settings.batches = 150 settings.inactive = 5 +.. _usersguide_batches: + +Number of Batches +----------------- + +In general, the stochastic uncertainty in your simulation results is directly +related to how many total active particles are simulated (the product of the +number of active batches, number of generations per batch, and number of +particles). At a minimum, you should use enough active batches so that the +central limit theorem is satisfied (about 30). Otherwise, reducing the overall +uncertainty in your simulation by a factor of 2 will require using 4 times as +many batches (since the standard deviation decreases as :math:`1/\sqrt{N}`). + +Number of Inactive Batches +-------------------------- + +For :math:`k` eigenvalue simulations, the source distribution is not known a +priori. Thus, a "guess" of the source distribution is made and then iterated on, +with the source evolving closer to the true distribution at each iteration. Once +the source distribution has converged, it is then safe to start accumulating +tallies. Consequently, a preset number of inactive batches are run before the +active batches (where tallies are turned on) begin. The number of inactive +batches necessary to reach a converged source depends on the spatial extent of +the problem, its dominance ratio, what boundary conditions are used, and many +other factors. For small problems, using 50--100 inactive batches is likely +sufficient. For larger models, many hundreds of inactive batches may be +necessary. Users are recommended to use the :ref:`Shannon entropy +` diagnostic as a way of determining how many inactive +batches are necessary. + +Specifying the initial source used for the very first batch is described in +:ref:`below `. Although the initial source is arbitrary in +the sense that any source will eventually converge to the correct distribution, +using a source guess that is closer to the actual converged source distribution +will translate into needing fewer inactive batches (and hence less simulation +time). + +For fixed source simulations, the source distribution is known exactly, so no +inactive batches are needed. In this case the :attr:`Settings.inactive` +attribute can be omitted since it defaults to zero. + +Number of Generations per Batch +------------------------------- + +The standard deviation of tally results is calculated assuming that all +realizations (batches) are independent. However, in a :math:`k` eigenvalue +calculation, the source sites for each batch are produced from fissions in the +preceding batch, resulting in a correlation between successive batches. This +correlation can result in an underprediction of the variance. That is, the +variance reported is actually less than the true variance. To mitigate this +effect, OpenMC allows you to group together multiple fission generations into a +single batch for statistical purposes, rather than having each fission +generation be a separate batch, which is the default behavior. + +Number of Particles per Generation +---------------------------------- + +There are several considerations for choosing the number of particles per +generation. As discussed in :ref:`usersguide_batches`, the total number of +active particles will determine the level of stochastic uncertainty in +simulation results, so using a higher number of particles will result in less +uncertainty. For parallel simulations that use OpenMP and/or MPI, the number of +particles per generation should be large enough to ensure good load balancing +between threads. For example, if you are running on a single processor with 32 +cores, each core should have at least 100 particles or so (i.e., at least 3,200 +particles per generation should be used). Using a larger number of particles per +generation can also help reduce the cost of synchronization and communication +between batches. For :math:`k` eigenvalue calculations, experts recommend_ at +least 10,000 particles per generation to avoid any bias in the estimate of +:math:`k` eigenvalue or tallies. + +.. _recommend: https://permalink.lanl.gov/object/tr?what=info:lanl-repo/lareport/LA-UR-09-03136 + .. _usersguide_source: ----------------------------- @@ -301,6 +374,8 @@ the source class when it is created: As with the basic custom source functionality, the custom source library location must be provided in the :attr:`openmc.Source.library` attribute. +.. _usersguide_entropy: + --------------- Shannon Entropy --------------- From 4f9bbca7f338ceedf97199b23724e021c0f20385 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 09:20:51 -0600 Subject: [PATCH 53/68] Add note about specifying volume for depletion --- docs/source/usersguide/depletion.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/usersguide/depletion.rst b/docs/source/usersguide/depletion.rst index d550222cf..6fa2b89c9 100644 --- a/docs/source/usersguide/depletion.rst +++ b/docs/source/usersguide/depletion.rst @@ -30,6 +30,12 @@ operator class requires a :class:`openmc.Geometry` instance and a Any material that contains a fissionable nuclide is depleted by default, but this can behavior can be changed with the :attr:`Material.depletable` attribute. + +.. important:: The volume must be specified for each material that is depleted by + setting the :attr:`Material.volume` attribute. This is necessary + in order to calculate the proper normalization of tally results + based on the source rate. + :mod:`openmc.deplete` supports multiple time-integration methods for determining material compositions over time. Each method appears as a different class. For example, :class:`openmc.deplete.CECMIntegrator` runs a depletion calculation From e4adb9f9b9fca2d78e7158129d61707c735dbcd2 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 09:25:35 -0600 Subject: [PATCH 54/68] Remove description of old scripts --- docs/source/usersguide/scripts.rst | 55 ------------------------------ 1 file changed, 55 deletions(-) diff --git a/docs/source/usersguide/scripts.rst b/docs/source/usersguide/scripts.rst index b10e74f93..963e91cf2 100644 --- a/docs/source/usersguide/scripts.rst +++ b/docs/source/usersguide/scripts.rst @@ -102,61 +102,6 @@ otherwise. --fission_energy_release FISSION_ENERGY_RELEASE HDF5 file containing fission energy release data -.. _scripts_compton: - ------------------------ -``openmc-make-compton`` ------------------------ - -This script generates an HDF5 file called ``compton_profiles.h5`` that contains -Compton profile data using an existing data library from `Geant4 -`_. Note that OpenMC includes this data file by -default so it should not be necessary in practice to generate it yourself. - - -.. _scripts_depletion_chain: - -------------------------------- -``openmc-make-depletion-chain`` -------------------------------- - -This script generates a depletion chain file called ``chain_endfb71.xml`` -using ENDF/B-VII.1 nuclear data. If the :envvar:`OPENMC_ENDF_DATA` variable -is not set, and ``"neutron"``, ``"decay"``, ``"nfy"`` directories -do not exist, then ENDF/B-VII.1 data will be downloaded. - -.. _scripts_depletion_chain_casl: - ------------------------------------- -``openmc-make-depletion-chain-casl`` ------------------------------------- - -This script generates a depletion chain called ``chain_casl.xml`` -using ENDF/B-VII.1 nuclear data for a simplified chain. -The nuclides were chosen by CASL-ORIGEN, which can be found in -Appendix A of Kang Seog Kim, `"Specification for the VERA Depletion -Benchmark Suite" `_, -CASL-U-2015-1014-000, Rev. 0, ORNL/TM-2016/53, 2016. -``Te129`` has been added into this chain due to its link to -``I129`` production. - -If the :envvar:`OPENMC_ENDF_DATA` variable is not set, -and ``"neutron"``, ``"decay"``, ``"nfy"`` directories -to not exist, then ENDF/B-VII.1 data will be downloaded. - -.. _scripts_stopping: - -------------------------------- -``openmc-make-stopping-powers`` -------------------------------- - -This script generates an HDF5 file called ``stopping_power.h5`` that contains -radiative and collision stopping powers and mean excitation energy pulled from -the `NIST ESTAR database -`_. Note that OpenMC -includes this data file by default so it should not be necessary in practice to -generate it yourself. - .. _scripts_plot: -------------------------- From f06f5b420e7b182ef59736081e3a6935a49a164c Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 09:26:53 -0600 Subject: [PATCH 55/68] Remove build instructions for Xeon Phi --- docs/source/usersguide/install.rst | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index d5c64d842..f457515a9 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -415,31 +415,6 @@ on Ubuntu on Windows setup. Once you are within bash, obtain the necessary :ref:`prerequisites ` via ``apt``. Finally, follow the :ref:`instructions for compiling on linux `. -Compiling for the Intel Xeon Phi --------------------------------- - -For the second generation Knights Landing architecture, nothing special is -required to compile OpenMC. You may wish to experiment with compiler flags that -control generation of vector instructions to see what configuration gives -optimal performance for your target problem. - -For the first generation Knights Corner architecture, it is necessary to -cross-compile OpenMC. If you are using the Intel compiler, it is necessary to -specify that all objects be compiled with the ``-mmic`` flag as follows: - -.. code-block:: sh - - mkdir build && cd build - CXX=icpc CXXFLAGS=-mmic cmake -Dopenmp=on .. - make - -Note that unless an HDF5 build for the Intel Xeon Phi (Knights Corner) is -already on your target machine, you will need to cross-compile HDF5 for the Xeon -Phi. An `example script`_ to build zlib and HDF5 provides several necessary -workarounds. - -.. _example script: https://github.com/paulromano/install-scripts/blob/master/install-hdf5-mic - Testing Build ------------- From 56fa60983dce7cc606c794e43f5cf144fa29f105 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 8 Mar 2021 09:45:31 -0600 Subject: [PATCH 56/68] Add doc section on file-based sources --- docs/source/usersguide/settings.rst | 25 +++++++++++++++++++++++++ openmc/source.py | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index 1f9636d39..84fc6ff47 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -248,6 +248,31 @@ following would generate a photon source:: For a full list of all classes related to statistical distributions, see :ref:`pythonapi_stats`. +File-based Sources +------------------ + +OpenMC can use a pregenerated HDF5 source file by specfying the ``filename`` +argument to :class:`openmc.Source`:: + + settings.source = openmc.Source(filename='source.h5') + +Statepoint and source files are generated automatically when a simulation is run +and can be used as the starting source in a new simulation. Alternatively, a +source file can be manually generated with the :func:`openmc.write_source_file` +function. This is particularly useful for coupling OpenMC with another program +that generates a source to be used in OpenMC. + +A source file based on particles that cross one or surfaces can be generated +during a simulation using the :attr:`Settings.surf_source_write` attribute:: + + settings.surf_source_write = { + 'surfaces_ids': [1, 2, 3], + 'max_particles': 10000 + } + +In this example, at most 10,000 source particles are stored when particles cross +surfaces with IDs of 1, 2, or 3. + .. _custom_source: Custom Sources diff --git a/openmc/source.py b/openmc/source.py index 3d544f3a1..415aacd96 100644 --- a/openmc/source.py +++ b/openmc/source.py @@ -298,6 +298,10 @@ def write_source_file(source_particles, filename, **kwargs): **kwargs Keyword arguments to pass to :class:`h5py.File` + See Also + -------- + openmc.SourceParticle + """ # Create compound datatype for source particles pos_dtype = np.dtype([('x', ' Date: Thu, 18 Mar 2021 06:39:34 -0500 Subject: [PATCH 57/68] Apply @drewejohnson suggestions from code review Co-authored-by: Andrew Johnson --- docs/source/releasenotes/0.12.1.rst | 5 ++--- docs/source/usersguide/install.rst | 4 ++-- examples/jupyter/nuclear-data.ipynb | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/source/releasenotes/0.12.1.rst b/docs/source/releasenotes/0.12.1.rst index 23f12f62e..b30c6f6bb 100644 --- a/docs/source/releasenotes/0.12.1.rst +++ b/docs/source/releasenotes/0.12.1.rst @@ -42,12 +42,12 @@ New Features - Various improvements related to depletion: - Reactions used in a depletion chain can now be configured through the - `reactions` argument to :meth:`openmc.deplete.Chain.from_endf`. + ``reactions`` argument to :meth:`openmc.deplete.Chain.from_endf`. - Specifying a power of zero during a depletion simulation no longer results in an unnecessary transport solve. - Reaction rates can be computed either directly or using multigroup flux tallies that are used to collapse reaction rates afterward. This is enabled - through the `reaction_rate_mode` and `reaction_rate_opts` to + through the ``reaction_rate_mode`` and ``reaction_rate_opts`` to :class:`openmc.deplete.Operator`. - Depletion results can be used to create a new :class:`openmc.Materials` object using the :meth:`openmc.deplete.ResultsList.export_to_materials` @@ -63,7 +63,6 @@ New Features - The new :func:`openmc.write_source_file` function allows source files to be generated programmatically. - --------- Bug Fixes --------- diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index f457515a9..ba3654612 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -143,13 +143,13 @@ provided by OpenMPI, the following command can be used: ``%gcc@``, ``%intel@``, etc. A useful tool in Spack is to look at the dependency tree before installation. -This can be observed using Spack's `spec` tool: +This can be observed using Spack's ``spec`` tool: .. code-block:: spack spec py-openmc+mpi ^openmc+optimize -Once installed, environment/lmod modules can be generated or Spack's `load` +Once installed, environment/lmod modules can be generated or Spack's ``load`` feature can be used to access the installed packages. .. _Spack: https://spack.readthedocs.io/en/latest/ diff --git a/examples/jupyter/nuclear-data.ipynb b/examples/jupyter/nuclear-data.ipynb index 30d2c09f0..6a8aeb90c 100644 --- a/examples/jupyter/nuclear-data.ipynb +++ b/examples/jupyter/nuclear-data.ipynb @@ -900,7 +900,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If had [Cython](https://cython.org/) installed when you built/installed OpenMC, you should be able to evaluate resonant cross sections from ENDF data directly, i.e., OpenMC will reconstruct resonances behind the scenes for you." + "If you had [Cython](https://cython.org/) installed when you built/installed OpenMC, you should be able to evaluate resonant cross sections from ENDF data directly, i.e., OpenMC will reconstruct resonances behind the scenes for you." ] }, { From e7aab41c6232199bb8fc7fa180a74aefa8ed63db Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 18 Mar 2021 15:58:08 -0500 Subject: [PATCH 58/68] Address comments from @gridley --- docs/source/quickinstall.rst | 5 ++--- docs/source/releasenotes/0.12.1.rst | 2 +- docs/source/usersguide/install.rst | 5 ++--- docs/source/usersguide/settings.rst | 7 ++++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/source/quickinstall.rst b/docs/source/quickinstall.rst index 726611ac5..e416b3efd 100644 --- a/docs/source/quickinstall.rst +++ b/docs/source/quickinstall.rst @@ -49,12 +49,11 @@ Installing on Linux/Mac/Windows with Docker OpenMC can be easily deployed using `Docker `_ on any Windows, Mac, or Linux system. With Docker running, execute the following command in the shell to download and run a `Docker image`_ with the most recent release -of OpenMC from `DockerHub `_ called -``openmc/openmc:v0.10.0``: +of OpenMC from `DockerHub `_: .. code-block:: sh - docker run openmc/openmc:v0.10.0 + docker run openmc/openmc:latest This will take several minutes to run depending on your internet download speed. The command will place you in an interactive shell running in a `Docker diff --git a/docs/source/releasenotes/0.12.1.rst b/docs/source/releasenotes/0.12.1.rst index b30c6f6bb..d8f67cf33 100644 --- a/docs/source/releasenotes/0.12.1.rst +++ b/docs/source/releasenotes/0.12.1.rst @@ -78,7 +78,7 @@ Bug Fixes - `Fix bug related to logging of particle restarts `_ - `Examine if region exists before removing redundant surfaces `_ - `Fix plotting of individual universe levels `_ -- `Mixed materials should inhereit depletable attribute `_ +- `Mixed materials should inherit depletable attribute `_ - `Fix typo in energy units in dose coefficients `_ - `Fixes for large tally cases `_ - `Fix verification of volume calculation results `_ diff --git a/docs/source/usersguide/install.rst b/docs/source/usersguide/install.rst index ba3654612..1a038d084 100644 --- a/docs/source/usersguide/install.rst +++ b/docs/source/usersguide/install.rst @@ -49,12 +49,11 @@ Installing on Linux/Mac/Windows with Docker OpenMC can be easily deployed using `Docker `_ on any Windows, Mac, or Linux system. With Docker running, execute the following command in the shell to download and run a `Docker image`_ with the most recent -release of OpenMC from `DockerHub `_ called -``openmc/openmc:v0.10.0``: +release of OpenMC from `DockerHub `_: .. code-block:: sh - docker run openmc/openmc:v0.10.0 + docker run openmc/openmc:latest This will take several minutes to run depending on your internet download speed. The command will place you in an interactive shell running in a `Docker diff --git a/docs/source/usersguide/settings.rst b/docs/source/usersguide/settings.rst index 84fc6ff47..39a011ec7 100644 --- a/docs/source/usersguide/settings.rst +++ b/docs/source/usersguide/settings.rst @@ -251,7 +251,7 @@ For a full list of all classes related to statistical distributions, see File-based Sources ------------------ -OpenMC can use a pregenerated HDF5 source file by specfying the ``filename`` +OpenMC can use a pregenerated HDF5 source file by specifying the ``filename`` argument to :class:`openmc.Source`:: settings.source = openmc.Source(filename='source.h5') @@ -262,8 +262,9 @@ source file can be manually generated with the :func:`openmc.write_source_file` function. This is particularly useful for coupling OpenMC with another program that generates a source to be used in OpenMC. -A source file based on particles that cross one or surfaces can be generated -during a simulation using the :attr:`Settings.surf_source_write` attribute:: +A source file based on particles that cross one or more surfaces can be +generated during a simulation using the :attr:`Settings.surf_source_write` +attribute:: settings.surf_source_write = { 'surfaces_ids': [1, 2, 3], From 9f37f1ada10548ddf05e37a7faca3442f16c012e Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Mon, 22 Mar 2021 15:32:27 -0500 Subject: [PATCH 59/68] Remove -dev tag from version number --- docs/source/conf.py | 3 ++- include/openmc/version.h.in | 2 +- openmc/__init__.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 7c9ba27d6..1a2a18d91 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -72,7 +72,7 @@ copyright = '2011-2021, Massachusetts Institute of Technology and OpenMC contrib # The short X.Y version. version = "0.12" # The full version, including alpha/beta/rc tags. -release = "0.12.1-dev" +release = "0.12.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -215,6 +215,7 @@ latex_elements = { \hypersetup{bookmarksdepth=3} \setcounter{tocdepth}{2} \numberwithin{equation}{section} +\DeclareUnicodeCharacter{03B1}{$\alpha$} """, 'printindex': r"" } diff --git a/include/openmc/version.h.in b/include/openmc/version.h.in index a7cf51d04..e094c9d02 100644 --- a/include/openmc/version.h.in +++ b/include/openmc/version.h.in @@ -6,7 +6,7 @@ namespace openmc { constexpr int VERSION_MAJOR {@OPENMC_VERSION_MAJOR@}; constexpr int VERSION_MINOR {@OPENMC_VERSION_MINOR@}; constexpr int VERSION_RELEASE {@OPENMC_VERSION_RELEASE@}; -constexpr bool VERSION_DEV {true}; +constexpr bool VERSION_DEV {false}; constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; } diff --git a/openmc/__init__.py b/openmc/__init__.py index 3e66c5411..b02bd5752 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -34,4 +34,4 @@ from . import examples # Import a few convencience functions that used to be here from openmc.model import rectangular_prism, hexagonal_prism -__version__ = '0.12.1-dev' +__version__ = '0.12.1' From 1317365cd7625647b521da0c645dc3c99a72b2c1 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 25 Mar 2021 11:31:54 +0000 Subject: [PATCH 60/68] moving from dagmc develop to latest tagged release --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index a43b4f31d..9a41b7c08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -117,7 +117,7 @@ RUN if [ "$include_dagmc" = "true" ] ; \ RUN if [ "$include_dagmc" = "true" ] ; \ then mkdir DAGMC ; \ cd DAGMC ; \ - git clone --single-branch --branch develop https://github.com/svalinn/DAGMC.git ; \ + git clone --single-branch --branch 3.2.0 https://github.com/svalinn/DAGMC.git ; \ mkdir build ; \ cd build ; \ cmake ../DAGMC -DBUILD_TALLY=ON \ From 5eddf6dfa578c0815ff1b35f6bb1b6746248faa4 Mon Sep 17 00:00:00 2001 From: Jonathan Shimwell Date: Thu, 25 Mar 2021 18:18:10 +0000 Subject: [PATCH 61/68] moved to specific versions for embree and moab --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9a41b7c08..89fec615e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,7 +65,7 @@ RUN if [ "$include_dagmc" = "true" ] ; \ # Clone and install Embree RUN if [ "$include_dagmc" = "true" ] ; \ - then git clone --single-branch --branch master https://github.com/embree/embree.git ; \ + then git clone --single-branch --branch v3.12.2 https://github.com/embree/embree.git ; \ cd embree ; \ mkdir build ; \ cd build ; \ @@ -81,7 +81,7 @@ RUN if [ "$include_dagmc" = "true" ] ; \ mkdir MOAB ; \ cd MOAB ; \ mkdir build ; \ - git clone --single-branch --branch master https://bitbucket.org/fathomteam/moab.git ; \ + git clone --single-branch --branch 5.2.1 https://bitbucket.org/fathomteam/moab.git ; \ cd build ; \ cmake ../moab -DENABLE_HDF5=ON \ -DENABLE_NETCDF=ON \ From f97d9d2a2d7724abceb1519bf4200caa76592a5f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 25 Mar 2021 13:24:49 -0500 Subject: [PATCH 62/68] Fix release notes for 0.12.1 --- docs/source/releasenotes/0.12.1.rst | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/docs/source/releasenotes/0.12.1.rst b/docs/source/releasenotes/0.12.1.rst index d8f67cf33..17ee1fb36 100644 --- a/docs/source/releasenotes/0.12.1.rst +++ b/docs/source/releasenotes/0.12.1.rst @@ -8,18 +8,11 @@ What's New in 0.12.1 Summary ------- -This release of OpenMC includes an assortment of new features and many bug fixes. -In particular, the :mod:`openmc.deplete` module has been heavily tested which -has resulted in a number of usability improvements, bug fixes, and other -enhancements. Energy deposition calculations, particularly for coupled -neutron-photon simulations, have been improved as well. - -Improvements in modeling capabilities continue to be added to the code, -including the ability to rotate surfaces in the Python API, several new -"composite" surfaces, a variety of new methods on :class:`openmc.Material`, -unstructured mesh tallies that leverage the existing DAGMC infrastructure, -effective dose coefficients from ICRP-116, and a new cell instance tally -filter. +This release of OpenMC includes an assortment of new features and many bug +fixes. The :mod:`openmc.deplete` module incorporates a number of improvements in +usability, accuracy, and performance. Other enhancements include generalized +rotational periodic boundary conditions, expanded source modeling capabilities, +and a capability to generate windowed multipole library files from ENDF files. ------------ New Features From 41d78a9b4e7f1bcd5f6b86dd1869124008cd928f Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Thu, 25 Mar 2021 14:43:19 -0500 Subject: [PATCH 63/68] Change version number to 0.13.0-dev --- CMakeLists.txt | 4 ++-- docs/source/conf.py | 4 ++-- include/openmc/version.h.in | 2 +- openmc/__init__.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9694d60b3..6008b8ad1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ project(openmc C CXX) # Set version numbers set(OPENMC_VERSION_MAJOR 0) -set(OPENMC_VERSION_MINOR 12) -set(OPENMC_VERSION_RELEASE 1) +set(OPENMC_VERSION_MINOR 13) +set(OPENMC_VERSION_RELEASE 0) set(OPENMC_VERSION ${OPENMC_VERSION_MAJOR}.${OPENMC_VERSION_MINOR}.${OPENMC_VERSION_RELEASE}) configure_file(include/openmc/version.h.in "${CMAKE_BINARY_DIR}/include/openmc/version.h" @ONLY) diff --git a/docs/source/conf.py b/docs/source/conf.py index 1a2a18d91..ed790a2e2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -70,9 +70,9 @@ copyright = '2011-2021, Massachusetts Institute of Technology and OpenMC contrib # built documents. # # The short X.Y version. -version = "0.12" +version = "0.13" # The full version, including alpha/beta/rc tags. -release = "0.12.1" +release = "0.13.0-dev" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/include/openmc/version.h.in b/include/openmc/version.h.in index e094c9d02..a7cf51d04 100644 --- a/include/openmc/version.h.in +++ b/include/openmc/version.h.in @@ -6,7 +6,7 @@ namespace openmc { constexpr int VERSION_MAJOR {@OPENMC_VERSION_MAJOR@}; constexpr int VERSION_MINOR {@OPENMC_VERSION_MINOR@}; constexpr int VERSION_RELEASE {@OPENMC_VERSION_RELEASE@}; -constexpr bool VERSION_DEV {false}; +constexpr bool VERSION_DEV {true}; constexpr std::array VERSION {VERSION_MAJOR, VERSION_MINOR, VERSION_RELEASE}; } diff --git a/openmc/__init__.py b/openmc/__init__.py index b02bd5752..50246cdd8 100644 --- a/openmc/__init__.py +++ b/openmc/__init__.py @@ -34,4 +34,4 @@ from . import examples # Import a few convencience functions that used to be here from openmc.model import rectangular_prism, hexagonal_prism -__version__ = '0.12.1' +__version__ = '0.13.0-dev' From bb9ef684ae7be0d084bcf35d0abe323b8c4866f1 Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Tue, 30 Mar 2021 13:54:33 -0400 Subject: [PATCH 64/68] fix calculation of min and max neutron data temperature --- src/nuclide.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index f925861ab..c7c84f23a 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -33,7 +33,7 @@ namespace data { std::array energy_min {0.0, 0.0}; std::array energy_max {INFTY, INFTY}; double temperature_min {0.0}; -double temperature_max {INFTY}; +double temperature_max {0.0}; std::unordered_map nuclide_map; std::vector> nuclides; } // namespace data @@ -169,11 +169,10 @@ Nuclide::Nuclide(hid_t group, const std::vector& temperature) // Sort temperatures to read std::sort(temps_to_read.begin(), temps_to_read.end()); - double T_min_read = *std::min_element(temps_to_read.cbegin(), temps_to_read.cend()); - double T_max_read = *std::max_element(temps_to_read.cbegin(), temps_to_read.cend()); - - data::temperature_min = std::max(data::temperature_min, T_min_read); - data::temperature_max = std::min(data::temperature_max, T_max_read); + data::temperature_min = + std::min(data::temperature_min, static_cast(temps_to_read.front())); + data::temperature_max = + std::max(data::temperature_max, static_cast(temps_to_read.back())); hid_t energy_group = open_group(group, "energy"); for (const auto& T : temps_to_read) { From d4cf2b321de19b43123c6536cf72a5cf2c5b7a7d Mon Sep 17 00:00:00 2001 From: Gavin Ridley Date: Tue, 30 Mar 2021 16:32:58 -0400 Subject: [PATCH 65/68] initialize min temperature as infty, so it strictly decreases --- src/nuclide.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nuclide.cpp b/src/nuclide.cpp index c7c84f23a..5ecbdc3b4 100644 --- a/src/nuclide.cpp +++ b/src/nuclide.cpp @@ -32,7 +32,7 @@ namespace openmc { namespace data { std::array energy_min {0.0, 0.0}; std::array energy_max {INFTY, INFTY}; -double temperature_min {0.0}; +double temperature_min {INFTY}; double temperature_max {0.0}; std::unordered_map nuclide_map; std::vector> nuclides; From 114a1062eadd851f0a0a16586ead5b3217e0e3a7 Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 31 Mar 2021 23:23:06 -0500 Subject: [PATCH 66/68] Adding a test for adjacent rotated and unrotated cells. --- .../adj_cell_rotation/__init__.py | 0 .../adj_cell_rotation/inputs_true.dat | 38 ++++++++++++++ .../adj_cell_rotation/results_true.dat | 2 + .../adj_cell_rotation/test.py | 50 +++++++++++++++++++ 4 files changed, 90 insertions(+) create mode 100644 tests/regression_tests/adj_cell_rotation/__init__.py create mode 100644 tests/regression_tests/adj_cell_rotation/inputs_true.dat create mode 100644 tests/regression_tests/adj_cell_rotation/results_true.dat create mode 100644 tests/regression_tests/adj_cell_rotation/test.py diff --git a/tests/regression_tests/adj_cell_rotation/__init__.py b/tests/regression_tests/adj_cell_rotation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/regression_tests/adj_cell_rotation/inputs_true.dat b/tests/regression_tests/adj_cell_rotation/inputs_true.dat new file mode 100644 index 000000000..24b00199b --- /dev/null +++ b/tests/regression_tests/adj_cell_rotation/inputs_true.dat @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + eigenvalue + 10000 + 10 + 5 + + + -4.0 -4.0 -4.0 4.0 4.0 4.0 + + + diff --git a/tests/regression_tests/adj_cell_rotation/results_true.dat b/tests/regression_tests/adj_cell_rotation/results_true.dat new file mode 100644 index 000000000..a58aa1c8e --- /dev/null +++ b/tests/regression_tests/adj_cell_rotation/results_true.dat @@ -0,0 +1,2 @@ +k-combined: +4.453328E-01 5.918369E-03 diff --git a/tests/regression_tests/adj_cell_rotation/test.py b/tests/regression_tests/adj_cell_rotation/test.py new file mode 100644 index 000000000..25aada5aa --- /dev/null +++ b/tests/regression_tests/adj_cell_rotation/test.py @@ -0,0 +1,50 @@ +import pytest +import openmc + +from tests.testing_harness import PyAPITestHarness + + +@pytest.fixture +def model(): + model = openmc.model.Model() + + fuel = openmc.Material() + fuel.set_density('g/cc', 10.0) + fuel.add_nuclide('U235', 1.0) + + h1 = openmc.Material() + h1.set_density('g/cc', 0.1) + h1.add_nuclide('H1', 0.1) + + inner_sphere = openmc.Sphere(x0=1.0, r=5.0) + + fuel_cell = openmc.Cell(fill=fuel, region=-inner_sphere) + hydrogen_cell = openmc.Cell(fill=h1, region=+inner_sphere) + univ = openmc.Universe(cells=[fuel_cell, hydrogen_cell]) + + # Create one cell on top of the other. Only one + # has a rotation + box = openmc.rectangular_prism(15., 15., 'z', boundary_type='vacuum') + lower_z = openmc.ZPlane(-7.5, boundary_type='vacuum') + upper_z = openmc.ZPlane(22.5, boundary_type='vacuum') + middle_z = openmc.ZPlane(7.5) + + lower_cell = openmc.Cell(fill=univ, region=box & +lower_z & -middle_z) + lower_cell.rotation = (10, 20, 30) + upper_cell = openmc.Cell(fill=univ, region=box & +middle_z & -upper_z) + upper_cell.translation = (0, 0, 15) + + model.geometry = openmc.Geometry(root=[lower_cell, upper_cell]) + + model.settings.particles = 10000 + model.settings.inactive = 5 + model.settings.batches = 10 + source_box = openmc.stats.Box((-4., -4., -4.), (4., 4., 4.)) + model.settings.source = openmc.Source(space=source_box) + + return model + + +def test_rotation(model): + harness = PyAPITestHarness('statepoint.10.h5', model) + harness.main() From 66e7e998af4f37b70b48f6dbf6526724adfbd76e Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 31 Mar 2021 23:31:12 -0500 Subject: [PATCH 67/68] Always call LocalCoord::rotate and set LocalCoord::rotated. --- src/geometry.cpp | 4 +--- src/particle.cpp | 4 ++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/geometry.cpp b/src/geometry.cpp index 8e6519fa8..9d612412b 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -188,9 +188,7 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) coord.r -= c.translation_; // Apply rotation. - if (!c.rotation_.empty()) { - coord.rotate(c.rotation_); - } + coord.rotate(c.rotation_); } else if (c.type_ == Fill::LATTICE) { //======================================================================== diff --git a/src/particle.cpp b/src/particle.cpp index 2e2413174..d05b81d84 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -38,6 +38,10 @@ namespace openmc { void LocalCoord::rotate(const std::vector& rotation) { + if (rotation.empty()) { + this->rotated = false; + return; + } this->r = this->r.rotate(rotation); this->u = this->u.rotate(rotation); this->rotated = true; From a8ce1445859c8be4ff794648693b3f966e7848fd Mon Sep 17 00:00:00 2001 From: Patrick Shriwise Date: Wed, 31 Mar 2021 23:38:25 -0500 Subject: [PATCH 68/68] Resetting coordinates at lower levels in neighbor_list_find_cell. --- src/geometry.cpp | 10 +++++++++- src/particle.cpp | 4 ---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/geometry.cpp b/src/geometry.cpp index 9d612412b..06d35cace 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -188,7 +188,9 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) coord.r -= c.translation_; // Apply rotation. - coord.rotate(c.rotation_); + if (!c.rotation_.empty()) { + coord.rotate(c.rotation_); + } } else if (c.type_ == Fill::LATTICE) { //======================================================================== @@ -245,6 +247,12 @@ find_cell_inner(Particle& p, const NeighborList* neighbor_list) bool neighbor_list_find_cell(Particle& p) { + + // Reset all the deeper coordinate levels. + for (int i = p.n_coord_; i < p.coord_.size(); i++) { + p.coord_[i].reset(); + } + // Get the cell this particle was in previously. auto coord_lvl = p.n_coord_ - 1; auto i_cell = p.coord_[coord_lvl].cell; diff --git a/src/particle.cpp b/src/particle.cpp index d05b81d84..2e2413174 100644 --- a/src/particle.cpp +++ b/src/particle.cpp @@ -38,10 +38,6 @@ namespace openmc { void LocalCoord::rotate(const std::vector& rotation) { - if (rotation.empty()) { - this->rotated = false; - return; - } this->r = this->r.rotate(rotation); this->u = this->u.rotate(rotation); this->rotated = true;