def universes(self, universes): cv.check_iterable_type('lattice universes', universes, Universe, min_depth=2, max_depth=3) self._universes = universes
def write_source_file(source_particles, filename, **kwargs): """Write a source file using a collection of source particles Parameters ---------- source_particles : iterable of SourceParticle Source particles to write to file filename : str or path-like Path to source file to write **kwargs Keyword arguments to pass to :class:`h5py.File` """ # Create compound datatype for source particles pos_dtype = np.dtype([('x', '<f8'), ('y', '<f8'), ('z', '<f8')]) source_dtype = np.dtype([ ('r', pos_dtype), ('u', pos_dtype), ('E', '<f8'), ('wgt', '<f8'), ('delayed_group', '<i4'), ('surf_id', '<i4'), ('particle', '<i4'), ]) # Create array of source particles cv.check_iterable_type("source particles", source_particles, SourceParticle) arr = np.array([s.to_tuple() for s in source_particles], dtype=source_dtype) # Write array to file kwargs.setdefault('mode', 'w') with h5py.File(filename, **kwargs) as fh: fh.attrs['filetype'] = np.string_("source") fh.create_dataset('source_bank', data=arr, dtype=source_dtype)
def universes(self, universes): cv.check_iterable_type('lattice universes', universes, openmc.Universe, min_depth=2, max_depth=3) self._universes = np.asarray(universes)
def nu_fission(self, nu_fission): # The NuFissionXS class does not have the capability to produce # a fission matrix and therefore if this path is pursued, we know # chi must be used. # nu_fission can be given as a vector or a matrix # Vector is used when chi also exists. # Matrix is used when chi does not exist. # We have to check that the correct form is given, but only if # chi already has been set. If not, we just check that this is OK # and set the use_chi flag accordingly # Convert to a numpy array so we can easily get the shape for # checking npnu_fission = np.asarray(nu_fission) check_iterable_type('nu_fission', npnu_fission, Real, max_depth=len(npnu_fission.shape)) if self.use_chi is not None: if self.use_chi: check_value('nu_fission shape', npnu_fission.shape, [self.vector_shape]) else: check_value('nu_fission shape', npnu_fission.shape, [self.matrix_shape]) else: check_value('nu_fission shape', npnu_fission.shape, [self.vector_shape, self.matrix_shape]) # Find out if we have a nu-fission matrix or vector # and set a flag to allow other methods to check this later. self.use_chi = (npnu_fission.shape == self.vector_shape) self._nu_fission = npnu_fission if np.sum(self._nu_fission) > 0.0: self._fissionable = True
def bins(self, bins): if bins is None: self.num_bins = 0 elif self._type is None: msg = 'Unable to set bins for Filter to "{0}" since ' \ 'the Filter type has not yet been set'.format(bins) raise ValueError(msg) # If the bin edge is a single value, it is a Cell, Material, etc. ID if not isinstance(bins, Iterable): bins = [bins] # If the bins are in a collection, convert it to a list else: bins = list(bins) if self.type in ['cell', 'cellborn', 'surface', 'material', 'universe', 'distribcell']: check_iterable_type('filter bins', bins, Integral) for edge in bins: check_greater_than('filter bin', edge, 0, equality=True) elif self._type in ['energy', 'energyout']: for edge in bins: if not isinstance(edge, Real): msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \ 'since it is a non-integer or floating point ' \ 'value'.format(edge, self.type) raise ValueError(msg) elif edge < 0.: msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \ 'since it is a negative value'.format(edge, self.type) raise ValueError(msg) # Check that bin edges are monotonically increasing for index in range(len(bins)): if index > 0 and bins[index] < bins[index-1]: msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ 'since they are not monotonically ' \ 'increasing'.format(bins, self.type) raise ValueError(msg) # mesh filters elif self._type == 'mesh': if not len(bins) == 1: msg = 'Unable to add bins "{0}" to a mesh Filter since ' \ 'only a single mesh can be used per tally'.format(bins) raise ValueError(msg) elif not isinstance(bins[0], Integral): msg = 'Unable to add bin "{0}" to mesh Filter since it ' \ 'is a non-integer'.format(bins[0]) raise ValueError(msg) elif bins[0] < 0: msg = 'Unable to add bin "{0}" to mesh Filter since it ' \ 'is a negative integer'.format(bins[0]) raise ValueError(msg) # If all error checks passed, add bin edges self._bins = np.array(bins)
def bins(self, bins): if self.type is None: msg = 'Unable to set bins for Filter to "{0}" since ' \ 'the Filter type has not yet been set'.format(bins) raise ValueError(msg) # If the bin edge is a single value, it is a Cell, Material, etc. ID if not isinstance(bins, Iterable): bins = [bins] # If the bins are in a collection, convert it to a list else: bins = list(bins) if self.type in [ 'cell', 'cellborn', 'surface', 'material', 'universe', 'distribcell', 'delayedgroup' ]: cv.check_iterable_type('filter bins', bins, Integral) for edge in bins: cv.check_greater_than('filter bin', edge, 0, equality=True) elif self.type in ['energy', 'energyout']: for edge in bins: if not isinstance(edge, Real): msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \ 'since it is a non-integer or floating point ' \ 'value'.format(edge, self.type) raise ValueError(msg) elif edge < 0.: msg = 'Unable to add bin edge "{0}" to a "{1}" Filter ' \ 'since it is a negative value'.format(edge, self.type) raise ValueError(msg) # Check that bin edges are monotonically increasing for index in range(len(bins)): if index > 0 and bins[index] < bins[index - 1]: msg = 'Unable to add bin edges "{0}" to a "{1}" Filter ' \ 'since they are not monotonically ' \ 'increasing'.format(bins, self.type) raise ValueError(msg) # mesh filters elif self.type == 'mesh': if not len(bins) == 1: msg = 'Unable to add bins "{0}" to a mesh Filter since ' \ 'only a single mesh can be used per tally'.format(bins) raise ValueError(msg) elif not isinstance(bins[0], Integral): msg = 'Unable to add bin "{0}" to mesh Filter since it ' \ 'is a non-integer'.format(bins[0]) raise ValueError(msg) elif bins[0] < 0: msg = 'Unable to add bin "{0}" to mesh Filter since it ' \ 'is a negative integer'.format(bins[0]) raise ValueError(msg) # If all error checks passed, add bin edges self._bins = np.array(bins)
def mgxs_types(self, mgxs_types): if mgxs_types == 'all': self._mgxs_types = openmc.mgxs.MGXS_TYPES else: cv.check_iterable_type('mgxs_types', mgxs_types, basestring) for mgxs_type in mgxs_types: cv.check_value('mgxs_type', mgxs_type, openmc.mgxs.MGXS_TYPES) self._mgxs_types = mgxs_types
def highlight_domains(self, geometry, domains, seed=1, alpha=0.5, background='gray'): """Use alpha compositing to highlight one or more domains in the plot. This routine generates a color scheme and applies alpha compositing to make all domains except the highlighted ones appear partially transparent. Parameters ---------- geometry : openmc.Geometry The geometry for which the plot is defined domains : Iterable of Integral A collection of the domain IDs to highlight in the plot seed : Integral The random number seed used to generate the color scheme alpha : Real in [0,1] The value to apply in alpha compisiting background : 3-tuple of Integral or 'white' or 'black' or 'gray' The background color to apply in alpha compisiting """ cv.check_iterable_type('domains', domains, Integral) cv.check_type('alpha', alpha, Real) cv.check_greater_than('alpha', alpha, 0., equality=True) cv.check_less_than('alpha', alpha, 1., equality=True) # Get a background (R,G,B) tuple to apply in alpha compositing if isinstance(background, basestring): if background == 'white': background = (255, 255, 255) elif background == 'black': background = (0, 0, 0) elif background == 'gray': background = (160, 160, 160) else: msg = 'The background "{}" is not defined'.format(background) raise ValueError(msg) cv.check_iterable_type('background', background, Integral) # Generate a color scheme self.colorize(geometry, seed) # Apply alpha compositing to the colors for all domains # other than those the user wishes to highlight for domain_id in self.col_spec: if domain_id not in domains: r, g, b = self.col_spec[domain_id] r = int(((1 - alpha) * background[0]) + (alpha * r)) g = int(((1 - alpha) * background[1]) + (alpha * g)) b = int(((1 - alpha) * background[2]) + (alpha * b)) self._col_spec[domain_id] = (r, g, b)
def scatter(self, scatter): # Convert to a numpy array so we can easily get the shape for # checking npscatter = np.asarray(scatter) check_iterable_type('scatter', npscatter, Real, max_depth=len(npscatter.shape)) check_value('scatter shape', npscatter.shape, [self.pn_matrix_shape]) self._scatter = npscatter
def multiplicity(self, multiplicity): # Convert to a numpy array so we can easily get the shape for # checking npmultiplicity = np.asarray(multiplicity) check_iterable_type('multiplicity', npmultiplicity, Real, max_depth=len(npmultiplicity.shape)) check_value('multiplicity shape', npmultiplicity.shape, [self.matrix_shape]) self._multiplicity = npmultiplicity
def highlight_domains(self, geometry, domains, seed=1, alpha=0.5, background='gray'): """Use alpha compositing to highlight one or more domains in the plot. This routine generates a color scheme and applies alpha compositing to make all domains except the highlighted ones appear partially transparent. Parameters ---------- geometry : openmc.Geometry The geometry for which the plot is defined domains : Iterable of Integral A collection of the domain IDs to highlight in the plot seed : Integral The random number seed used to generate the color scheme alpha : Real in [0,1] The value to apply in alpha compisiting background : 3-tuple of Integral or 'white' or 'black' or 'gray' The background color to apply in alpha compisiting """ cv.check_iterable_type('domains', domains, Integral) cv.check_type('alpha', alpha, Real) cv.check_greater_than('alpha', alpha, 0., equality=True) cv.check_less_than('alpha', alpha, 1., equality=True) # Get a background (R,G,B) tuple to apply in alpha compositing if isinstance(background, basestring): if background == 'white': background = (255, 255, 255) elif background == 'black': background = (0, 0, 0) elif background == 'gray': background = (160, 160, 160) else: msg = 'The background "{}" is not defined'.format(background) raise ValueError(msg) cv.check_iterable_type('background', background, Integral) # Generate a color scheme self.colorize(geometry, seed) # Apply alpha compositing to the colors for all domains # other than those the user wishes to highlight for domain_id in self.col_spec: if domain_id not in domains: r, g, b = self.col_spec[domain_id] r = int(((1-alpha) * background[0]) + (alpha * r)) g = int(((1-alpha) * background[1]) + (alpha * g)) b = int(((1-alpha) * background[2]) + (alpha * b)) self._col_spec[domain_id] = (r, g, b)
def add_xsdatas(self, xsdatas): """Add multiple xsdatas to the file. Parameters ---------- xsdatas : tuple or list of openmc.XSdata XSdatas to add """ check_iterable_type('xsdatas', xsdatas, XSdata) for xsdata in xsdatas: self.add_xsdata(xsdata)
def domains(self, domains): # Use all materials, cells or universes in the geometry as domains if domains == 'all': self._domains = domains # User specified a list of material, cell or universe domains else: if self.domain_type == 'material': cv.check_iterable_type('domain', domains, openmc.Material) all_domains = self.openmc_geometry.get_all_materials() elif self.domain_type in ['cell', 'distribcell']: cv.check_iterable_type('domain', domains, openmc.Cell) all_domains = self.openmc_geometry.get_all_material_cells() elif self.domain_type == 'universe': cv.check_iterable_type('domain', domains, openmc.Universe) all_domains = self.openmc_geometry.get_all_universes() else: raise ValueError('Unable to set domains with domain ' 'type "{}"'.format(self.domain_type)) # Check that each domain can be found in the geometry for domain in domains: if domain not in all_domains: raise ValueError('Domain "{}" could not be found in the ' 'geometry.'.format(domain)) self._domains = domains
def domains(self, domains): # Use all materials, cells or universes in the geometry as domains if domains == 'all': self._domains = domains # User specified a list of material, cell or universe domains else: if self.domain_type == 'material': cv.check_iterable_type('domain', domains, openmc.Material) all_domains = self.openmc_geometry.get_all_materials() elif self.domain_type in ['cell', 'distribcell']: cv.check_iterable_type('domain', domains, openmc.Cell) all_domains = self.openmc_geometry.get_all_material_cells() elif self.domain_type == 'universe': cv.check_iterable_type('domain', domains, openmc.Universe) all_domains = self.openmc_geometry.get_all_universes() else: msg = 'Unable to set domains with ' \ 'domain type "{}"'.format(self.domain_type) raise ValueError(msg) # Check that each domain can be found in the geometry for domain in domains: if domain not in all_domains: msg = 'Domain "{}" could not be found in the ' \ 'geometry.'.format(domain) raise ValueError(msg) self._domains = domains
def calculate_mgxs(this, types, orders=None, temperature=294., cross_sections=None, ce_cross_sections=None, enrichment=None): """Calculates continuous-energy cross sections of a requested type. If the data for the nuclide or macroscopic object in the library is represented as angle-dependent data then this method will return the geometric average cross section over all angles. Parameters ---------- this : openmc.Element, openmc.Nuclide, openmc.Material, or openmc.Macroscopic Object to source data from types : Iterable of values of PLOT_TYPES_MGXS The type of cross sections to calculate orders : Iterable of Integral, optional The scattering order or delayed group index to use for the corresponding entry in types. Defaults to the 0th order for scattering and the total delayed neutron data. temperature : float, optional Temperature in Kelvin to plot. If not specified, a default temperature of 294K will be plotted. Note that the nearest temperature in the library for each nuclide will be used as opposed to using any interpolation. cross_sections : str, optional Location of MGXS HDF5 Library file. Default is None. ce_cross_sections : str, optional Location of continuous-energy cross_sections.xml file. Default is None. This is used only for expanding an openmc.Element object passed as this enrichment : float, optional Enrichment for U235 in weight percent. For example, input 4.95 for 4.95 weight percent enriched U. Default is None (natural composition). Returns ------- energy_grid : numpy.ndarray Energies at which cross sections are calculated, in units of eV data : numpy.ndarray Cross sections calculated at the energy grid described by energy_grid """ # Check types cv.check_type('temperature', temperature, Real) if enrichment: cv.check_type('enrichment', enrichment, Real) cv.check_iterable_type('types', types, string_types) cv.check_type("cross_sections", cross_sections, str) library = openmc.MGXSLibrary.from_hdf5(cross_sections) if isinstance(this, (openmc.Nuclide, openmc.Macroscopic)): mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, temperature) elif isinstance(this, (openmc.Element, openmc.Material)): mgxs = _calculate_mgxs_elem_mat(this, types, library, orders, temperature, ce_cross_sections, enrichment) else: raise TypeError("Invalid type") # Convert the data to the format needed data = np.zeros((len(types), 2 * library.energy_groups.num_groups)) energy_grid = np.zeros(2 * library.energy_groups.num_groups) for g in range(library.energy_groups.num_groups): energy_grid[g * 2: g * 2 + 2] = \ library.energy_groups.group_edges[g: g + 2] # Ensure the energy will show on a log-axis by replacing 0s with a # sufficiently small number energy_grid[0] = max(energy_grid[0], _MIN_E) for line in range(len(types)): for g in range(library.energy_groups.num_groups): data[line, g * 2: g * 2 + 2] = mgxs[line, g] return energy_grid[::-1], data
def _calculate_mgxs_nuc_macro(this, types, library, orders=None, temperature=294.): """Determines the multi-group cross sections of a nuclide or macroscopic object. If the data for the nuclide or macroscopic object in the library is represented as angle-dependent data then this method will return the geometric average cross section over all angles. Parameters ---------- this : openmc.Nuclide or openmc.Macroscopic Object to source data from types : Iterable of str The type of cross sections to calculate; values can either be those in openmc.PLOT_TYPES_MGXS library : openmc.MGXSLibrary MGXS Library containing the data of interest orders : Iterable of Integral, optional The scattering order or delayed group index to use for the corresponding entry in types. Defaults to the 0th order for scattering and the total delayed neutron data. temperature : float, optional Temperature in Kelvin to plot. If not specified, a default temperature of 294K will be plotted. Note that the nearest temperature in the library for each nuclide will be used as opposed to using any interpolation. Returns ------- data : numpy.ndarray Cross sections calculated at the energy grid described by energy_grid """ # Check the parameters and grab order/delayed groups if orders: cv.check_iterable_type('orders', orders, Integral, min_depth=len(types), max_depth=len(types)) else: orders = [None] * len(types) for i, line in enumerate(types): cv.check_type("line", line, str) cv.check_value("line", line, PLOT_TYPES_MGXS) if orders[i]: cv.check_greater_than("order value", orders[i], 0, equality=True) xsdata = library.get_by_name(this.name) if xsdata is not None: # Obtain the nearest temperature t = np.abs(xsdata.temperatures - temperature).argmin() # Get the data data = np.zeros((len(types), library.energy_groups.num_groups)) for i, line in enumerate(types): if 'fission' in line and not xsdata.fissionable: continue elif line == 'unity': data[i, :] = 1. else: # Now we have to get the cross section data and properly # treat it depending on the requested type. # First get the data in a generic fashion temp_data = getattr(xsdata, _PLOT_MGXS_ATTR[line])[t] shape = temp_data.shape[:] # If we have angular data, then want the geometric # average over all provided angles. Since the angles are # equi-distant, un-weighted averaging will suffice if xsdata.representation == 'angle': temp_data = np.mean(temp_data, axis=(0, 1)) # Now we can look at the shape of the data to identify how # it should be modified to produce an array of values # with groups. if shape in (xsdata.xs_shapes["[G']"], xsdata.xs_shapes["[G]"]): # Then the data is already an array vs groups so copy # and move along data[i, :] = temp_data elif shape == xsdata.xs_shapes["[G][G']"]: # Sum the data over outgoing groups to create our array vs # groups data[i, :] = np.sum(temp_data, axis=1) elif shape == xsdata.xs_shapes["[DG]"]: # Then we have a constant vs groups with a value for each # delayed group. The user-provided value of orders tells us # which delayed group we want. If none are provided, then # we sum all the delayed groups together. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i]] else: data[i, :] = np.sum(temp_data[:]) elif shape in (xsdata.xs_shapes["[DG][G']"], xsdata.xs_shapes["[DG][G]"]): # Then we have an array vs groups with values for each # delayed group. The user-provided value of orders tells us # which delayed group we want. If none are provided, then # we sum all the delayed groups together. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i], :] else: data[i, :] = np.sum(temp_data[:, :], axis=0) elif shape == xsdata.xs_shapes["[DG][G][G']"]: # Then we have a delayed group matrix. We will first # remove the outgoing group dependency temp_data = np.sum(temp_data, axis=-1) # And then proceed in exactly the same manner as the # "[DG][G']" or "[DG][G]" shapes in the previous block. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i], :] else: data[i, :] = np.sum(temp_data[:, :], axis=0) elif shape == xsdata.xs_shapes["[G][G'][Order]"]: # This is a scattering matrix with angular data # First remove the outgoing group dependence temp_data = np.sum(temp_data, axis=1) # The user either provided a specific order or we resort # to the default 0th order if orders[i]: order = orders[i] else: order = 0 # If the order is available, store the data for that order # if it is not available, then the expansion coefficient # is zero and thus we already have the correct value. if order < shape[1]: data[i, :] = temp_data[:, order] else: raise ValueError("{} not present in provided MGXS " "library".format(this.name)) return data
def nuclides(self, nuclides): cv.check_iterable_type('nuclides', nuclides, (basestring, Nuclide, CrossNuclide)) self._nuclides = nuclides
def universes(self, universes): cv.check_iterable_type('lattice universes', universes, openmc.Universe, min_depth=2, max_depth=3) self._universes = universes # NOTE: This routine assumes that the user creates a "ragged" list of # lists, where each sub-list corresponds to one ring of Universes. # The sub-lists are ordered from outermost ring to innermost ring. # The Universes within each sub-list are ordered from the "top" in a # clockwise fashion. # Set the number of axial positions. if self.ndim == 3: self._num_axial = len(self._universes) else: self._num_axial = None # Set the number of rings and make sure this number is consistent for # all axial positions. if self.ndim == 3: self._num_rings = len(self._universes[0]) for rings in self._universes: if len(rings) != self._num_rings: msg = 'HexLattice ID={0:d} has an inconsistent number of ' \ 'rings per axial positon'.format(self._id) raise ValueError(msg) else: self._num_rings = len(self._universes) # Make sure there are the correct number of elements in each ring. if self.ndim == 3: for axial_slice in self._universes: # Check the center ring. if len(axial_slice[-1]) != 1: msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in the innermost ring. Only 1 element is ' \ 'allowed in the innermost ring.'.format(self._id) raise ValueError(msg) # Check the outer rings. for r in range(self._num_rings - 1): if len(axial_slice[r]) != 6 * (self._num_rings - 1 - r): msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in ring number {1:d} (counting from the '\ 'outermost ring). This ring should have {2:d} ' \ 'elements.'.format(self._id, r, 6*(self._num_rings - 1 - r)) raise ValueError(msg) else: axial_slice = self._universes # Check the center ring. if len(axial_slice[-1]) != 1: msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in the innermost ring. Only 1 element is ' \ 'allowed in the innermost ring.'.format(self._id) raise ValueError(msg) # Check the outer rings. for r in range(self._num_rings - 1): if len(axial_slice[r]) != 6 * (self._num_rings - 1 - r): msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in ring number {1:d} (counting from the '\ 'outermost ring). This ring should have {2:d} ' \ 'elements.'.format(self._id, r, 6*(self._num_rings - 1 - r)) raise ValueError(msg)
def bins(self, bins): cv.check_iterable_type('bins', bins, Iterable) self._bins = list(map(tuple, bins))
def isotropic(self, isotropic): cv.check_iterable_type('Isotropic scattering nuclides', isotropic, string_types) self._isotropic = list(isotropic)
def nuclides(self, nuclides): cv.check_iterable_type('nuclides', nuclides, string_types + (openmc.Nuclide, CrossNuclide)) self._nuclides = nuclides
def scores(self, scores): cv.check_iterable_type('scores', scores, str) self._scores = scores
def breakpoints(self, breakpoints): cv.check_iterable_type('breakpoints', breakpoints, Real) self._breakpoints = breakpoints
def mu(self, mu): cv.check_iterable_type('correlated angle-energy outgoing cosine', mu, Univariate, 2, 2) self._mu = mu
def pin(surfaces, items, subdivisions=None, divide_vols=True, **kwargs): """Convenience function for building a fuel pin Parameters ---------- surfaces : iterable of :class:`openmc.Cylinder` Cylinders used to define boundaries between items. All cylinders must be concentric and of the same orientation, e.g. all :class:`openmc.ZCylinder` items : iterable Objects to go between ``surfaces``. These can be anything that can fill a :class:`openmc.Cell`, including :class:`openmc.Material`, or other :class:`openmc.Universe` objects. There must be one more item than surfaces, which will span all space outside the final ring. subdivisions : None or dict of int to int Dictionary describing which rings to subdivide and how many times. Keys are indexes of the annular rings to be divided. Will construct equal area rings divide_vols : bool If this evaluates to ``True``, then volumes of subdivided :class:`openmc.Material` instances will also be divided by the number of divisions. Otherwise the volume of the original material will not be modified before subdivision kwargs: Additional key-word arguments to be passed to :class:`openmc.Universe`, like ``name="Fuel pin"`` Returns ------- :class:`openmc.Universe` Universe of concentric cylinders filled with the desired items """ if "cells" in kwargs: raise SyntaxError( "Cells will be set by this function, not from input arguments.") check_type("items", items, Iterable) check_length("surfaces", surfaces, len(items) - 1, len(items) - 1) # Check that all surfaces are of similar orientation check_type("surface", surfaces[0], Cylinder) surf_type = type(surfaces[0]) check_iterable_type("surfaces", surfaces[1:], surf_type) # Check for increasing radii and equal centers if surf_type is ZCylinder: center_getter = attrgetter("x0", "y0") elif surf_type is YCylinder: center_getter = attrgetter("x0", "z0") elif surf_type is XCylinder: center_getter = attrgetter("z0", "y0") else: raise TypeError("Not configured to interpret {} surfaces".format( surf_type.__name__)) centers = set() prev_rad = 0 for ix, surf in enumerate(surfaces): cur_rad = surf.r if cur_rad <= prev_rad: raise ValueError( "Surfaces do not appear to be increasing in radius. " "Surface {} at index {} has radius {:7.3e} compared to " "previous radius of {:7.5e}".format(surf.id, ix, cur_rad, prev_rad)) prev_rad = cur_rad centers.add(center_getter(surf)) if len(centers) > 1: raise ValueError( "Surfaces do not appear to be concentric. The following " "centers were found: {}".format(centers)) if subdivisions is not None: check_length("subdivisions", subdivisions, 1, len(surfaces)) orig_indexes = list(subdivisions.keys()) check_iterable_type("ring indexes", orig_indexes, int) check_iterable_type("number of divisions", list(subdivisions.values()), int) for ix in orig_indexes: if ix < 0: subdivisions[len(surfaces) + ix] = subdivisions.pop(ix) # Dissallow subdivision on outer most, infinite region check_less_than("outer ring", max(subdivisions), len(surfaces), equality=True) # ensure ability to concatenate if not isinstance(items, list): items = list(items) if not isinstance(surfaces, list): surfaces = list(surfaces) # generate equal area divisions # Adding N - 1 new regions # N - 2 surfaces are made # Original cell is not removed, but not occupies last ring for ring_index in reversed(sorted(subdivisions.keys())): nr = subdivisions[ring_index] new_surfs = [] lower_rad = 0.0 if ring_index == 0 else surfaces[ring_index - 1].r upper_rad = surfaces[ring_index].r area_term = (upper_rad**2 - lower_rad**2) / nr for new_index in range(nr - 1): lower_rad = sqrt(area_term + lower_rad**2) new_surfs.append(surf_type(r=lower_rad)) surfaces = (surfaces[:ring_index] + new_surfs + surfaces[ring_index:]) filler = items[ring_index] if (divide_vols and hasattr(filler, "volume") and filler.volume is not None): filler.volume /= nr items[ring_index:ring_index] = [ filler.clone() for _i in range(nr - 1) ] # Build the universe regions = subdivide(surfaces) cells = [Cell(fill=f, region=r) for r, f in zip(regions, items)] return Universe(cells=cells, **kwargs)
def distribcell_paths(self, distribcell_paths): cv.check_iterable_type('distribcell_paths', distribcell_paths, str) self._distribcell_paths = distribcell_paths
def create_mg_library(self, xs_type='macro', xsdata_names=None, xs_ids=None, tabular_legendre=None, tabular_points=33): """Creates an openmc.MGXSLibrary object to contain the MGXS data for the Multi-Group mode of OpenMC. Parameters ---------- xs_type: {'macro', 'micro'} Provide the macro or micro cross section in units of cm^-1 or barns. Defaults to 'macro'. If the Library object is not tallied by nuclide this will be set to 'macro' regardless. xsdata_names : Iterable of str List of names to apply to the "xsdata" entries in the resultant mgxs data file. Defaults to 'set1', 'set2', ... xs_ids : str or Iterable of str Cross section set identifier (i.e., '71c') for all data sets (if only str) or for each individual one (if iterable of str). Defaults to '1m'. tabular_legendre : None or bool Flag to denote whether or not the Legendre expansion of the scattering angular distribution is to be converted to a tabular representation by OpenMC. A value of `True` means that it is to be converted while a value of `False` means that it will not be. Defaults to `None` which leaves the default behavior of OpenMC in place (the distribution is converted to a tabular representation). tabular_points : int This parameter is not used unless the ``tabular_legendre`` parameter is set to `True`. In this case, this parameter sets the number of equally-spaced points in the domain of [-1,1] to be used in building the tabular distribution. Default is `33`. Returns ------- mgxs_file : openmc.MGXSLibrary Multi-Group Cross Section File that is ready to be printed to the file of choice by the user. Raises ------ ValueError When the Library object is initialized with insufficient types of cross sections for the Library. See also -------- Library.dump_to_file() Library.create_mg_mode() """ # Check to ensure the Library contains the correct # multi-group cross section types self.check_library_for_openmc_mgxs() cv.check_value('xs_type', xs_type, ['macro', 'micro']) if xsdata_names is not None: cv.check_iterable_type('xsdata_names', xsdata_names, basestring) if xs_ids is not None: if isinstance(xs_ids, basestring): # If we only have a string lets convert it now to a list # of strings. xs_ids = [xs_ids for i in range(len(self.domains))] else: cv.check_iterable_type('xs_ids', xs_ids, basestring) else: xs_ids = ['1m' for i in range(len(self.domains))] # If gathering material-specific data, set the xs_type to macro if not self.by_nuclide: xs_type = 'macro' # Initialize file mgxs_file = openmc.MGXSLibrary(self.energy_groups) # Create the xsdata object and add it to the mgxs_file for i, domain in enumerate(self.domains): if self.by_nuclide: nuclides = list(domain.get_all_nuclides().keys()) else: nuclides = ['total'] for nuclide in nuclides: # Build & add metadata to XSdata object if xsdata_names is None: xsdata_name = 'set' + str(i + 1) else: xsdata_name = xsdata_names[i] if nuclide is not 'total': xsdata_name += '_' + nuclide xsdata = self.get_xsdata(domain, xsdata_name, nuclide=nuclide, xs_type=xs_type, xs_id=xs_ids[i], tabular_legendre=tabular_legendre, tabular_points=tabular_points) mgxs_file.add_xsdata(xsdata) return mgxs_file
def scores(self, scores): cv.check_iterable_type('scores', scores, string_types) self._scores = scores
def create_mg_mode(self, xsdata_names=None, xs_ids=None, tabular_legendre=None, tabular_points=33): """Creates an openmc.MGXSLibrary object to contain the MGXS data for the Multi-Group mode of OpenMC as well as the associated openmc.Materials and openmc.Geometry objects. The created Geometry is the same as that used to generate the MGXS data, with the only differences being modifications to point to newly-created Materials which point to the multi-group data. This method only creates a macroscopic MGXS Library even if nuclidic tallies are specified in the Library. Parameters ---------- xsdata_names : Iterable of str List of names to apply to the "xsdata" entries in the resultant mgxs data file. Defaults to 'set1', 'set2', ... xs_ids : str or Iterable of str Cross section set identifier (i.e., '71c') for all data sets (if only str) or for each individual one (if iterable of str). Defaults to '1m'. tabular_legendre : None or bool Flag to denote whether or not the Legendre expansion of the scattering angular distribution is to be converted to a tabular representation by OpenMC. A value of `True` means that it is to be converted while a value of `False` means that it will not be. Defaults to `None` which leaves the default behavior of OpenMC in place (the distribution is converted to a tabular representation). tabular_points : int This parameter is not used unless the ``tabular_legendre`` parameter is set to `True`. In this case, this parameter sets the number of equally-spaced points in the domain of [-1,1] to be used in building the tabular distribution. Default is `33`. Returns ------- mgxs_file : openmc.MGXSLibrary Multi-Group Cross Section File that is ready to be printed to the file of choice by the user. materials : openmc.Materials Materials file ready to be printed with all the macroscopic data present within this Library. geometry : openmc.Geometry Materials file ready to be printed with all the macroscopic data present within this Library. Raises ------ ValueError When the Library object is initialized with insufficient types of cross sections for the Library. See also -------- Library.create_mg_library() Library.dump_to_file() """ # Check to ensure the Library contains the correct # multi-group cross section types self.check_library_for_openmc_mgxs() if xsdata_names is not None: cv.check_iterable_type('xsdata_names', xsdata_names, basestring) if xs_ids is not None: if isinstance(xs_ids, basestring): # If we only have a string lets convert it now to a list # of strings. xs_ids = [xs_ids for i in range(len(self.domains))] else: cv.check_iterable_type('xs_ids', xs_ids, basestring) else: xs_ids = ['1m' for i in range(len(self.domains))] xs_type = 'macro' # Initialize MGXS File mgxs_file = openmc.MGXSLibrary(self.energy_groups) # Create a copy of the Geometry to differentiate for these Macroscopics geometry = copy.deepcopy(self.openmc_geometry) materials = openmc.Materials() # Get all Cells from the Geometry for differentiation all_cells = geometry.get_all_material_cells() # Create the xsdata object and add it to the mgxs_file for i, domain in enumerate(self.domains): # Build & add metadata to XSdata object if xsdata_names is None: xsdata_name = 'set' + str(i + 1) else: xsdata_name = xsdata_names[i] # Create XSdata and Macroscopic for this domain xsdata = self.get_xsdata(domain, xsdata_name, nuclide='total', xs_type=xs_type, xs_id=xs_ids[i], tabular_legendre=tabular_legendre, tabular_points=tabular_points) mgxs_file.add_xsdata(xsdata) macroscopic = openmc.Macroscopic(name=xsdata_name, xs=xs_ids[i]) # Create Material and add to collection material = openmc.Material(name=xsdata_name + '.' + xs_ids[i]) material.add_macroscopic(macroscopic) materials.append(material) # Differentiate Geometry with new Material if self.domain_type == 'material': # Fill all appropriate Cells with new Material for cell in all_cells: if cell.fill.id == domain.id: cell.fill = material elif self.domain_type == 'cell': for cell in all_cells: if cell.id == domain.id: cell.fill = material return mgxs_file, materials, geometry
def nuclides(self, nuclides): cv.check_iterable_type('nuclides', nuclides, (str, CrossNuclide)) self._nuclides = nuclides
def search_for_keff(model_builder, initial_guess=None, target=1.0, bracket=None, model_args=None, tol=None, bracketed_method='bisect', print_iterations=False, print_output=False, **kwargs): """Function to perform a keff search by modifying a model parametrized by a single independent variable. Parameters ---------- model_builder : collections.Callable Callable function which builds a model according to a passed parameter. This function must return an openmc.model.Model object. initial_guess : Real, optional Initial guess for the parameter to be searched in `model_builder`. One of `guess` or `bracket` must be provided. target : Real, optional keff value to search for, defaults to 1.0. bracket : None or Iterable of Real, optional Bracketing interval to search for the solution; if not provided, a generic non-bracketing method is used. If provided, the brackets are used. Defaults to no brackets provided. One of `guess` or `bracket` must be provided. If both are provided, the bracket will be preferentially used. model_args : dict, optional Keyword-based arguments to pass to the `model_builder` method. Defaults to no arguments. tol : float Tolerance to pass to the search method bracketed_method : {'brentq', 'brenth', 'ridder', 'bisect'}, optional Solution method to use; only applies if `bracket` is set, otherwise the Secant method is used. Defaults to 'bisect'. print_iterations : bool Whether or not to print the guess and the result during the iteration process. Defaults to False. print_output : bool Whether or not to print the OpenMC output during the iterations. Defaults to False. **kwargs All remaining keyword arguments are passed to the root-finding method. Returns ------- zero_value : float Estimated value of the variable parameter where keff is the targeted value guesses : List of Real List of guesses attempted by the search results : List of 2-tuple of Real List of keffs and uncertainties corresponding to the guess attempted by the search """ if initial_guess is not None: cv.check_type('initial_guess', initial_guess, Real) if bracket is not None: cv.check_iterable_type('bracket', bracket, Real) cv.check_length('bracket', bracket, 2) cv.check_less_than('bracket values', bracket[0], bracket[1]) if model_args is None: model_args = {} else: cv.check_type('model_args', model_args, dict) cv.check_type('target', target, Real) cv.check_type('tol', tol, Real) cv.check_value('bracketed_method', bracketed_method, _SCALAR_BRACKETED_METHODS) cv.check_type('print_iterations', print_iterations, bool) cv.check_type('print_output', print_output, bool) cv.check_type('model_builder', model_builder, Callable) # Run the model builder function once to make sure it provides the correct # output type if bracket is not None: model = model_builder(bracket[0], **model_args) elif initial_guess is not None: model = model_builder(initial_guess, **model_args) cv.check_type('model_builder return', model, openmc.model.Model) # Set the iteration data storage variables guesses = [] results = [] # Set the searching function (for easy replacement should a later # generic function be added. search_function = _search_keff if bracket is not None: # Generate our arguments args = {'f': search_function, 'a': bracket[0], 'b': bracket[1]} if tol is not None: args['rtol'] = tol # Set the root finding method if bracketed_method == 'brentq': root_finder = sopt.brentq elif bracketed_method == 'brenth': root_finder = sopt.brenth elif bracketed_method == 'ridder': root_finder = sopt.ridder elif bracketed_method == 'bisect': root_finder = sopt.bisect elif initial_guess is not None: # Generate our arguments args = {'func': search_function, 'x0': initial_guess} if tol is not None: args['tol'] = tol # Set the root finding method root_finder = sopt.newton else: raise ValueError("Either the 'bracket' or 'initial_guess' parameters " "must be set") # Add information to be passed to the searching function args['args'] = (target, model_builder, model_args, print_iterations, print_output, guesses, results) # Create a new dictionary with the arguments from args and kwargs args.update(kwargs) # Perform the search zero_value = root_finder(**args) return zero_value, guesses, results
def _calculate_mgxs_nuc_macro(this, types, library, orders=None, temperature=294.): """Determines the multi-group cross sections of a nuclide or macroscopic object. If the data for the nuclide or macroscopic object in the library is represented as angle-dependent data then this method will return the geometric average cross section over all angles. Parameters ---------- this : openmc.Nuclide or openmc.Macroscopic Object to source data from types : Iterable of str The type of cross sections to calculate; values can either be those in openmc.PLOT_TYPES_MGXS library : openmc.MGXSLibrary MGXS Library containing the data of interest orders : Iterable of Integral, optional The scattering order or delayed group index to use for the corresponding entry in types. Defaults to the 0th order for scattering and the total delayed neutron data. temperature : float, optional Temperature in Kelvin to plot. If not specified, a default temperature of 294K will be plotted. Note that the nearest temperature in the library for each nuclide will be used as opposed to using any interpolation. Returns ------- data : numpy.ndarray Cross sections calculated at the energy grid described by energy_grid """ # Check the parameters and grab order/delayed groups if orders: cv.check_iterable_type('orders', orders, Integral, min_depth=len(types), max_depth=len(types)) else: orders = [None] * len(types) for i, line in enumerate(types): cv.check_type("line", line, str) cv.check_value("line", line, PLOT_TYPES_MGXS) if orders[i]: cv.check_greater_than("order value", orders[i], 0, equality=True) xsdata = library.get_by_name(this) if xsdata is not None: # Obtain the nearest temperature t = np.abs(xsdata.temperatures - temperature).argmin() # Get the data data = np.zeros((len(types), library.energy_groups.num_groups)) for i, line in enumerate(types): if 'fission' in line and not xsdata.fissionable: continue elif line == 'unity': data[i, :] = 1. else: # Now we have to get the cross section data and properly # treat it depending on the requested type. # First get the data in a generic fashion temp_data = getattr(xsdata, _PLOT_MGXS_ATTR[line])[t] shape = temp_data.shape[:] # If we have angular data, then want the geometric # average over all provided angles. Since the angles are # equi-distant, un-weighted averaging will suffice if xsdata.representation == 'angle': temp_data = np.mean(temp_data, axis=(0, 1)) # Now we can look at the shape of the data to identify how # it should be modified to produce an array of values # with groups. if shape in (xsdata.xs_shapes["[G']"], xsdata.xs_shapes["[G]"]): # Then the data is already an array vs groups so copy # and move along data[i, :] = temp_data elif shape == xsdata.xs_shapes["[G][G']"]: # Sum the data over outgoing groups to create our array vs # groups data[i, :] = np.sum(temp_data, axis=1) elif shape == xsdata.xs_shapes["[DG]"]: # Then we have a constant vs groups with a value for each # delayed group. The user-provided value of orders tells us # which delayed group we want. If none are provided, then # we sum all the delayed groups together. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i]] else: data[i, :] = np.sum(temp_data[:]) elif shape in (xsdata.xs_shapes["[DG][G']"], xsdata.xs_shapes["[DG][G]"]): # Then we have an array vs groups with values for each # delayed group. The user-provided value of orders tells us # which delayed group we want. If none are provided, then # we sum all the delayed groups together. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i], :] else: data[i, :] = np.sum(temp_data[:, :], axis=0) elif shape == xsdata.xs_shapes["[DG][G][G']"]: # Then we have a delayed group matrix. We will first # remove the outgoing group dependency temp_data = np.sum(temp_data, axis=-1) # And then proceed in exactly the same manner as the # "[DG][G']" or "[DG][G]" shapes in the previous block. if orders[i]: if orders[i] < len(shape[0]): data[i, :] = temp_data[orders[i], :] else: data[i, :] = np.sum(temp_data[:, :], axis=0) elif shape == xsdata.xs_shapes["[G][G'][Order]"]: # This is a scattering matrix with angular data # First remove the outgoing group dependence temp_data = np.sum(temp_data, axis=1) # The user either provided a specific order or we resort # to the default 0th order if orders[i]: order = orders[i] else: order = 0 # If the order is available, store the data for that order # if it is not available, then the expansion coefficient # is zero and thus we already have the correct value. if order < shape[1]: data[i, :] = temp_data[:, order] else: raise ValueError(f"{this} not present in provided MGXS library") return data
def scores(self, scores): cv.check_iterable_type('scores', scores, basestring) self._scores = scores
def universes(self, universes): cv.check_iterable_type('lattice universes', universes, openmc.Universe, min_depth=2, max_depth=3) self._universes = universes # NOTE: This routine assumes that the user creates a "ragged" list of # lists, where each sub-list corresponds to one ring of Universes. # The sub-lists are ordered from outermost ring to innermost ring. # The Universes within each sub-list are ordered from the "top" in a # clockwise fashion. # Check to see if the given universes look like a 2D or a 3D array. if isinstance(self._universes[0][0], openmc.Universe): n_dims = 2 elif isinstance(self._universes[0][0][0], openmc.Universe): n_dims = 3 else: msg = 'HexLattice ID={0:d} does not appear to be either 2D or ' \ '3D. Make sure set_universes was given a two-deep or ' \ 'three-deep iterable of universes.'.format(self._id) raise RuntimeError(msg) # Set the number of axial positions. if n_dims == 3: self._num_axial = len(self._universes) else: self._num_axial = None # Set the number of rings and make sure this number is consistent for # all axial positions. if n_dims == 3: self._num_rings = len(self._universes[0]) for rings in self._universes: if len(rings) != self._num_rings: msg = 'HexLattice ID={0:d} has an inconsistent number of ' \ 'rings per axial positon'.format(self._id) raise ValueError(msg) else: self._num_rings = len(self._universes) # Make sure there are the correct number of elements in each ring. if n_dims == 3: for axial_slice in self._universes: # Check the center ring. if len(axial_slice[-1]) != 1: msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in the innermost ring. Only 1 element is ' \ 'allowed in the innermost ring.'.format(self._id) raise ValueError(msg) # Check the outer rings. for r in range(self._num_rings-1): if len(axial_slice[r]) != 6*(self._num_rings - 1 - r): msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in ring number {1:d} (counting from the '\ 'outermost ring). This ring should have {2:d} ' \ 'elements.'.format(self._id, r, 6*(self._num_rings - 1 - r)) raise ValueError(msg) else: axial_slice = self._universes # Check the center ring. if len(axial_slice[-1]) != 1: msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in the innermost ring. Only 1 element is ' \ 'allowed in the innermost ring.'.format(self._id) raise ValueError(msg) # Check the outer rings. for r in range(self._num_rings-1): if len(axial_slice[r]) != 6*(self._num_rings - 1 - r): msg = 'HexLattice ID={0:d} has the wrong number of ' \ 'elements in ring number {1:d} (counting from the '\ 'outermost ring). This ring should have {2:d} ' \ 'elements.'.format(self._id, r, 6*(self._num_rings - 1 - r)) raise ValueError(msg)
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 Index of burnup step to evaluate. See also: get_step_where for obtaining burnup step indices from other data such as the time. nuc_with_data : Iterable of str, optional Nuclides to include in resulting materials. This can be specified if not all nuclides appearing in depletion results have associated neutron cross sections, and as such cannot be used in subsequent transport calculations. If not provided, nuclides from the cross_sections element of materials.xml will be used. If that element is not present, nuclides from OPENMC_CROSS_SECTIONS will be used. Returns ------- mat_file : Materials A modified Materials instance containing depleted material data and original isotopic compositions of non-depletable materials """ result = self[burnup_index] # Only materials found in the original materials.xml file will be # updated. If for some reason you have modified OpenMC to produce # new materials as depletion takes place, this method will not # work as expected and leave out that material. mat_file = Materials.from_xml("materials.xml") # Only nuclides with valid transport data will be written to # the new materials XML file. The precedence of nuclides to select # is first ones provided as a kwarg here, then ones specified # in the materials.xml file if provided, then finally from # the environment variable OPENMC_CROSS_SECTIONS. if nuc_with_data: cv.check_iterable_type('nuclide names', nuc_with_data, str) available_cross_sections = nuc_with_data else: # select cross_sections.xml file to use if mat_file.cross_sections: this_library = DataLibrary.from_xml(path=mat_file.cross_sections) else: this_library = DataLibrary.from_xml() # Find neutron libraries we have access to available_cross_sections = set() for lib in this_library.libraries: if lib['type'] == 'neutron': available_cross_sections.update(lib['materials']) if not available_cross_sections: raise DataError('No neutron libraries found in cross_sections.xml') # Overwrite material definitions, if they can be found in the depletion # results, and save them to the new depleted xml file. for mat in mat_file: mat_id = str(mat.id) if mat_id in result.mat_to_ind: mat.volume = result.volume[mat_id] mat.set_density('sum') for nuc in result.nuc_to_ind: if nuc not in available_cross_sections: continue atoms = result[0, mat_id, nuc] if atoms > 0.0: atoms_per_barn_cm = 1e-24 * atoms / mat.volume mat.remove_nuclide(nuc) # Replace if it's there mat.add_nuclide(nuc, atoms_per_barn_cm) return mat_file
def energy_out(self, energy_out): cv.check_iterable_type('laboratory angle-energy outgoing energy', energy_out, Univariate, 2, 2) self._energy_out = energy_out
def calculate_mgxs(this, data_type, types, orders=None, temperature=294., cross_sections=None, ce_cross_sections=None, enrichment=None): """Calculates multi-group cross sections of a requested type. If the data for the nuclide or macroscopic object in the library is represented as angle-dependent data then this method will return the geometric average cross section over all angles. Parameters ---------- this : str or openmc.Material Object to source data from data_type : {'nuclide', 'element', 'material', 'macroscopic'} Type of object to plot types : Iterable of values of PLOT_TYPES_MGXS The type of cross sections to calculate orders : Iterable of Integral, optional The scattering order or delayed group index to use for the corresponding entry in types. Defaults to the 0th order for scattering and the total delayed neutron data. temperature : float, optional Temperature in Kelvin to plot. If not specified, a default temperature of 294K will be plotted. Note that the nearest temperature in the library for each nuclide will be used as opposed to using any interpolation. cross_sections : str, optional Location of MGXS HDF5 Library file. Default is None. ce_cross_sections : str, optional Location of continuous-energy cross_sections.xml file. Default is None. This is used only for expanding an openmc.Element object passed as this enrichment : float, optional Enrichment for U235 in weight percent. For example, input 4.95 for 4.95 weight percent enriched U. Default is None (natural composition). Returns ------- energy_grid : numpy.ndarray Energies at which cross sections are calculated, in units of eV data : numpy.ndarray Cross sections calculated at the energy grid described by energy_grid """ # Check types cv.check_type('temperature', temperature, Real) if enrichment: cv.check_type('enrichment', enrichment, Real) cv.check_iterable_type('types', types, str) cv.check_type("cross_sections", cross_sections, str) library = openmc.MGXSLibrary.from_hdf5(cross_sections) if data_type in ('nuclide', 'macroscopic'): mgxs = _calculate_mgxs_nuc_macro(this, types, library, orders, temperature) elif data_type in ('element', 'material'): mgxs = _calculate_mgxs_elem_mat(this, types, library, orders, temperature, ce_cross_sections, enrichment) else: raise TypeError("Invalid type") # Convert the data to the format needed data = np.zeros((len(types), 2 * library.energy_groups.num_groups)) energy_grid = np.zeros(2 * library.energy_groups.num_groups) for g in range(library.energy_groups.num_groups): energy_grid[g * 2: g * 2 + 2] = \ library.energy_groups.group_edges[g: g + 2] # Ensure the energy will show on a log-axis by replacing 0s with a # sufficiently small number energy_grid[0] = max(energy_grid[0], _MIN_E) for line in range(len(types)): for g in range(library.energy_groups.num_groups): data[line, g * 2: g * 2 + 2] = mgxs[line, g] return energy_grid[::-1], data
def isotropic(self, isotropic): cv.check_iterable_type('Isotropic scattering nuclides', isotropic, str) self._isotropic = list(isotropic)